X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=readline%2Fhistfile.c;h=30a618247f1ec8a7be1c9202ae84224c7e8d5d5e;hb=cc88a640ca1d0356c5feb40bb48869bab5a2bdce;hp=3325b7fc1f7193c4dbea6c32026db3b95c035582;hpb=c862e87b3ec8647ab6c1bb08443088ea81c74225;p=deliverable%2Fbinutils-gdb.git diff --git a/readline/histfile.c b/readline/histfile.c index 3325b7fc1f..30a618247f 100644 --- a/readline/histfile.c +++ b/readline/histfile.c @@ -1,30 +1,34 @@ /* histfile.c - functions to manipulate the history file. */ -/* Copyright (C) 1989, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989-2010 Free Software Foundation, Inc. - This file contains the GNU History Library (the Library), a set of + This file contains the GNU History Library (History), a set of routines for managing the text of previously typed lines. - The Library is free software; you can redistribute it and/or modify + History is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - The Library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + History is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - The GNU General Public License is often shipped with GNU software, and - is generally kept in a file called COPYING or LICENSE. If you do not - have a copy of the license, write to the Free Software Foundation, - 675 Mass Ave, Cambridge, MA 02139, USA. */ + You should have received a copy of the GNU General Public License + along with History. If not, see . +*/ /* The goal is to make the implementation transparent, so that you don't have to know what data types are used, just what functions you can call. I think I have done that. */ + #define READLINE_LIBRARY +#if defined (__TANDEM) +# include +#endif + #if defined (HAVE_CONFIG_H) # include #endif @@ -32,10 +36,10 @@ #include #include -#ifndef _MINIX +#if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H) # include #endif -#include +#include "posixstat.h" #include #if defined (HAVE_STDLIB_H) @@ -48,21 +52,41 @@ # include #endif -#if defined (HAVE_STRING_H) -# include -#else -# include -#endif /* !HAVE_STRING_H */ +#include #if defined (__EMX__) +# undef HAVE_MMAP +#endif + +#ifdef HISTORY_USE_MMAP +# include + +# 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 /* HISTORY_USE_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 + is no \n -> \r\n conversion performed. On other systems, we don't want to + mess around with O_BINARY at all, so we ensure that it's defined to 0. */ +#if defined (__EMX__) || defined (__CYGWIN__) # ifndef O_BINARY # define O_BINARY 0 # endif -#else /* !__EMX__ */ - /* If we're not compiling for __EMX__, we don't want this at all. Ever. */ +#else /* !__EMX__ && !__CYGWIN__ */ # undef O_BINARY # define O_BINARY 0 -#endif /* !__EMX__ */ +#endif /* !__EMX__ && !__CYGWIN__ */ #include #if !defined (errno) @@ -72,19 +96,25 @@ extern int errno; #include "history.h" #include "histlib.h" -/* Functions imported from shell.c */ -extern char *get_env_value (); +#include "rlshell.h" +#include "xmalloc.h" -extern char *xmalloc (), *xrealloc (); +/* If non-zero, we write timestamps to the history file in history_do_write() */ +int history_write_timestamps = 0; + +/* Does S look like the beginning of a history timestamp entry? Placeholder + for more extensive tests. */ +#define HIST_TIMESTAMP_START(s) (*(s) == history_comment_char && isdigit ((s)[1]) ) /* Return the string that should be used in the place of this filename. This only matters when you don't specify the 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; @@ -92,20 +122,28 @@ history_filename (filename) if (return_val) return (return_val); - home = get_env_value ("HOME"); + home = sh_get_env_value ("HOME"); if (home == 0) { +#if 0 home = "."; home_len = 1; +#else + return (NULL); +#endif } 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__) + strcpy (return_val + home_len + 1, "_history"); +#else strcpy (return_val + home_len + 1, ".history"); +#endif return (return_val); } @@ -115,7 +153,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)); } @@ -127,18 +165,25 @@ 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; - int file, current_line; + register char *line_start, *line_end, *p; + char *input, *buffer, *bufend, *last_ts; + int file, current_line, chars_read; struct stat finfo; size_t file_size; +#if defined (EFBIG) + int overflow_errno = EFBIG; +#elif defined (EOVERFLOW) + int overflow_errno = EOVERFLOW; +#else + int overflow_errno = EIO; +#endif - buffer = (char *)NULL; + buffer = last_ts = (char *)NULL; input = history_filename (filename); - file = open (input, O_RDONLY|O_BINARY, 0666); + file = input ? open (input, O_RDONLY|O_BINARY, 0666) : -1; if ((file < 0) || (fstat (file, &finfo) == -1)) goto error_and_exit; @@ -148,59 +193,97 @@ read_history_range (filename, from, to) /* check for overflow on very large files */ if (file_size != finfo.st_size || file_size + 1 < file_size) { -#if defined (EFBIG) - errno = EFBIG; -#endif + errno = overflow_errno; goto error_and_exit; } - buffer = xmalloc (file_size + 1); -#if 0 - if (read (file, buffer, file_size) != file_size) +#ifdef HISTORY_USE_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) + { + errno = overflow_errno; + goto error_and_exit; + } + chars_read = file_size; #else - if (read (file, buffer, file_size) < 0) + buffer = (char *)malloc (file_size + 1); + if (buffer == 0) + { + errno = overflow_errno; + goto error_and_exit; + } + + chars_read = read (file, buffer, file_size); #endif + if (chars_read < 0) { error_and_exit: + if (errno != 0) + chars_read = errno; + else + chars_read = EIO; if (file >= 0) close (file); FREE (input); +#ifndef HISTORY_USE_MMAP FREE (buffer); +#endif - return (errno); + return (chars_read); } close (file); /* Set TO to larger than end of file if negative. */ if (to < 0) - to = file_size; + 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 < file_size && current_line < from) - { - for (line_end = line_start; line_end < file_size; 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') + { + p = line_end + 1; + /* If we see something we think is a timestamp, continue with this + line. We should check more extensively here... */ + if (HIST_TIMESTAMP_START(p) == 0) + current_line++; + line_start = p; + } /* If there are lines left to gobble, then gobble them now. */ - for (line_end = line_start; line_end < file_size; 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'; + /* Change to allow Windows-like \r\n end of line delimiter. */ + if (line_end > line_start && line_end[-1] == '\r') + line_end[-1] = '\0'; + else + *line_end = '\0'; - if (buffer[line_start]) - add_history (buffer + line_start); + if (*line_start) + { + if (HIST_TIMESTAMP_START(line_start) == 0) + { + add_history (line_start); + if (last_ts) + { + add_history_time (last_ts); + last_ts = NULL; + } + } + else + { + last_ts = line_start; + current_line--; + } + } current_line++; @@ -211,30 +294,52 @@ read_history_range (filename, from, to) } FREE (input); +#ifndef HISTORY_USE_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, *bp1; /* bp1 == bp+1 */ + 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); + file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1; + 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; + } + + 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; @@ -243,24 +348,40 @@ 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--) + 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--) { - if (buffer[i] == '\n') + if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0) lines--; + bp1 = bp; } /* If this is the first line, then the file contains exactly the @@ -268,22 +389,25 @@ 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') - { - i++; - break; - } + for ( ; bp > buffer; bp--) + { + if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0) + { + bp++; + break; + } + bp1 = bp; + } /* 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, file_size - i); + write (file, bp, chars_read - (bp - buffer)); #if defined (__BEOS__) /* BeOS ignores O_TRUNC. */ - ftruncate (file, file_size - i); + ftruncate (file, chars_read - (bp - buffer)); #endif close (file); @@ -293,8 +417,8 @@ history_truncate_file (fname, lines) FREE (buffer); - free (filename); - return 0; + xfree (filename); + return rv; } /* Workhorse function for writing history. Writes NELEMENT entries @@ -302,22 +426,33 @@ 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; +#ifdef HISTORY_USE_MMAP + size_t cursize; + 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); + file = output ? open (output, mode, 0600) : -1; + rv = 0; - if ((file = open (output, mode, 0600)) == -1) + if (file == -1) { FREE (output); return (errno); } +#ifdef HISTORY_USE_MMAP + cursize = overwrite ? 0 : lseek (file, 0, SEEK_END); +#endif + if (nelements > history_length) nelements = history_length; @@ -332,27 +467,68 @@ history_do_write (filename, nelements, overwrite) the_history = history_list (); /* Calculate the total number of bytes to write. */ for (buffer_size = 0, i = history_length - nelements; i < history_length; i++) - buffer_size += 1 + strlen (the_history[i]->line); +#if 0 + buffer_size += 2 + HISTENT_BYTES (the_history[i]); +#else + { + if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0]) + buffer_size += strlen (the_history[i]->timestamp) + 1; + buffer_size += strlen (the_history[i]->line) + 1; + } +#endif /* Allocate the buffer, and fill it. */ - buffer = xmalloc (buffer_size); +#ifdef HISTORY_USE_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++) { + if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0]) + { + strcpy (buffer + j, the_history[i]->timestamp); + j += strlen (the_history[i]->timestamp); + buffer[j++] = '\n'; + } strcpy (buffer + j, the_history[i]->line); j += strlen (the_history[i]->line); buffer[j++] = '\n'; } - write (file, buffer, buffer_size); - free (buffer); +#ifdef HISTORY_USE_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; + xfree (buffer); +#endif } close (file); FREE (output); - return (0); + return (rv); } /* Append NELEMENT entries to FILENAME. The entries appended are from @@ -360,7 +536,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)); } @@ -370,7 +546,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)); }