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 *
history_filename(filename)100 history_filename (filename)
101      const char *filename;
102 {
103   char *return_val;
104   const char *home;
105   int home_len;
106 
107   return_val = filename ? savestring (filename) : (char *)NULL;
108 
109   if (return_val)
110     return (return_val);
111 
112   home = sh_get_env_value ("HOME");
113 
114   if (home == 0)
115     {
116       home = ".";
117       home_len = 1;
118     }
119   else
120     home_len = strlen (home);
121 
122   return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
123   strcpy (return_val, home);
124   return_val[home_len] = '/';
125 #if defined (__MSDOS__)
126   strcpy (return_val + home_len + 1, "_history");
127 #else
128   strcpy (return_val + home_len + 1, ".history");
129 #endif
130 
131   return (return_val);
132 }
133 
134 /* Add the contents of FILENAME to the history list, a line at a time.
135    If FILENAME is NULL, then read from ~/.history.  Returns 0 if
136    successful, or errno if not. */
137 int
read_history(filename)138 read_history (filename)
139      const char *filename;
140 {
141   return (read_history_range (filename, 0, -1));
142 }
143 
144 /* Read a range of lines from FILENAME, adding them to the history list.
145    Start reading at the FROM'th line and end at the TO'th.  If FROM
146    is zero, start at the beginning.  If TO is less than FROM, read
147    until the end of the file.  If FILENAME is NULL, then read from
148    ~/.history.  Returns 0 if successful, or errno if not. */
149 int
read_history_range(filename,from,to)150 read_history_range (filename, from, to)
151      const char *filename;
152      int from, to;
153 {
154   register char *line_start, *line_end;
155   char *input, *buffer, *bufend;
156   int file, current_line, chars_read;
157   struct stat finfo;
158   size_t file_size;
159 
160   buffer = (char *)NULL;
161   input = history_filename (filename);
162   file = open (input, O_RDONLY|O_BINARY, 0666);
163 
164   if ((file < 0) || (fstat (file, &finfo) == -1))
165     goto error_and_exit;
166 
167   file_size = (size_t)finfo.st_size;
168 
169   /* check for overflow on very large files */
170   if (file_size != finfo.st_size || file_size + 1 < file_size)
171     {
172 #if defined (EFBIG)
173       errno = EFBIG;
174 #elif defined (EOVERFLOW)
175       errno = EOVERFLOW;
176 #endif
177       goto error_and_exit;
178     }
179 
180 #ifdef HAVE_MMAP
181   /* We map read/write and private so we can change newlines to NULs without
182      affecting the underlying object. */
183   buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
184   if ((void *)buffer == MAP_FAILED)
185     goto error_and_exit;
186   chars_read = file_size;
187 #else
188   buffer = (char *)malloc (file_size + 1);
189   if (buffer == 0)
190     goto error_and_exit;
191 
192   chars_read = read (file, buffer, file_size);
193 #endif
194   if (chars_read < 0)
195     {
196   error_and_exit:
197       chars_read = errno;
198       if (file >= 0)
199 	close (file);
200 
201       FREE (input);
202 #ifndef HAVE_MMAP
203       FREE (buffer);
204 #endif
205 
206       return (chars_read);
207     }
208 
209   close (file);
210 
211   /* Set TO to larger than end of file if negative. */
212   if (to < 0)
213     to = chars_read;
214 
215   /* Start at beginning of file, work to end. */
216   bufend = buffer + chars_read;
217   current_line = 0;
218 
219   /* Skip lines until we are at FROM. */
220   for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
221     if (*line_end == '\n')
222       {
223 	current_line++;
224 	line_start = line_end + 1;
225       }
226 
227   /* If there are lines left to gobble, then gobble them now. */
228   for (line_end = line_start; line_end < bufend; line_end++)
229     if (*line_end == '\n')
230       {
231 	*line_end = '\0';
232 
233 	if (*line_start)
234 	  add_history (line_start);
235 
236 	current_line++;
237 
238 	if (current_line >= to)
239 	  break;
240 
241 	line_start = line_end + 1;
242       }
243 
244   FREE (input);
245 #ifndef HAVE_MMAP
246   FREE (buffer);
247 #else
248   munmap (buffer, file_size);
249 #endif
250 
251   return (0);
252 }
253 
254 /* Truncate the history file FNAME, leaving only LINES trailing lines.
255    If FNAME is NULL, then use ~/.history.  Returns 0 on success, errno
256    on failure. */
257 int
history_truncate_file(fname,lines)258 history_truncate_file (fname, lines)
259      const char *fname;
260      int lines;
261 {
262   char *buffer, *filename, *bp;
263   int file, chars_read, rv;
264   struct stat finfo;
265   size_t file_size;
266 
267   buffer = (char *)NULL;
268   filename = history_filename (fname);
269   file = open (filename, O_RDONLY|O_BINARY, 0666);
270   rv = 0;
271 
272   /* Don't try to truncate non-regular files. */
273   if (file == -1 || fstat (file, &finfo) == -1)
274     {
275       rv = errno;
276       if (file != -1)
277 	close (file);
278       goto truncate_exit;
279     }
280 
281   if (S_ISREG (finfo.st_mode) == 0)
282     {
283       close (file);
284 #ifdef EFTYPE
285       rv = EFTYPE;
286 #else
287       rv = EINVAL;
288 #endif
289       goto truncate_exit;
290     }
291 
292   file_size = (size_t)finfo.st_size;
293 
294   /* check for overflow on very large files */
295   if (file_size != finfo.st_size || file_size + 1 < file_size)
296     {
297       close (file);
298 #if defined (EFBIG)
299       rv = errno = EFBIG;
300 #elif defined (EOVERFLOW)
301       rv = errno = EOVERFLOW;
302 #else
303       rv = errno = EINVAL;
304 #endif
305       goto truncate_exit;
306     }
307 
308   buffer = (char *)malloc (file_size + 1);
309   if (buffer == 0)
310     {
311       close (file);
312       goto truncate_exit;
313     }
314 
315   chars_read = read (file, buffer, file_size);
316   close (file);
317 
318   if (chars_read <= 0)
319     {
320       rv = (chars_read < 0) ? errno : 0;
321       goto truncate_exit;
322     }
323 
324   /* Count backwards from the end of buffer until we have passed
325      LINES lines. */
326   for (bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
327     {
328       if (*bp == '\n')
329 	lines--;
330     }
331 
332   /* If this is the first line, then the file contains exactly the
333      number of lines we want to truncate to, so we don't need to do
334      anything.  It's the first line if we don't find a newline between
335      the current value of i and 0.  Otherwise, write from the start of
336      this line until the end of the buffer. */
337   for ( ; bp > buffer; bp--)
338     if (*bp == '\n')
339       {
340 	bp++;
341 	break;
342       }
343 
344   /* Write only if there are more lines in the file than we want to
345      truncate to. */
346   if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
347     {
348       write (file, bp, chars_read - (bp - buffer));
349 
350 #if defined (__BEOS__)
351       /* BeOS ignores O_TRUNC. */
352       ftruncate (file, chars_read - (bp - buffer));
353 #endif
354 
355       close (file);
356     }
357 
358  truncate_exit:
359 
360   FREE (buffer);
361 
362   free (filename);
363   return rv;
364 }
365 
366 /* Workhorse function for writing history.  Writes NELEMENT entries
367    from the history list to FILENAME.  OVERWRITE is non-zero if you
368    wish to replace FILENAME with the entries. */
369 static int
history_do_write(filename,nelements,overwrite)370 history_do_write (filename, nelements, overwrite)
371      const char *filename;
372      int nelements, overwrite;
373 {
374   register int i;
375   char *output;
376   int file, mode, rv;
377   size_t cursize;
378 
379 #ifdef HAVE_MMAP
380   mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
381 #else
382   mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
383 #endif
384   output = history_filename (filename);
385   rv = 0;
386 
387   if ((file = open (output, mode, 0600)) == -1)
388     {
389       FREE (output);
390       return (errno);
391     }
392 
393 #ifdef HAVE_MMAP
394   cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
395 #endif
396 
397   if (nelements > history_length)
398     nelements = history_length;
399 
400   /* Build a buffer of all the lines to write, and write them in one syscall.
401      Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
402   {
403     HIST_ENTRY **the_history;	/* local */
404     register int j;
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 = 0, 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 
437     for (j = 0, i = history_length - nelements; i < history_length; i++)
438       {
439 	strcpy (buffer + j, the_history[i]->line);
440 	j += strlen (the_history[i]->line);
441 	buffer[j++] = '\n';
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) < 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
append_history(nelements,filename)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
write_history(filename)475 write_history (filename)
476      const char *filename;
477 {
478   return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
479 }
480