diff options
Diffstat (limited to 'histfile.c')
-rw-r--r-- | histfile.c | 118 |
1 files changed, 91 insertions, 27 deletions
@@ -1,6 +1,6 @@ /* histfile.c - functions to manipulate the history file. */ -/* Copyright (C) 1989-2019 Free Software Foundation, Inc. +/* Copyright (C) 1989-2019,2023 Free Software Foundation, Inc. This file contains the GNU History Library (History), a set of routines for managing the text of previously typed lines. @@ -153,7 +153,7 @@ history_filename (const char *filename) { char *return_val; const char *home; - int home_len; + size_t home_len; return_val = filename ? savestring (filename) : (char *)NULL; @@ -168,9 +168,8 @@ history_filename (const char *filename) if (home == 0) return (NULL); - else - home_len = strlen (home); + home_len = strlen (home); return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */ strcpy (return_val, home); return_val[home_len] = '/'; @@ -190,7 +189,6 @@ history_backupfile (const char *filename) char *ret, linkbuf[PATH_MAX+1]; size_t len; ssize_t n; - struct stat fs; fn = filename; #if defined (HAVE_READLINK) @@ -218,7 +216,6 @@ history_tempfile (const char *filename) char *ret, linkbuf[PATH_MAX+1]; size_t len; ssize_t n; - struct stat fs; int pid; fn = filename; @@ -284,7 +281,10 @@ read_history_range (const char *filename, int from, int to) buffer = last_ts = (char *)NULL; input = history_filename (filename); - file = input ? open (input, O_RDONLY|O_BINARY, 0666) : -1; + if (input == 0) + return 0; + errno = 0; + file = open (input, O_RDONLY|O_BINARY, 0666); if ((file < 0) || (fstat (file, &finfo) == -1)) goto error_and_exit; @@ -526,10 +526,14 @@ history_truncate_file (const char *fname, int lines) buffer = (char *)NULL; filename = history_filename (fname); + if (filename == 0) + return 0; tempname = 0; - file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1; + file = open (filename, O_RDONLY|O_BINARY, 0666); rv = exists = 0; + orig_lines = lines; + /* Don't try to truncate non-regular files. */ if (file == -1 || fstat (file, &finfo) == -1) { @@ -578,6 +582,15 @@ history_truncate_file (const char *fname, int lines) goto truncate_exit; } + /* Don't bother with any of this if we're truncating to zero length. */ + if (lines == 0) + { + close (file); + buffer[chars_read = 0] = '\0'; + bp = buffer; + goto truncate_write; + } + chars_read = read (file, buffer, file_size); close (file); @@ -586,31 +599,38 @@ history_truncate_file (const char *fname, int lines) rv = (chars_read < 0) ? errno : 0; goto truncate_exit; } + buffer[chars_read] = '\0'; /* for the initial check of bp1[1] */ - orig_lines = lines; /* Count backwards from the end of buffer until we have passed LINES lines. bp1 is set funny initially. But since bp[1] can't be a comment character (since it's off the end) and *bp can't be - both a newline and the history comment character, it should be OK. */ - for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--) + both a newline and the history comment character, it should be OK. + If we are writing history timestamps, we need to add one to LINES + because we decrement it one extra time the first time through the loop + and we need the final timestamp line. */ + lines += history_write_timestamps; + for (bp1 = bp = buffer + chars_read - 1; lines > 0 && bp > buffer; bp--) { if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0) lines--; bp1 = bp; } - /* If this is the first line, then the file contains exactly the + /* This is the right line, so move to its start. If we're writing history + timestamps, we want to go back until *bp == '\n' and bp1 starts a + history timestamp. If we're not, just move back to *bp == '\n'. + If this is the first line, then the file contains exactly the number of lines we want to truncate to, so we don't need to do - anything. It's the first line if we don't find a newline between - the current value of i and 0. Otherwise, write from the start of - this line until the end of the buffer. */ + anything, and we'll end up with bp == buffer. + Otherwise, write from the start of this line until the end of the + buffer. */ for ( ; bp > buffer; bp--) { - if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0) - { + if (*bp == '\n' && (history_write_timestamps == 0 || HIST_TIMESTAMP_START(bp1))) + { bp++; break; - } + } bp1 = bp; } @@ -624,17 +644,24 @@ history_truncate_file (const char *fname, int lines) goto truncate_exit; } +truncate_write: tempname = history_tempfile (filename); if ((file = open (tempname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600)) != -1) { if (write (file, bp, chars_read - (bp - buffer)) < 0) - rv = errno; - - if (fstat (file, &nfinfo) < 0 && rv == 0) - rv = errno; - - if (close (file) < 0 && rv == 0) + { + rv = errno; + close (file); + } + + if (rv == 0 && fstat (file, &nfinfo) < 0) + { + rv = errno; + close (file); + } + + if (rv == 0 && close (file) < 0) rv = errno; } else @@ -671,6 +698,38 @@ history_truncate_file (const char *fname, int lines) return rv; } +/* Use stdio to write the history file after mmap or malloc fails, on the + assumption that the stdio library can allocate the smaller buffers it uses. */ +static int +history_write_slow (int fd, HIST_ENTRY **the_history, int nelements, int overwrite) +{ + FILE *fp; + int i, j, e; + + fp = fdopen (fd, overwrite ? "w" : "a"); + if (fp == 0) + return -1; + + for (j = 0, i = history_length - nelements; i < history_length; i++) + { + if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0]) + fprintf (fp, "%s\n", the_history[i]->timestamp); + if (fprintf (fp, "%s\n", the_history[i]->line) < 0) + goto slow_write_error; + } + if (fflush (fp) < 0) + { +slow_write_error: + e = errno; + fclose (fp); + errno = e; + return -1; + } + if (fclose (fp) < 0) + return -1; + return 0; +} + /* Workhorse function for writing history. Writes the last NELEMENT entries from the history list to FILENAME. OVERWRITE is non-zero if you wish to replace FILENAME with the entries. */ @@ -680,7 +739,7 @@ history_do_write (const char *filename, int nelements, int overwrite) register int i; char *output, *tempname, *histname; int file, mode, rv, exists; - struct stat finfo, nfinfo; + struct stat finfo; #ifdef HISTORY_USE_MMAP size_t cursize; @@ -718,8 +777,8 @@ history_do_write (const char *filename, int nelements, int overwrite) Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */ { HIST_ENTRY **the_history; /* local */ - register int j; - int buffer_size; + size_t j; + size_t buffer_size; char *buffer; the_history = history_list (); @@ -739,6 +798,8 @@ history_do_write (const char *filename, int nelements, int overwrite) if ((void *)buffer == MAP_FAILED) { mmap_error: + if ((rv = history_write_slow (file, the_history, nelements, overwrite)) == 0) + goto write_success; rv = errno; close (file); if (tempname) @@ -751,6 +812,8 @@ mmap_error: buffer = (char *)malloc (buffer_size); if (buffer == 0) { + if ((rv = history_write_slow (file, the_history, nelements, overwrite)) == 0) + goto write_success; rv = errno; close (file); if (tempname) @@ -789,6 +852,7 @@ mmap_error: if (close (file) < 0 && rv == 0) rv = errno; +write_success: if (rv == 0 && histname && tempname) rv = histfile_restore (tempname, histname); |