This commit was generated by cvs2svn to track changes on a CVS vendor
[external/binutils.git] / readline / histfile.c
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
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
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
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         if (line_end - 1 >= line_start && *(line_end - 1) == '\r')
232           *(line_end - 1) = '\0';
233         else
234           *line_end = '\0';
235
236         if (*line_start)
237           add_history (line_start);
238
239         current_line++;
240
241         if (current_line >= to)
242           break;
243
244         line_start = line_end + 1;
245       }
246
247   FREE (input);
248 #ifndef HAVE_MMAP
249   FREE (buffer);
250 #else
251   munmap (buffer, file_size);
252 #endif
253
254   return (0);
255 }
256
257 /* Truncate the history file FNAME, leaving only LINES trailing lines.
258    If FNAME is NULL, then use ~/.history.  Returns 0 on success, errno
259    on failure. */
260 int
261 history_truncate_file (fname, lines)
262      const char *fname;
263      int lines;
264 {
265   char *buffer, *filename, *bp;
266   int file, chars_read, rv;
267   struct stat finfo;
268   size_t file_size;
269
270   buffer = (char *)NULL;
271   filename = history_filename (fname);
272   file = open (filename, O_RDONLY|O_BINARY, 0666);
273   rv = 0;
274
275   /* Don't try to truncate non-regular files. */
276   if (file == -1 || fstat (file, &finfo) == -1)
277     {
278       rv = errno;
279       if (file != -1)
280         close (file);
281       goto truncate_exit;
282     }
283
284   if (S_ISREG (finfo.st_mode) == 0)
285     {
286       close (file);
287 #ifdef EFTYPE
288       rv = EFTYPE;
289 #else
290       rv = EINVAL;
291 #endif
292       goto truncate_exit;
293     }
294
295   file_size = (size_t)finfo.st_size;
296
297   /* check for overflow on very large files */
298   if (file_size != finfo.st_size || file_size + 1 < file_size)
299     {
300       close (file);
301 #if defined (EFBIG)
302       rv = errno = EFBIG;
303 #elif defined (EOVERFLOW)
304       rv = errno = EOVERFLOW;
305 #else
306       rv = errno = EINVAL;
307 #endif
308       goto truncate_exit;
309     }
310
311   buffer = (char *)malloc (file_size + 1);
312   if (buffer == 0)
313     {
314       close (file);
315       goto truncate_exit;
316     }
317
318   chars_read = read (file, buffer, file_size);
319   close (file);
320
321   if (chars_read <= 0)
322     {
323       rv = (chars_read < 0) ? errno : 0;
324       goto truncate_exit;
325     }
326
327   /* Count backwards from the end of buffer until we have passed
328      LINES lines. */
329   for (bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
330     {
331       if (*bp == '\n')
332         lines--;
333     }
334
335   /* If this is the first line, then the file contains exactly the
336      number of lines we want to truncate to, so we don't need to do
337      anything.  It's the first line if we don't find a newline between
338      the current value of i and 0.  Otherwise, write from the start of
339      this line until the end of the buffer. */
340   for ( ; bp > buffer; bp--)
341     if (*bp == '\n')
342       {
343         bp++;
344         break;
345       }
346
347   /* Write only if there are more lines in the file than we want to
348      truncate to. */
349   if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
350     {
351       write (file, bp, chars_read - (bp - buffer));
352
353 #if defined (__BEOS__)
354       /* BeOS ignores O_TRUNC. */
355       ftruncate (file, chars_read - (bp - buffer));
356 #endif
357
358       close (file);
359     }
360
361  truncate_exit:
362
363   FREE (buffer);
364
365   free (filename);
366   return rv;
367 }
368
369 /* Workhorse function for writing history.  Writes NELEMENT entries
370    from the history list to FILENAME.  OVERWRITE is non-zero if you
371    wish to replace FILENAME with the entries. */
372 static int
373 history_do_write (filename, nelements, overwrite)
374      const char *filename;
375      int nelements, overwrite;
376 {
377   register int i;
378   char *output;
379   int file, mode, rv;
380   size_t cursize;
381
382 #ifdef HAVE_MMAP
383   mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
384 #else
385   mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
386 #endif
387   output = history_filename (filename);
388   rv = 0;
389
390   if ((file = open (output, mode, 0600)) == -1)
391     {
392       FREE (output);
393       return (errno);
394     }
395
396 #ifdef HAVE_MMAP
397   cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
398 #endif
399
400   if (nelements > history_length)
401     nelements = history_length;
402
403   /* Build a buffer of all the lines to write, and write them in one syscall.
404      Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
405   {
406     HIST_ENTRY **the_history;   /* local */
407     register int j;
408     int buffer_size;
409     char *buffer;
410
411     the_history = history_list ();
412     /* Calculate the total number of bytes to write. */
413     for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
414       buffer_size += 1 + strlen (the_history[i]->line);
415
416     /* Allocate the buffer, and fill it. */
417 #ifdef HAVE_MMAP
418     if (ftruncate (file, buffer_size+cursize) == -1)
419       goto mmap_error;
420     buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
421     if ((void *)buffer == MAP_FAILED)
422       {
423 mmap_error:
424         rv = errno;
425         FREE (output);
426         close (file);
427         return rv;
428       }
429 #else    
430     buffer = (char *)malloc (buffer_size);
431     if (buffer == 0)
432       {
433         rv = errno;
434         FREE (output);
435         close (file);
436         return rv;
437       }
438 #endif
439
440     for (j = 0, i = history_length - nelements; i < history_length; i++)
441       {
442         strcpy (buffer + j, the_history[i]->line);
443         j += strlen (the_history[i]->line);
444         buffer[j++] = '\n';
445       }
446
447 #ifdef HAVE_MMAP
448     if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0)
449       rv = errno;
450 #else
451     if (write (file, buffer, buffer_size) < 0)
452       rv = errno;
453     free (buffer);
454 #endif
455   }
456
457   close (file);
458
459   FREE (output);
460
461   return (rv);
462 }
463
464 /* Append NELEMENT entries to FILENAME.  The entries appended are from
465    the end of the list minus NELEMENTs up to the end of the list. */
466 int
467 append_history (nelements, filename)
468      int nelements;
469      const char *filename;
470 {
471   return (history_do_write (filename, nelements, HISTORY_APPEND));
472 }
473
474 /* Overwrite FILENAME with the current history.  If FILENAME is NULL,
475    then write the history list to ~/.history.  Values returned
476    are as in read_history ().*/
477 int
478 write_history (filename)
479      const char *filename;
480 {
481   return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
482 }