diff options
Diffstat (limited to 'readline/histfile.c')
-rw-r--r-- | readline/histfile.c | 215 |
1 files changed, 155 insertions, 60 deletions
diff --git a/readline/histfile.c b/readline/histfile.c index b908e2261f8..60a91251b7a 100644 --- a/readline/histfile.c +++ b/readline/histfile.c @@ -48,12 +48,26 @@ # include <unistd.h> #endif -#if defined (HAVE_STRING_H) -# include <string.h> -#else -# include <strings.h> -#endif /* !HAVE_STRING_H */ +#if defined (__EMX__) || defined (__CYGWIN__) +# undef HAVE_MMAP +#endif + +#ifdef HAVE_MMAP +# include <sys/mman.h> + +# ifdef MAP_FILE +# define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE) +# define MAP_WFLAGS (MAP_FILE|MAP_SHARED) +# else +# define MAP_RFLAGS MAP_PRIVATE +# define MAP_WFLAGS MAP_SHARED +# endif + +# ifndef MAP_FAILED +# define MAP_FAILED ((void *)-1) +# endif +#endif /* HAVE_MMAP */ /* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment on win 95/98/nt), we want to open files with O_BINARY mode so that there @@ -84,9 +98,10 @@ extern int errno; filename to read_history (), or write_history (). */ static char * history_filename (filename) - char *filename; + const char *filename; { - char *return_val, *home; + char *return_val; + const char *home; int home_len; return_val = filename ? savestring (filename) : (char *)NULL; @@ -94,7 +109,7 @@ history_filename (filename) if (return_val) return (return_val); - home = get_env_value ("HOME"); + home = sh_get_env_value ("HOME"); if (home == 0) { @@ -104,7 +119,7 @@ history_filename (filename) else home_len = strlen (home); - return_val = xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */ + return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */ strcpy (return_val, home); return_val[home_len] = '/'; #if defined (__MSDOS__) @@ -121,7 +136,7 @@ history_filename (filename) successful, or errno if not. */ int read_history (filename) - char *filename; + const char *filename; { return (read_history_range (filename, 0, -1)); } @@ -133,11 +148,11 @@ read_history (filename) ~/.history. Returns 0 if successful, or errno if not. */ int read_history_range (filename, from, to) - char *filename; + const char *filename; int from, to; { - register int line_start, line_end; - char *input, *buffer; + register char *line_start, *line_end; + char *input, *buffer, *bufend; int file, current_line, chars_read; struct stat finfo; size_t file_size; @@ -156,23 +171,39 @@ read_history_range (filename, from, to) { #if defined (EFBIG) errno = EFBIG; +#elif defined (EOVERFLOW) + errno = EOVERFLOW; #endif goto error_and_exit; } - buffer = xmalloc (file_size + 1); +#ifdef HAVE_MMAP + /* We map read/write and private so we can change newlines to NULs without + affecting the underlying object. */ + buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0); + if ((void *)buffer == MAP_FAILED) + goto error_and_exit; + chars_read = file_size; +#else + buffer = (char *)malloc (file_size + 1); + if (buffer == 0) + goto error_and_exit; chars_read = read (file, buffer, file_size); +#endif if (chars_read < 0) { error_and_exit: + chars_read = errno; if (file >= 0) close (file); FREE (input); +#ifndef HAVE_MMAP FREE (buffer); +#endif - return (errno); + return (chars_read); } close (file); @@ -182,29 +213,25 @@ read_history_range (filename, from, to) to = chars_read; /* Start at beginning of file, work to end. */ - line_start = line_end = current_line = 0; + bufend = buffer + chars_read; + current_line = 0; /* Skip lines until we are at FROM. */ - while (line_start < chars_read && current_line < from) - { - for (line_end = line_start; line_end < chars_read; line_end++) - if (buffer[line_end] == '\n') - { - current_line++; - line_start = line_end + 1; - if (current_line == from) - break; - } - } + for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++) + if (*line_end == '\n') + { + current_line++; + line_start = line_end + 1; + } /* If there are lines left to gobble, then gobble them now. */ - for (line_end = line_start; line_end < chars_read; line_end++) - if (buffer[line_end] == '\n') + for (line_end = line_start; line_end < bufend; line_end++) + if (*line_end == '\n') { - buffer[line_end] = '\0'; + *line_end = '\0'; - if (buffer[line_start]) - add_history (buffer + line_start); + if (*line_start) + add_history (line_start); current_line++; @@ -215,34 +242,52 @@ read_history_range (filename, from, to) } FREE (input); +#ifndef HAVE_MMAP FREE (buffer); +#else + munmap (buffer, file_size); +#endif return (0); } /* Truncate the history file FNAME, leaving only LINES trailing lines. - If FNAME is NULL, then use ~/.history. */ + If FNAME is NULL, then use ~/.history. Returns 0 on success, errno + on failure. */ int history_truncate_file (fname, lines) - char *fname; + const char *fname; int lines; { - register int i; - int file, chars_read; - char *buffer, *filename; + char *buffer, *filename, *bp; + int file, chars_read, rv; struct stat finfo; size_t file_size; buffer = (char *)NULL; filename = history_filename (fname); file = open (filename, O_RDONLY|O_BINARY, 0666); + rv = 0; + /* Don't try to truncate non-regular files. */ if (file == -1 || fstat (file, &finfo) == -1) - goto truncate_exit; + { + rv = errno; + if (file != -1) + close (file); + goto truncate_exit; + } - /* Don't try to truncate non-regular files. */ - if (S_ISREG(finfo.st_mode) == 0) - goto truncate_exit; + if (S_ISREG (finfo.st_mode) == 0) + { + close (file); +#ifdef EFTYPE + rv = EFTYPE; +#else + rv = EINVAL; +#endif + goto truncate_exit; + } file_size = (size_t)finfo.st_size; @@ -251,23 +296,36 @@ history_truncate_file (fname, lines) { close (file); #if defined (EFBIG) - errno = EFBIG; + rv = errno = EFBIG; +#elif defined (EOVERFLOW) + rv = errno = EOVERFLOW; +#else + rv = errno = EINVAL; #endif goto truncate_exit; } - buffer = xmalloc (file_size + 1); + buffer = (char *)malloc (file_size + 1); + if (buffer == 0) + { + close (file); + goto truncate_exit; + } + chars_read = read (file, buffer, file_size); close (file); if (chars_read <= 0) - goto truncate_exit; + { + rv = (chars_read < 0) ? errno : 0; + goto truncate_exit; + } /* Count backwards from the end of buffer until we have passed LINES lines. */ - for (i = chars_read - 1; lines && i; i--) + for (bp = buffer + chars_read - 1; lines && bp > buffer; bp--) { - if (buffer[i] == '\n') + if (*bp == '\n') lines--; } @@ -276,22 +334,22 @@ history_truncate_file (fname, lines) 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. */ - for ( ; i; i--) - if (buffer[i] == '\n') + for ( ; bp > buffer; bp--) + if (*bp == '\n') { - i++; + bp++; break; } /* Write only if there are more lines in the file than we want to truncate to. */ - if (i && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1)) + if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1)) { - write (file, buffer + i, chars_read - i); + write (file, bp, chars_read - (bp - buffer)); #if defined (__BEOS__) /* BeOS ignores O_TRUNC. */ - ftruncate (file, chars_read - i); + ftruncate (file, chars_read - (bp - buffer)); #endif close (file); @@ -302,7 +360,7 @@ history_truncate_file (fname, lines) FREE (buffer); free (filename); - return 0; + return rv; } /* Workhorse function for writing history. Writes NELEMENT entries @@ -310,15 +368,21 @@ history_truncate_file (fname, lines) wish to replace FILENAME with the entries. */ static int history_do_write (filename, nelements, overwrite) - char *filename; + const char *filename; int nelements, overwrite; { register int i; char *output; - int file, mode; + int file, mode, rv; + size_t cursize; +#ifdef HAVE_MMAP + mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY; +#else mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY; +#endif output = history_filename (filename); + rv = 0; if ((file = open (output, mode, 0600)) == -1) { @@ -326,6 +390,10 @@ history_do_write (filename, nelements, overwrite) return (errno); } +#ifdef HAVE_MMAP + cursize = overwrite ? 0 : lseek (file, 0, SEEK_END); +#endif + if (nelements > history_length) nelements = history_length; @@ -343,7 +411,28 @@ history_do_write (filename, nelements, overwrite) buffer_size += 1 + strlen (the_history[i]->line); /* Allocate the buffer, and fill it. */ - buffer = xmalloc (buffer_size); +#ifdef HAVE_MMAP + if (ftruncate (file, buffer_size+cursize) == -1) + goto mmap_error; + buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize); + if ((void *)buffer == MAP_FAILED) + { +mmap_error: + rv = errno; + FREE (output); + close (file); + return rv; + } +#else + buffer = (char *)malloc (buffer_size); + if (buffer == 0) + { + rv = errno; + FREE (output); + close (file); + return rv; + } +#endif for (j = 0, i = history_length - nelements; i < history_length; i++) { @@ -352,15 +441,21 @@ history_do_write (filename, nelements, overwrite) buffer[j++] = '\n'; } - write (file, buffer, buffer_size); +#ifdef HAVE_MMAP + if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0) + rv = errno; +#else + if (write (file, buffer, buffer_size) < 0) + rv = errno; free (buffer); +#endif } close (file); FREE (output); - return (0); + return (rv); } /* Append NELEMENT entries to FILENAME. The entries appended are from @@ -368,7 +463,7 @@ history_do_write (filename, nelements, overwrite) int append_history (nelements, filename) int nelements; - char *filename; + const char *filename; { return (history_do_write (filename, nelements, HISTORY_APPEND)); } @@ -378,7 +473,7 @@ append_history (nelements, filename) are as in read_history ().*/ int write_history (filename) - char *filename; + const char *filename; { return (history_do_write (filename, history_length, HISTORY_OVERWRITE)); } |