Merge from vendor branch BIND:
[dragonfly.git] / contrib / libreadline / 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 (HAVE_STRING_H)
52 #  include <string.h>
53 #else
54 #  include <strings.h>
55 #endif /* !HAVE_STRING_H */
56
57
58 /* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
59    on win 95/98/nt), we want to open files with O_BINARY mode so that there
60    is no \n -> \r\n conversion performed.  On other systems, we don't want to
61    mess around with O_BINARY at all, so we ensure that it's defined to 0. */
62 #if defined (__EMX__) || defined (__CYGWIN__)
63 #  ifndef O_BINARY
64 #    define O_BINARY 0
65 #  endif
66 #else /* !__EMX__ && !__CYGWIN__ */
67 #  undef O_BINARY
68 #  define O_BINARY 0
69 #endif /* !__EMX__ && !__CYGWIN__ */
70
71 #include <errno.h>
72 #if !defined (errno)
73 extern int errno;
74 #endif /* !errno */
75
76 #include "history.h"
77 #include "histlib.h"
78
79 #include "rlshell.h"
80 #include "xmalloc.h"
81
82 /* Return the string that should be used in the place of this
83    filename.  This only matters when you don't specify the
84    filename to read_history (), or write_history (). */
85 static char *
86 history_filename (filename)
87      char *filename;
88 {
89   char *return_val, *home;
90   int home_len;
91
92   return_val = filename ? savestring (filename) : (char *)NULL;
93
94   if (return_val)
95     return (return_val);
96   
97   home = get_env_value ("HOME");
98
99   if (home == 0)
100     {
101       home = ".";
102       home_len = 1;
103     }
104   else
105     home_len = strlen (home);
106
107   return_val = xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
108   strcpy (return_val, home);
109   return_val[home_len] = '/';
110 #if defined (__MSDOS__)
111   strcpy (return_val + home_len + 1, "_history");
112 #else
113   strcpy (return_val + home_len + 1, ".history");
114 #endif
115
116   return (return_val);
117 }
118
119 /* Add the contents of FILENAME to the history list, a line at a time.
120    If FILENAME is NULL, then read from ~/.history.  Returns 0 if
121    successful, or errno if not. */
122 int
123 read_history (filename)
124      char *filename;
125 {
126   return (read_history_range (filename, 0, -1));
127 }
128
129 /* Read a range of lines from FILENAME, adding them to the history list.
130    Start reading at the FROM'th line and end at the TO'th.  If FROM
131    is zero, start at the beginning.  If TO is less than FROM, read
132    until the end of the file.  If FILENAME is NULL, then read from
133    ~/.history.  Returns 0 if successful, or errno if not. */
134 int
135 read_history_range (filename, from, to)
136      char *filename;
137      int from, to;
138 {
139   register int line_start, line_end;
140   char *input, *buffer;
141   int file, current_line, chars_read;
142   struct stat finfo;
143   size_t file_size;
144
145   buffer = (char *)NULL;
146   input = history_filename (filename);
147   file = open (input, O_RDONLY|O_BINARY, 0666);
148
149   if ((file < 0) || (fstat (file, &finfo) == -1))
150     goto error_and_exit;
151
152   file_size = (size_t)finfo.st_size;
153
154   /* check for overflow on very large files */
155   if (file_size != finfo.st_size || file_size + 1 < file_size)
156     {
157 #if defined (EFBIG)
158       errno = EFBIG;
159 #endif
160       goto error_and_exit;
161     }
162
163   buffer = xmalloc (file_size + 1);
164
165   chars_read = read (file, buffer, file_size);
166   if (chars_read < 0)
167     {
168   error_and_exit:
169       if (file >= 0)
170         close (file);
171
172       FREE (input);
173       FREE (buffer);
174
175       return (errno);
176     }
177
178   close (file);
179
180   /* Set TO to larger than end of file if negative. */
181   if (to < 0)
182     to = chars_read;
183
184   /* Start at beginning of file, work to end. */
185   line_start = line_end = current_line = 0;
186
187   /* Skip lines until we are at FROM. */
188   while (line_start < chars_read && current_line < from)
189     {
190       for (line_end = line_start; line_end < chars_read; line_end++)
191         if (buffer[line_end] == '\n')
192           {
193             current_line++;
194             line_start = line_end + 1;
195             if (current_line == from)
196               break;
197           }
198     }
199
200   /* If there are lines left to gobble, then gobble them now. */
201   for (line_end = line_start; line_end < chars_read; line_end++)
202     if (buffer[line_end] == '\n')
203       {
204         buffer[line_end] = '\0';
205
206         if (buffer[line_start])
207           add_history (buffer + line_start);
208
209         current_line++;
210
211         if (current_line >= to)
212           break;
213
214         line_start = line_end + 1;
215       }
216
217   FREE (input);
218   FREE (buffer);
219
220   return (0);
221 }
222
223 /* Truncate the history file FNAME, leaving only LINES trailing lines.
224    If FNAME is NULL, then use ~/.history. */
225 int
226 history_truncate_file (fname, lines)
227      char *fname;
228      int lines;
229 {
230   register int i;
231   int file, chars_read;
232   char *buffer, *filename;
233   struct stat finfo;
234   size_t file_size;
235
236   buffer = (char *)NULL;
237   filename = history_filename (fname);
238   file = open (filename, O_RDONLY|O_BINARY, 0666);
239
240   if (file == -1 || fstat (file, &finfo) == -1)
241     goto truncate_exit;
242
243   /* Don't try to truncate non-regular files. */
244   if (S_ISREG(finfo.st_mode) == 0)
245     goto truncate_exit;
246
247   file_size = (size_t)finfo.st_size;
248
249   /* check for overflow on very large files */
250   if (file_size != finfo.st_size || file_size + 1 < file_size)
251     {
252       close (file);
253 #if defined (EFBIG)
254       errno = EFBIG;
255 #endif
256       goto truncate_exit;
257     }
258
259   buffer = xmalloc (file_size + 1);
260   chars_read = read (file, buffer, file_size);
261   close (file);
262
263   if (chars_read <= 0)
264     goto truncate_exit;
265
266   /* Count backwards from the end of buffer until we have passed
267      LINES lines. */
268   for (i = chars_read - 1; lines && i; i--)
269     {
270       if (buffer[i] == '\n')
271         lines--;
272     }
273
274   /* If this is the first line, then the file contains exactly the
275      number of lines we want to truncate to, so we don't need to do
276      anything.  It's the first line if we don't find a newline between
277      the current value of i and 0.  Otherwise, write from the start of
278      this line until the end of the buffer. */
279   for ( ; i; i--)
280     if (buffer[i] == '\n')
281       {
282         i++;
283         break;
284       }
285
286   /* Write only if there are more lines in the file than we want to
287      truncate to. */
288   if (i && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
289     {
290       write (file, buffer + i, chars_read - i);
291
292 #if defined (__BEOS__)
293       /* BeOS ignores O_TRUNC. */
294       ftruncate (file, chars_read - i);
295 #endif
296
297       close (file);
298     }
299
300  truncate_exit:
301
302   FREE (buffer);
303
304   free (filename);
305   return 0;
306 }
307
308 /* Workhorse function for writing history.  Writes NELEMENT entries
309    from the history list to FILENAME.  OVERWRITE is non-zero if you
310    wish to replace FILENAME with the entries. */
311 static int
312 history_do_write (filename, nelements, overwrite)
313      char *filename;
314      int nelements, overwrite;
315 {
316   register int i;
317   char *output;
318   int file, mode;
319
320   mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
321   output = history_filename (filename);
322
323   if ((file = open (output, mode, 0600)) == -1)
324     {
325       FREE (output);
326       return (errno);
327     }
328
329   if (nelements > history_length)
330     nelements = history_length;
331
332   /* Build a buffer of all the lines to write, and write them in one syscall.
333      Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
334   {
335     HIST_ENTRY **the_history;   /* local */
336     register int j;
337     int buffer_size;
338     char *buffer;
339
340     the_history = history_list ();
341     /* Calculate the total number of bytes to write. */
342     for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
343       buffer_size += 1 + strlen (the_history[i]->line);
344
345     /* Allocate the buffer, and fill it. */
346     buffer = xmalloc (buffer_size);
347
348     for (j = 0, i = history_length - nelements; i < history_length; i++)
349       {
350         strcpy (buffer + j, the_history[i]->line);
351         j += strlen (the_history[i]->line);
352         buffer[j++] = '\n';
353       }
354
355     write (file, buffer, buffer_size);
356     free (buffer);
357   }
358
359   close (file);
360
361   FREE (output);
362
363   return (0);
364 }
365
366 /* Append NELEMENT entries to FILENAME.  The entries appended are from
367    the end of the list minus NELEMENTs up to the end of the list. */
368 int
369 append_history (nelements, filename)
370      int nelements;
371      char *filename;
372 {
373   return (history_do_write (filename, nelements, HISTORY_APPEND));
374 }
375
376 /* Overwrite FILENAME with the current history.  If FILENAME is NULL,
377    then write the history list to ~/.history.  Values returned
378    are as in read_history ().*/
379 int
380 write_history (filename)
381      char *filename;
382 {
383   return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
384 }