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