xref: /openbsd-src/gnu/lib/libreadline/histfile.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /* histfile.c - functions to manipulate the history file. */
2 
3 /* Copyright (C) 1989, 1992 Free Software Foundation, Inc.
4 
5    This file contains the GNU History Library (the Library), a set of
6    routines for managing the text of previously typed lines.
7 
8    The Library 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 2, or (at your option)
11    any later version.
12 
13    The Library is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17 
18    The GNU General Public License is often shipped with GNU software, and
19    is generally kept in a file called COPYING or LICENSE.  If you do not
20    have a copy of the license, write to the Free Software Foundation,
21    59 Temple Place, Suite 330, Boston, MA 02111 USA. */
22 
23 /* The goal is to make the implementation transparent, so that you
24    don't have to know what data types are used, just what functions
25    you can call.  I think I have done that. */
26 #define READLINE_LIBRARY
27 
28 #if defined (HAVE_CONFIG_H)
29 #  include <config.h>
30 #endif
31 
32 #include <stdio.h>
33 
34 #include <sys/types.h>
35 #ifndef _MINIX
36 #  include <sys/file.h>
37 #endif
38 #include "posixstat.h"
39 #include <fcntl.h>
40 
41 #if defined (HAVE_STDLIB_H)
42 #  include <stdlib.h>
43 #else
44 #  include "ansi_stdlib.h"
45 #endif /* HAVE_STDLIB_H */
46 
47 #if defined (HAVE_UNISTD_H)
48 #  include <unistd.h>
49 #endif
50 
51 #if defined (__EMX__) || defined (__CYGWIN__)
52 #  undef HAVE_MMAP
53 #endif
54 
55 #ifdef HAVE_MMAP
56 #  include <sys/mman.h>
57 
58 #  ifdef MAP_FILE
59 #    define MAP_RFLAGS	(MAP_FILE|MAP_PRIVATE)
60 #    define MAP_WFLAGS	(MAP_FILE|MAP_SHARED)
61 #  else
62 #    define MAP_RFLAGS	MAP_PRIVATE
63 #    define MAP_WFLAGS	MAP_SHARED
64 #  endif
65 
66 #  ifndef MAP_FAILED
67 #    define MAP_FAILED	((void *)-1)
68 #  endif
69 
70 #endif /* HAVE_MMAP */
71 
72 /* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
73    on win 95/98/nt), we want to open files with O_BINARY mode so that there
74    is no \n -> \r\n conversion performed.  On other systems, we don't want to
75    mess around with O_BINARY at all, so we ensure that it's defined to 0. */
76 #if defined (__EMX__) || defined (__CYGWIN__)
77 #  ifndef O_BINARY
78 #    define O_BINARY 0
79 #  endif
80 #else /* !__EMX__ && !__CYGWIN__ */
81 #  undef O_BINARY
82 #  define O_BINARY 0
83 #endif /* !__EMX__ && !__CYGWIN__ */
84 
85 #include <errno.h>
86 #if !defined (errno)
87 extern int errno;
88 #endif /* !errno */
89 
90 #include "history.h"
91 #include "histlib.h"
92 
93 #include "rlshell.h"
94 #include "xmalloc.h"
95 
96 /* Return the string that should be used in the place of this
97    filename.  This only matters when you don't specify the
98    filename to read_history (), or write_history (). */
99 static char *
100 history_filename (filename)
101      const char *filename;
102 {
103   char *return_val;
104   const char *home;
105   int home_len;
106   char dot;
107 
108   return_val = filename ? savestring (filename) : (char *)NULL;
109 
110   if (return_val)
111     return (return_val);
112 
113   home = sh_get_env_value ("HOME");
114 
115   if (home == 0 || *home == '\0') {
116     errno = ENOENT;
117     return (NULL);
118   }
119   home_len = strlen (home);
120 
121 #if defined (__MSDOS__)
122   dot = '_';
123 #else
124   dot = '.';
125 #endif
126   if (asprintf(&return_val, "%s/%c%s", home, dot, "history") == -1)
127 	  memory_error_and_abort("asprintf");
128   return (return_val);
129 }
130 
131 /* Add the contents of FILENAME to the history list, a line at a time.
132    If FILENAME is NULL, then read from ~/.history.  Returns 0 if
133    successful, or errno if not. */
134 int
135 read_history (filename)
136      const char *filename;
137 {
138   return (read_history_range (filename, 0, -1));
139 }
140 
141 /* Read a range of lines from FILENAME, adding them to the history list.
142    Start reading at the FROM'th line and end at the TO'th.  If FROM
143    is zero, start at the beginning.  If TO is less than FROM, read
144    until the end of the file.  If FILENAME is NULL, then read from
145    ~/.history.  Returns 0 if successful, or errno if not. */
146 int
147 read_history_range (filename, from, to)
148      const char *filename;
149      int from, to;
150 {
151   register char *line_start, *line_end;
152   char *input, *buffer, *bufend;
153   int file, current_line, chars_read;
154   struct stat finfo;
155   size_t file_size;
156 
157   buffer = (char *)NULL;
158   if ((input = history_filename (filename)))
159     file = open (input, O_RDONLY|O_BINARY, 0666);
160   else
161     file = -1;
162 
163   if ((file < 0) || (fstat (file, &finfo) == -1))
164     goto error_and_exit;
165 
166   file_size = (size_t)finfo.st_size;
167 
168   /* check for overflow on very large files */
169   if (file_size != finfo.st_size || file_size + 1 < file_size)
170     {
171 #if defined (EFBIG)
172       errno = EFBIG;
173 #elif defined (EOVERFLOW)
174       errno = EOVERFLOW;
175 #endif
176       goto error_and_exit;
177     }
178 
179 #ifdef HAVE_MMAP
180   /* We map read/write and private so we can change newlines to NULs without
181      affecting the underlying object. */
182   buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
183   if ((void *)buffer == MAP_FAILED)
184     goto error_and_exit;
185   chars_read = file_size;
186 #else
187   buffer = (char *)malloc (file_size + 1);
188   if (buffer == 0)
189     goto error_and_exit;
190 
191   chars_read = read (file, buffer, file_size);
192 #endif
193   if (chars_read < 0)
194     {
195   error_and_exit:
196       chars_read = errno;
197       if (file >= 0)
198 	close (file);
199 
200       FREE (input);
201 #ifndef HAVE_MMAP
202       FREE (buffer);
203 #endif
204 
205       return (chars_read);
206     }
207 
208   close (file);
209 
210   /* Set TO to larger than end of file if negative. */
211   if (to < 0)
212     to = chars_read;
213 
214   /* Start at beginning of file, work to end. */
215   bufend = buffer + chars_read;
216   current_line = 0;
217 
218   /* Skip lines until we are at FROM. */
219   for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
220     if (*line_end == '\n')
221       {
222 	current_line++;
223 	line_start = line_end + 1;
224       }
225 
226   /* If there are lines left to gobble, then gobble them now. */
227   for (line_end = line_start; line_end < bufend; line_end++)
228     if (*line_end == '\n')
229       {
230 	*line_end = '\0';
231 
232 	if (*line_start)
233 	  add_history (line_start);
234 
235 	current_line++;
236 
237 	if (current_line >= to)
238 	  break;
239 
240 	line_start = line_end + 1;
241       }
242 
243   FREE (input);
244 #ifndef HAVE_MMAP
245   FREE (buffer);
246 #else
247   munmap (buffer, file_size);
248 #endif
249 
250   return (0);
251 }
252 
253 /* Truncate the history file FNAME, leaving only LINES trailing lines.
254    If FNAME is NULL, then use ~/.history.  Returns 0 on success, errno
255    on failure. */
256 int
257 history_truncate_file (fname, lines)
258      const char *fname;
259      int lines;
260 {
261   char *buffer, *filename, *bp;
262   int file, chars_read, rv;
263   struct stat finfo;
264   size_t file_size;
265 
266   buffer = (char *)NULL;
267   if ((filename = history_filename (fname)))
268     file = open (filename, O_RDONLY|O_BINARY, 0666);
269   else
270     file = -1;
271   rv = 0;
272 
273   /* Don't try to truncate non-regular files. */
274   if (file == -1 || fstat (file, &finfo) == -1)
275     {
276       rv = errno;
277       if (file != -1)
278 	close (file);
279       goto truncate_exit;
280     }
281 
282   if (S_ISREG (finfo.st_mode) == 0)
283     {
284       close (file);
285 #ifdef EFTYPE
286       rv = EFTYPE;
287 #else
288       rv = EINVAL;
289 #endif
290       goto truncate_exit;
291     }
292 
293   file_size = (size_t)finfo.st_size;
294 
295   /* check for overflow on very large files */
296   if (file_size != finfo.st_size || file_size + 1 < file_size)
297     {
298       close (file);
299 #if defined (EFBIG)
300       rv = errno = EFBIG;
301 #elif defined (EOVERFLOW)
302       rv = errno = EOVERFLOW;
303 #else
304       rv = errno = EINVAL;
305 #endif
306       goto truncate_exit;
307     }
308 
309   buffer = (char *)malloc (file_size + 1);
310   if (buffer == 0)
311     {
312       close (file);
313       goto truncate_exit;
314     }
315 
316   chars_read = read (file, buffer, file_size);
317   close (file);
318 
319   if (chars_read <= 0)
320     {
321       rv = (chars_read < 0) ? errno : 0;
322       goto truncate_exit;
323     }
324 
325   /* Count backwards from the end of buffer until we have passed
326      LINES lines. */
327   for (bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
328     {
329       if (*bp == '\n')
330 	lines--;
331     }
332 
333   /* If this is the first line, then the file contains exactly the
334      number of lines we want to truncate to, so we don't need to do
335      anything.  It's the first line if we don't find a newline between
336      the current value of i and 0.  Otherwise, write from the start of
337      this line until the end of the buffer. */
338   for ( ; bp > buffer; bp--)
339     if (*bp == '\n')
340       {
341 	bp++;
342 	break;
343       }
344 
345   /* Write only if there are more lines in the file than we want to
346      truncate to. */
347   if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
348     {
349       write (file, bp, chars_read - (bp - buffer));
350 
351 #if defined (__BEOS__)
352       /* BeOS ignores O_TRUNC. */
353       ftruncate (file, chars_read - (bp - buffer));
354 #endif
355 
356       close (file);
357     }
358 
359  truncate_exit:
360 
361   FREE (buffer);
362 
363   free (filename);
364   return rv;
365 }
366 
367 /* Workhorse function for writing history.  Writes NELEMENT entries
368    from the history list to FILENAME.  OVERWRITE is non-zero if you
369    wish to replace FILENAME with the entries. */
370 static int
371 history_do_write (filename, nelements, overwrite)
372      const char *filename;
373      int nelements, overwrite;
374 {
375   register int i;
376   char *output;
377   int file, mode, rv;
378   size_t cursize;
379 
380 #ifdef HAVE_MMAP
381   mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
382 #else
383   mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
384 #endif
385   output = history_filename (filename);
386   rv = 0;
387 
388   if (!output || (file = open (output, mode, 0600)) == -1)
389     {
390       FREE (output);
391       return (errno);
392     }
393 
394 #ifdef HAVE_MMAP
395   cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
396 #endif
397 
398   if (nelements > history_length)
399     nelements = history_length;
400 
401   /* Build a buffer of all the lines to write, and write them in one syscall.
402      Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
403   {
404     HIST_ENTRY **the_history;	/* local */
405     int buffer_size;
406     char *buffer;
407 
408     the_history = history_list ();
409     /* Calculate the total number of bytes to write. */
410     for (buffer_size = 1, i = history_length - nelements; i < history_length; i++)
411       buffer_size += 1 + strlen (the_history[i]->line);
412 
413     /* Allocate the buffer, and fill it. */
414 #ifdef HAVE_MMAP
415     if (ftruncate (file, buffer_size+cursize) == -1)
416       goto mmap_error;
417     buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
418     if ((void *)buffer == MAP_FAILED)
419       {
420 mmap_error:
421 	rv = errno;
422 	FREE (output);
423 	close (file);
424 	return rv;
425       }
426 #else
427     buffer = (char *)malloc (buffer_size);
428     if (buffer == 0)
429       {
430       	rv = errno;
431 	FREE (output);
432 	close (file);
433 	return rv;
434       }
435 #endif
436     buffer[0] = '\0';
437 
438     for (i = history_length - nelements; i < history_length; i++)
439       {
440 	strlcat (buffer, the_history[i]->line, buffer_size);
441 	strlcat (buffer, "\n", buffer_size);
442       }
443 
444 #ifdef HAVE_MMAP
445     if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0)
446       rv = errno;
447 #else
448     if (write (file, buffer, buffer_size - 1) < 0)
449       rv = errno;
450     free (buffer);
451 #endif
452   }
453 
454   close (file);
455 
456   FREE (output);
457 
458   return (rv);
459 }
460 
461 /* Append NELEMENT entries to FILENAME.  The entries appended are from
462    the end of the list minus NELEMENTs up to the end of the list. */
463 int
464 append_history (nelements, filename)
465      int nelements;
466      const char *filename;
467 {
468   return (history_do_write (filename, nelements, HISTORY_APPEND));
469 }
470 
471 /* Overwrite FILENAME with the current history.  If FILENAME is NULL,
472    then write the history list to ~/.history.  Values returned
473    are as in read_history ().*/
474 int
475 write_history (filename)
476      const char *filename;
477 {
478   return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
479 }
480