1 /* histfile.c - functions to manipulate the history file. */
2
3 /* Copyright (C) 1989-2019 Free Software Foundation, Inc.
4
5 This file contains the GNU History Library (History), a set of
6 routines for managing the text of previously typed lines.
7
8 History is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 History is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with History. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /* The goal is to make the implementation transparent, so that you
23 don't have to know what data types are used, just what functions
24 you can call. I think I have done that. */
25
26 #define READLINE_LIBRARY
27
28 #if defined (__TANDEM)
29 # define _XOPEN_SOURCE_EXTENDED 1
30 # include <unistd.h>
31 # include <floss.h>
32 #endif
33
34 #if defined (HAVE_CONFIG_H)
35 # include <config.h>
36 #endif
37
38 #include <stdio.h>
39
40 #if defined (HAVE_LIMITS_H)
41 # include <limits.h>
42 #endif
43
44 #include <sys/types.h>
45 #if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H)
46 # include <sys/file.h>
47 #endif
48 #include "posixstat.h"
49 #include <fcntl.h>
50
51 #if defined (HAVE_STDLIB_H)
52 # include <stdlib.h>
53 #else
54 # include "ansi_stdlib.h"
55 #endif /* HAVE_STDLIB_H */
56
57 #if defined (HAVE_UNISTD_H)
58 # include <unistd.h>
59 #endif
60
61 #include <ctype.h>
62
63 #if defined (__EMX__)
64 # undef HAVE_MMAP
65 #endif
66
67 #ifdef HISTORY_USE_MMAP
68 # include <sys/mman.h>
69
70 # ifdef MAP_FILE
71 # define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE)
72 # define MAP_WFLAGS (MAP_FILE|MAP_SHARED)
73 # else
74 # define MAP_RFLAGS MAP_PRIVATE
75 # define MAP_WFLAGS MAP_SHARED
76 # endif
77
78 # ifndef MAP_FAILED
79 # define MAP_FAILED ((void *)-1)
80 # endif
81
82 #endif /* HISTORY_USE_MMAP */
83
84 #if defined(_WIN32)
85 # define WIN32_LEAN_AND_MEAN
86 # include <windows.h>
87 #endif
88
89 /* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
90 on win 95/98/nt), we want to open files with O_BINARY mode so that there
91 is no \n -> \r\n conversion performed. On other systems, we don't want to
92 mess around with O_BINARY at all, so we ensure that it's defined to 0. */
93 #if defined (__EMX__) || defined (__CYGWIN__)
94 # ifndef O_BINARY
95 # define O_BINARY 0
96 # endif
97 #else /* !__EMX__ && !__CYGWIN__ */
98 # undef O_BINARY
99 # define O_BINARY 0
100 #endif /* !__EMX__ && !__CYGWIN__ */
101
102 #include <errno.h>
103 #if !defined (errno)
104 extern int errno;
105 #endif /* !errno */
106
107 #include "history.h"
108 #include "histlib.h"
109
110 #include "rlshell.h"
111 #include "xmalloc.h"
112
113 #if !defined (PATH_MAX)
114 # define PATH_MAX 1024 /* default */
115 #endif
116
117 extern void _hs_append_history_line PARAMS((int, const char *));
118
119 /* history file version; currently unused */
120 int history_file_version = 1;
121
122 /* If non-zero, we write timestamps to the history file in history_do_write() */
123 int history_write_timestamps = 0;
124
125 /* If non-zero, we assume that a history file that starts with a timestamp
126 uses timestamp-delimited entries and can include multi-line history
127 entries. Used by read_history_range */
128 int history_multiline_entries = 0;
129
130 /* Immediately after a call to read_history() or read_history_range(), this
131 will return the number of lines just read from the history file in that
132 call. */
133 int history_lines_read_from_file = 0;
134
135 /* Immediately after a call to write_history() or history_do_write(), this
136 will return the number of lines just written to the history file in that
137 call. This also works with history_truncate_file. */
138 int history_lines_written_to_file = 0;
139
140 /* Does S look like the beginning of a history timestamp entry? Placeholder
141 for more extensive tests. */
142 #define HIST_TIMESTAMP_START(s) (*(s) == history_comment_char && isdigit ((unsigned char)(s)[1]) )
143
144 static char *history_backupfile PARAMS((const char *));
145 static char *history_tempfile PARAMS((const char *));
146 static int histfile_backup PARAMS((const char *, const char *));
147 static int histfile_restore PARAMS((const char *, const char *));
148 static int history_rename PARAMS((const char *, const char *));
149
150 /* Return the string that should be used in the place of this
151 filename. This only matters when you don't specify the
152 filename to read_history (), or write_history (). */
153 static char *
history_filename(const char * filename)154 history_filename (const char *filename)
155 {
156 char *return_val;
157 const char *home;
158 int home_len;
159
160 return_val = filename ? savestring (filename) : (char *)NULL;
161
162 if (return_val)
163 return (return_val);
164
165 home = sh_get_env_value ("HOME");
166 #if defined (_WIN32)
167 if (home == 0)
168 home = sh_get_env_value ("APPDATA");
169 #endif
170
171 if (home == 0)
172 return (NULL);
173 else
174 home_len = strlen (home);
175
176 return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
177 strcpy (return_val, home);
178 return_val[home_len] = '/';
179 #if defined (__MSDOS__)
180 strcpy (return_val + home_len + 1, "_history");
181 #else
182 strcpy (return_val + home_len + 1, ".history");
183 #endif
184
185 return (return_val);
186 }
187
188 static char *
history_backupfile(const char * filename)189 history_backupfile (const char *filename)
190 {
191 const char *fn;
192 char *ret, linkbuf[PATH_MAX+1];
193 size_t len;
194 ssize_t n;
195 struct stat fs;
196
197 fn = filename;
198 #if defined (HAVE_READLINK)
199 /* Follow symlink to avoid backing up symlink itself; call will fail if
200 not a symlink */
201 if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0)
202 {
203 linkbuf[n] = '\0';
204 fn = linkbuf;
205 }
206 #endif
207
208 len = strlen (fn);
209 ret = xmalloc (len + 2);
210 strcpy (ret, fn);
211 ret[len] = '-';
212 ret[len+1] = '\0';
213 return ret;
214 }
215
216 static char *
history_tempfile(const char * filename)217 history_tempfile (const char *filename)
218 {
219 const char *fn;
220 char *ret, linkbuf[PATH_MAX+1];
221 size_t len;
222 ssize_t n;
223 struct stat fs;
224 int pid;
225
226 fn = filename;
227 #if defined (HAVE_READLINK)
228 /* Follow symlink so tempfile created in the same directory as any symlinked
229 history file; call will fail if not a symlink */
230 if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0)
231 {
232 linkbuf[n] = '\0';
233 fn = linkbuf;
234 }
235 #endif
236
237 len = strlen (fn);
238 ret = xmalloc (len + 11);
239 strcpy (ret, fn);
240
241 pid = (int)getpid ();
242
243 /* filename-PID.tmp */
244 ret[len] = '-';
245 ret[len+1] = (pid / 10000 % 10) + '0';
246 ret[len+2] = (pid / 1000 % 10) + '0';
247 ret[len+3] = (pid / 100 % 10) + '0';
248 ret[len+4] = (pid / 10 % 10) + '0';
249 ret[len+5] = (pid % 10) + '0';
250 strcpy (ret + len + 6, ".tmp");
251
252 return ret;
253 }
254
255 /* Add the contents of FILENAME to the history list, a line at a time.
256 If FILENAME is NULL, then read from ~/.history. Returns 0 if
257 successful, or errno if not. */
258 int
read_history(const char * filename)259 read_history (const char *filename)
260 {
261 return (read_history_range (filename, 0, -1));
262 }
263
264 /* Read a range of lines from FILENAME, adding them to the history list.
265 Start reading at the FROM'th line and end at the TO'th. If FROM
266 is zero, start at the beginning. If TO is less than FROM, read
267 until the end of the file. If FILENAME is NULL, then read from
268 ~/.history. Returns 0 if successful, or errno if not. */
269 int
read_history_range(const char * filename,int from,int to)270 read_history_range (const char *filename, int from, int to)
271 {
272 register char *line_start, *line_end, *p;
273 char *input, *buffer, *bufend, *last_ts;
274 int file, current_line, chars_read, has_timestamps, reset_comment_char;
275 struct stat finfo;
276 size_t file_size;
277 #if defined (EFBIG)
278 int overflow_errno = EFBIG;
279 #elif defined (EOVERFLOW)
280 int overflow_errno = EOVERFLOW;
281 #else
282 int overflow_errno = EIO;
283 #endif
284
285 history_lines_read_from_file = 0;
286
287 buffer = last_ts = (char *)NULL;
288 input = history_filename (filename);
289 file = input ? open (input, O_RDONLY|O_BINARY, 0666) : -1;
290
291 if ((file < 0) || (fstat (file, &finfo) == -1))
292 goto error_and_exit;
293
294 if (S_ISREG (finfo.st_mode) == 0)
295 {
296 #ifdef EFTYPE
297 errno = EFTYPE;
298 #else
299 errno = EINVAL;
300 #endif
301 goto error_and_exit;
302 }
303
304 file_size = (size_t)finfo.st_size;
305
306 /* check for overflow on very large files */
307 if (file_size != finfo.st_size || file_size + 1 < file_size)
308 {
309 errno = overflow_errno;
310 goto error_and_exit;
311 }
312
313 if (file_size == 0)
314 {
315 free (input);
316 close (file);
317 return 0; /* don't waste time if we don't have to */
318 }
319
320 #ifdef HISTORY_USE_MMAP
321 /* We map read/write and private so we can change newlines to NULs without
322 affecting the underlying object. */
323 buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
324 if ((void *)buffer == MAP_FAILED)
325 {
326 errno = overflow_errno;
327 goto error_and_exit;
328 }
329 chars_read = file_size;
330 #else
331 buffer = (char *)malloc (file_size + 1);
332 if (buffer == 0)
333 {
334 errno = overflow_errno;
335 goto error_and_exit;
336 }
337
338 chars_read = read (file, buffer, file_size);
339 #endif
340 if (chars_read < 0)
341 {
342 error_and_exit:
343 if (errno != 0)
344 chars_read = errno;
345 else
346 chars_read = EIO;
347 if (file >= 0)
348 close (file);
349
350 FREE (input);
351 #ifndef HISTORY_USE_MMAP
352 FREE (buffer);
353 #endif
354
355 return (chars_read);
356 }
357
358 close (file);
359
360 /* Set TO to larger than end of file if negative. */
361 if (to < 0)
362 to = chars_read;
363
364 /* Start at beginning of file, work to end. */
365 bufend = buffer + chars_read;
366 *bufend = '\0'; /* null-terminate buffer for timestamp checks */
367 current_line = 0;
368
369 /* Heuristic: the history comment character rarely changes, so assume we
370 have timestamps if the buffer starts with `#[:digit:]' and temporarily
371 set history_comment_char so timestamp parsing works right */
372 reset_comment_char = 0;
373 if (history_comment_char == '\0' && buffer[0] == '#' && isdigit ((unsigned char)buffer[1]))
374 {
375 history_comment_char = '#';
376 reset_comment_char = 1;
377 }
378
379 has_timestamps = HIST_TIMESTAMP_START (buffer);
380 history_multiline_entries += has_timestamps && history_write_timestamps;
381
382 /* Skip lines until we are at FROM. */
383 if (has_timestamps)
384 last_ts = buffer;
385 for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
386 if (*line_end == '\n')
387 {
388 p = line_end + 1;
389 /* If we see something we think is a timestamp, continue with this
390 line. We should check more extensively here... */
391 if (HIST_TIMESTAMP_START(p) == 0)
392 current_line++;
393 else
394 last_ts = p;
395 line_start = p;
396 /* If we are at the last line (current_line == from) but we have
397 timestamps (has_timestamps), then line_start points to the
398 text of the last command, and we need to skip to its end. */
399 if (current_line >= from && has_timestamps)
400 {
401 for (line_end = p; line_end < bufend && *line_end != '\n'; line_end++)
402 ;
403 line_start = (*line_end == '\n') ? line_end + 1 : line_end;
404 }
405 }
406
407 /* If there are lines left to gobble, then gobble them now. */
408 for (line_end = line_start; line_end < bufend; line_end++)
409 if (*line_end == '\n')
410 {
411 /* Change to allow Windows-like \r\n end of line delimiter. */
412 if (line_end > line_start && line_end[-1] == '\r')
413 line_end[-1] = '\0';
414 else
415 *line_end = '\0';
416
417 if (*line_start)
418 {
419 if (HIST_TIMESTAMP_START(line_start) == 0)
420 {
421 if (last_ts == NULL && history_length > 0 && history_multiline_entries)
422 _hs_append_history_line (history_length - 1, line_start);
423 else
424 add_history (line_start);
425 if (last_ts)
426 {
427 add_history_time (last_ts);
428 last_ts = NULL;
429 }
430 }
431 else
432 {
433 last_ts = line_start;
434 current_line--;
435 }
436 }
437
438 current_line++;
439
440 if (current_line >= to)
441 break;
442
443 line_start = line_end + 1;
444 }
445
446 history_lines_read_from_file = current_line;
447 if (reset_comment_char)
448 history_comment_char = '\0';
449
450 FREE (input);
451 #ifndef HISTORY_USE_MMAP
452 FREE (buffer);
453 #else
454 munmap (buffer, file_size);
455 #endif
456
457 return (0);
458 }
459
460 /* We need a special version for WIN32 because Windows rename() refuses to
461 overwrite an existing file. */
462 static int
history_rename(const char * old,const char * new)463 history_rename (const char *old, const char *new)
464 {
465 #if defined (_WIN32)
466 return (MoveFileEx (old, new, MOVEFILE_REPLACE_EXISTING) == 0 ? -1 : 0);
467 #else
468 return (rename (old, new));
469 #endif
470 }
471
472 /* Save FILENAME to BACK, handling case where FILENAME is a symlink
473 (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */
474 static int
histfile_backup(const char * filename,const char * back)475 histfile_backup (const char *filename, const char *back)
476 {
477 #if defined (HAVE_READLINK)
478 char linkbuf[PATH_MAX+1];
479 ssize_t n;
480
481 /* Follow to target of symlink to avoid renaming symlink itself */
482 if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0)
483 {
484 linkbuf[n] = '\0';
485 return (history_rename (linkbuf, back));
486 }
487 #endif
488 return (history_rename (filename, back));
489 }
490
491 /* Restore ORIG from BACKUP handling case where ORIG is a symlink
492 (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */
493 static int
histfile_restore(const char * backup,const char * orig)494 histfile_restore (const char *backup, const char *orig)
495 {
496 #if defined (HAVE_READLINK)
497 char linkbuf[PATH_MAX+1];
498 ssize_t n;
499
500 /* Follow to target of symlink to avoid renaming symlink itself */
501 if ((n = readlink (orig, linkbuf, sizeof (linkbuf) - 1)) > 0)
502 {
503 linkbuf[n] = '\0';
504 return (history_rename (backup, linkbuf));
505 }
506 #endif
507 return (history_rename (backup, orig));
508 }
509
510 /* Should we call chown, based on whether finfo and nfinfo describe different
511 files with different owners? */
512
513 #define SHOULD_CHOWN(finfo, nfinfo) \
514 (finfo.st_uid != nfinfo.st_uid || finfo.st_gid != nfinfo.st_gid)
515
516 /* Truncate the history file FNAME, leaving only LINES trailing lines.
517 If FNAME is NULL, then use ~/.history. Writes a new file and renames
518 it to the original name. Returns 0 on success, errno on failure. */
519 int
history_truncate_file(const char * fname,int lines)520 history_truncate_file (const char *fname, int lines)
521 {
522 char *buffer, *filename, *tempname, *bp, *bp1; /* bp1 == bp+1 */
523 int file, chars_read, rv, orig_lines, exists, r;
524 struct stat finfo, nfinfo;
525 size_t file_size;
526
527 history_lines_written_to_file = 0;
528
529 buffer = (char *)NULL;
530 filename = history_filename (fname);
531 tempname = 0;
532 file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
533 rv = exists = 0;
534
535 /* Don't try to truncate non-regular files. */
536 if (file == -1 || fstat (file, &finfo) == -1)
537 {
538 rv = errno;
539 if (file != -1)
540 close (file);
541 goto truncate_exit;
542 }
543 exists = 1;
544
545 nfinfo.st_uid = finfo.st_uid;
546 nfinfo.st_gid = finfo.st_gid;
547
548 if (S_ISREG (finfo.st_mode) == 0)
549 {
550 close (file);
551 #ifdef EFTYPE
552 rv = EFTYPE;
553 #else
554 rv = EINVAL;
555 #endif
556 goto truncate_exit;
557 }
558
559 file_size = (size_t)finfo.st_size;
560
561 /* check for overflow on very large files */
562 if (file_size != finfo.st_size || file_size + 1 < file_size)
563 {
564 close (file);
565 #if defined (EFBIG)
566 rv = errno = EFBIG;
567 #elif defined (EOVERFLOW)
568 rv = errno = EOVERFLOW;
569 #else
570 rv = errno = EINVAL;
571 #endif
572 goto truncate_exit;
573 }
574
575 buffer = (char *)malloc (file_size + 1);
576 if (buffer == 0)
577 {
578 rv = errno;
579 close (file);
580 goto truncate_exit;
581 }
582
583 chars_read = read (file, buffer, file_size);
584 close (file);
585
586 if (chars_read <= 0)
587 {
588 rv = (chars_read < 0) ? errno : 0;
589 goto truncate_exit;
590 }
591
592 orig_lines = lines;
593 /* Count backwards from the end of buffer until we have passed
594 LINES lines. bp1 is set funny initially. But since bp[1] can't
595 be a comment character (since it's off the end) and *bp can't be
596 both a newline and the history comment character, it should be OK. */
597 for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
598 {
599 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
600 lines--;
601 bp1 = bp;
602 }
603
604 /* If this is the first line, then the file contains exactly the
605 number of lines we want to truncate to, so we don't need to do
606 anything. It's the first line if we don't find a newline between
607 the current value of i and 0. Otherwise, write from the start of
608 this line until the end of the buffer. */
609 for ( ; bp > buffer; bp--)
610 {
611 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
612 {
613 bp++;
614 break;
615 }
616 bp1 = bp;
617 }
618
619 /* Write only if there are more lines in the file than we want to
620 truncate to. */
621 if (bp <= buffer)
622 {
623 rv = 0;
624 /* No-op if LINES == 0 at this point */
625 history_lines_written_to_file = orig_lines - lines;
626 goto truncate_exit;
627 }
628
629 tempname = history_tempfile (filename);
630
631 if ((file = open (tempname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600)) != -1)
632 {
633 if (write (file, bp, chars_read - (bp - buffer)) < 0)
634 rv = errno;
635
636 if (fstat (file, &nfinfo) < 0 && rv == 0)
637 rv = errno;
638
639 if (close (file) < 0 && rv == 0)
640 rv = errno;
641 }
642 else
643 rv = errno;
644
645 truncate_exit:
646 FREE (buffer);
647
648 history_lines_written_to_file = orig_lines - lines;
649
650 if (rv == 0 && filename && tempname)
651 rv = histfile_restore (tempname, filename);
652
653 if (rv != 0)
654 {
655 rv = errno;
656 if (tempname)
657 unlink (tempname);
658 history_lines_written_to_file = 0;
659 }
660
661 #if defined (HAVE_CHOWN)
662 /* Make sure the new filename is owned by the same user as the old. If one
663 user is running this, it's a no-op. If the shell is running after sudo
664 with a shared history file, we don't want to leave the history file
665 owned by root. */
666 if (rv == 0 && exists && SHOULD_CHOWN (finfo, nfinfo))
667 r = chown (filename, finfo.st_uid, finfo.st_gid);
668 #endif
669
670 xfree (filename);
671 FREE (tempname);
672
673 return rv;
674 }
675
676 /* Workhorse function for writing history. Writes the last NELEMENT entries
677 from the history list to FILENAME. OVERWRITE is non-zero if you
678 wish to replace FILENAME with the entries. */
679 static int
history_do_write(const char * filename,int nelements,int overwrite)680 history_do_write (const char *filename, int nelements, int overwrite)
681 {
682 register int i;
683 char *output, *tempname, *histname;
684 int file, mode, rv, exists;
685 struct stat finfo, nfinfo;
686 #ifdef HISTORY_USE_MMAP
687 size_t cursize;
688
689 history_lines_written_to_file = 0;
690
691 mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
692 #else
693 mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
694 #endif
695 histname = history_filename (filename);
696 exists = histname ? (stat (histname, &finfo) == 0) : 0;
697
698 tempname = (overwrite && exists && S_ISREG (finfo.st_mode)) ? history_tempfile (histname) : 0;
699 output = tempname ? tempname : histname;
700
701 file = output ? open (output, mode, 0600) : -1;
702 rv = 0;
703
704 if (file == -1)
705 {
706 rv = errno;
707 FREE (histname);
708 FREE (tempname);
709 return (rv);
710 }
711
712 #ifdef HISTORY_USE_MMAP
713 cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
714 #endif
715
716 if (nelements > history_length)
717 nelements = history_length;
718
719 /* Build a buffer of all the lines to write, and write them in one syscall.
720 Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
721 {
722 HIST_ENTRY **the_history; /* local */
723 register int j;
724 int buffer_size;
725 char *buffer;
726
727 the_history = history_list ();
728 /* Calculate the total number of bytes to write. */
729 for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
730 {
731 if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
732 buffer_size += strlen (the_history[i]->timestamp) + 1;
733 buffer_size += strlen (the_history[i]->line) + 1;
734 }
735
736 /* Allocate the buffer, and fill it. */
737 #ifdef HISTORY_USE_MMAP
738 if (ftruncate (file, buffer_size+cursize) == -1)
739 goto mmap_error;
740 buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
741 if ((void *)buffer == MAP_FAILED)
742 {
743 mmap_error:
744 rv = errno;
745 close (file);
746 if (tempname)
747 unlink (tempname);
748 FREE (histname);
749 FREE (tempname);
750 return rv;
751 }
752 #else
753 buffer = (char *)malloc (buffer_size);
754 if (buffer == 0)
755 {
756 rv = errno;
757 close (file);
758 if (tempname)
759 unlink (tempname);
760 FREE (histname);
761 FREE (tempname);
762 return rv;
763 }
764 #endif
765
766 for (j = 0, i = history_length - nelements; i < history_length; i++)
767 {
768 if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
769 {
770 strcpy (buffer + j, the_history[i]->timestamp);
771 j += strlen (the_history[i]->timestamp);
772 buffer[j++] = '\n';
773 }
774 strcpy (buffer + j, the_history[i]->line);
775 j += strlen (the_history[i]->line);
776 buffer[j++] = '\n';
777 }
778
779 #ifdef HISTORY_USE_MMAP
780 if (msync (buffer, buffer_size, MS_ASYNC) != 0 || munmap (buffer, buffer_size) != 0)
781 rv = errno;
782 #else
783 if (write (file, buffer, buffer_size) < 0)
784 rv = errno;
785 xfree (buffer);
786 #endif
787 }
788
789 history_lines_written_to_file = nelements;
790
791 if (close (file) < 0 && rv == 0)
792 rv = errno;
793
794 if (rv == 0 && histname && tempname)
795 rv = histfile_restore (tempname, histname);
796
797 if (rv != 0)
798 {
799 rv = errno;
800 if (tempname)
801 unlink (tempname);
802 history_lines_written_to_file = 0;
803 }
804
805 #if defined (HAVE_CHOWN)
806 /* Make sure the new filename is owned by the same user as the old. If one
807 user is running this, it's a no-op. If the shell is running after sudo
808 with a shared history file, we don't want to leave the history file
809 owned by root. */
810 if (rv == 0 && exists)
811 mode = chown (histname, finfo.st_uid, finfo.st_gid);
812 #endif
813
814 FREE (histname);
815 FREE (tempname);
816
817 return (rv);
818 }
819
820 /* Append NELEMENT entries to FILENAME. The entries appended are from
821 the end of the list minus NELEMENTs up to the end of the list. */
822 int
append_history(int nelements,const char * filename)823 append_history (int nelements, const char *filename)
824 {
825 return (history_do_write (filename, nelements, HISTORY_APPEND));
826 }
827
828 /* Overwrite FILENAME with the current history. If FILENAME is NULL,
829 then write the history list to ~/.history. Values returned
830 are as in read_history ().*/
831 int
write_history(const char * filename)832 write_history (const char *filename)
833 {
834 return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
835 }
836