Annotation of embedaddon/readline/histfile.c, revision 1.1.1.2
1.1 misho 1: /* histfile.c - functions to manipulate the history file. */
2:
1.1.1.2 ! misho 3: /* Copyright (C) 1989-2019 Free Software Foundation, Inc.
1.1 misho 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)
1.1.1.2 ! misho 29: # define _XOPEN_SOURCE_EXTENDED 1
! 30: # include <unistd.h>
1.1 misho 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:
1.1.1.2 ! misho 40: #if defined (HAVE_LIMITS_H)
! 41: # include <limits.h>
! 42: #endif
! 43:
1.1 misho 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:
1.1.1.2 ! misho 84: #if defined(_WIN32)
! 85: # define WIN32_LEAN_AND_MEAN
! 86: # include <windows.h>
! 87: #endif
! 88:
1.1 misho 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:
1.1.1.2 ! misho 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:
1.1 misho 122: /* If non-zero, we write timestamps to the history file in history_do_write() */
123: int history_write_timestamps = 0;
124:
1.1.1.2 ! misho 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:
1.1 misho 140: /* Does S look like the beginning of a history timestamp entry? Placeholder
141: for more extensive tests. */
1.1.1.2 ! misho 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 *));
1.1 misho 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 *
1.1.1.2 ! misho 154: history_filename (const char *filename)
1.1 misho 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");
1.1.1.2 ! misho 166: #if defined (_WIN32)
! 167: if (home == 0)
! 168: home = sh_get_env_value ("APPDATA");
! 169: #endif
1.1 misho 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 *
1.1.1.2 ! misho 189: history_backupfile (const char *filename)
1.1 misho 190: {
1.1.1.2 ! misho 191: const char *fn;
! 192: char *ret, linkbuf[PATH_MAX+1];
1.1 misho 193: size_t len;
1.1.1.2 ! misho 194: ssize_t n;
! 195: struct stat fs;
1.1 misho 196:
1.1.1.2 ! misho 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);
1.1 misho 209: ret = xmalloc (len + 2);
1.1.1.2 ! misho 210: strcpy (ret, fn);
1.1 misho 211: ret[len] = '-';
212: ret[len+1] = '\0';
213: return ret;
214: }
215:
1.1.1.2 ! misho 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:
1.1 misho 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
1.1.1.2 ! misho 259: read_history (const char *filename)
1.1 misho 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
1.1.1.2 ! misho 270: read_history_range (const char *filename, int from, int to)
1.1 misho 271: {
272: register char *line_start, *line_end, *p;
273: char *input, *buffer, *bufend, *last_ts;
1.1.1.2 ! misho 274: int file, current_line, chars_read, has_timestamps, reset_comment_char;
1.1 misho 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:
1.1.1.2 ! misho 285: history_lines_read_from_file = 0;
! 286:
1.1 misho 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:
1.1.1.2 ! misho 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:
1.1 misho 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:
1.1.1.2 ! misho 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:
1.1 misho 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;
1.1.1.2 ! misho 366: *bufend = '\0'; /* null-terminate buffer for timestamp checks */
1.1 misho 367: current_line = 0;
368:
1.1.1.2 ! misho 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:
1.1 misho 382: /* Skip lines until we are at FROM. */
1.1.1.2 ! misho 383: if (has_timestamps)
! 384: last_ts = buffer;
1.1 misho 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++;
1.1.1.2 ! misho 393: else
! 394: last_ts = p;
1.1 misho 395: line_start = p;
1.1.1.2 ! misho 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: }
1.1 misho 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: {
1.1.1.2 ! misho 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);
1.1 misho 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:
1.1.1.2 ! misho 446: history_lines_read_from_file = current_line;
! 447: if (reset_comment_char)
! 448: history_comment_char = '\0';
! 449:
1.1 misho 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:
1.1.1.2 ! misho 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:
1.1 misho 516: /* Truncate the history file FNAME, leaving only LINES trailing lines.
1.1.1.2 ! misho 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. */
1.1 misho 519: int
1.1.1.2 ! misho 520: history_truncate_file (const char *fname, int lines)
1.1 misho 521: {
1.1.1.2 ! misho 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;
1.1 misho 525: size_t file_size;
526:
1.1.1.2 ! misho 527: history_lines_written_to_file = 0;
! 528:
1.1 misho 529: buffer = (char *)NULL;
530: filename = history_filename (fname);
1.1.1.2 ! misho 531: tempname = 0;
1.1 misho 532: file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
1.1.1.2 ! misho 533: rv = exists = 0;
1.1 misho 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: }
1.1.1.2 ! misho 543: exists = 1;
! 544:
! 545: nfinfo.st_uid = finfo.st_uid;
! 546: nfinfo.st_gid = finfo.st_gid;
1.1 misho 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: {
1.1.1.2 ! misho 578: rv = errno;
1.1 misho 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:
1.1.1.2 ! misho 592: orig_lines = lines;
1.1 misho 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. */
1.1.1.2 ! misho 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)
1.1 misho 632: {
633: if (write (file, bp, chars_read - (bp - buffer)) < 0)
634: rv = errno;
635:
1.1.1.2 ! misho 636: if (fstat (file, &nfinfo) < 0 && rv == 0)
! 637: rv = errno;
1.1 misho 638:
639: if (close (file) < 0 && rv == 0)
640: rv = errno;
641: }
1.1.1.2 ! misho 642: else
! 643: rv = errno;
1.1 misho 644:
645: truncate_exit:
646: FREE (buffer);
647:
1.1.1.2 ! misho 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:
1.1 misho 670: xfree (filename);
1.1.1.2 ! misho 671: FREE (tempname);
! 672:
1.1 misho 673: return rv;
674: }
675:
1.1.1.2 ! misho 676: /* Workhorse function for writing history. Writes the last NELEMENT entries
1.1 misho 677: from the history list to FILENAME. OVERWRITE is non-zero if you
678: wish to replace FILENAME with the entries. */
679: static int
1.1.1.2 ! misho 680: history_do_write (const char *filename, int nelements, int overwrite)
1.1 misho 681: {
682: register int i;
1.1.1.2 ! misho 683: char *output, *tempname, *histname;
! 684: int file, mode, rv, exists;
! 685: struct stat finfo, nfinfo;
1.1 misho 686: #ifdef HISTORY_USE_MMAP
687: size_t cursize;
688:
1.1.1.2 ! misho 689: history_lines_written_to_file = 0;
! 690:
1.1 misho 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
1.1.1.2 ! misho 695: histname = history_filename (filename);
! 696: exists = histname ? (stat (histname, &finfo) == 0) : 0;
1.1 misho 697:
1.1.1.2 ! misho 698: tempname = (overwrite && exists && S_ISREG (finfo.st_mode)) ? history_tempfile (histname) : 0;
! 699: output = tempname ? tempname : histname;
1.1 misho 700:
701: file = output ? open (output, mode, 0600) : -1;
702: rv = 0;
703:
704: if (file == -1)
705: {
706: rv = errno;
1.1.1.2 ! misho 707: FREE (histname);
! 708: FREE (tempname);
1.1 misho 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);
1.1.1.2 ! misho 746: if (tempname)
! 747: unlink (tempname);
! 748: FREE (histname);
! 749: FREE (tempname);
1.1 misho 750: return rv;
751: }
752: #else
753: buffer = (char *)malloc (buffer_size);
754: if (buffer == 0)
755: {
756: rv = errno;
757: close (file);
1.1.1.2 ! misho 758: if (tempname)
! 759: unlink (tempname);
! 760: FREE (histname);
! 761: FREE (tempname);
1.1 misho 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
1.1.1.2 ! misho 780: if (msync (buffer, buffer_size, MS_ASYNC) != 0 || munmap (buffer, buffer_size) != 0)
1.1 misho 781: rv = errno;
782: #else
783: if (write (file, buffer, buffer_size) < 0)
784: rv = errno;
785: xfree (buffer);
786: #endif
787: }
788:
1.1.1.2 ! misho 789: history_lines_written_to_file = nelements;
! 790:
1.1 misho 791: if (close (file) < 0 && rv == 0)
792: rv = errno;
793:
1.1.1.2 ! misho 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
1.1 misho 813:
1.1.1.2 ! misho 814: FREE (histname);
! 815: FREE (tempname);
1.1 misho 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
1.1.1.2 ! misho 823: append_history (int nelements, const char *filename)
1.1 misho 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
1.1.1.2 ! misho 832: write_history (const char *filename)
1.1 misho 833: {
834: return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
835: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>