summaryrefslogtreecommitdiff
path: root/histfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'histfile.c')
-rw-r--r--histfile.c118
1 files changed, 91 insertions, 27 deletions
diff --git a/histfile.c b/histfile.c
index 3bfec55..e4d0b37 100644
--- a/histfile.c
+++ b/histfile.c
@@ -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);