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