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 *
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 *
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 *
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
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
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
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
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
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
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
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
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
832: write_history (const char *filename)
833: {
834: return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
835: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>