xref: /openbsd-src/gnu/lib/libreadline/histfile.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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 || *home == '\0') {
100     errno = ENOENT;
101     return (NULL);
102   }
103   home_len = strlen (home);
104 
105   return_val = xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
106   strcpy (return_val, home);
107   return_val[home_len] = '/';
108 #if defined (__MSDOS__)
109   strcpy (return_val + home_len + 1, "_history");
110 #else
111   strcpy (return_val + home_len + 1, ".history");
112 #endif
113 
114   return (return_val);
115 }
116 
117 /* Add the contents of FILENAME to the history list, a line at a time.
118    If FILENAME is NULL, then read from ~/.history.  Returns 0 if
119    successful, or errno if not. */
120 int
121 read_history (filename)
122      char *filename;
123 {
124   return (read_history_range (filename, 0, -1));
125 }
126 
127 /* Read a range of lines from FILENAME, adding them to the history list.
128    Start reading at the FROM'th line and end at the TO'th.  If FROM
129    is zero, start at the beginning.  If TO is less than FROM, read
130    until the end of the file.  If FILENAME is NULL, then read from
131    ~/.history.  Returns 0 if successful, or errno if not. */
132 int
133 read_history_range (filename, from, to)
134      char *filename;
135      int from, to;
136 {
137   register int line_start, line_end;
138   char *input, *buffer;
139   int file, current_line, chars_read;
140   struct stat finfo;
141   size_t file_size;
142 
143   buffer = (char *)NULL;
144   if ((input = history_filename (filename)))
145     file = open (input, O_RDONLY|O_BINARY, 0666);
146   else
147     file = -1;
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   if ((filename = history_filename (fname)))
238     file = open (filename, O_RDONLY|O_BINARY, 0666);
239   else
240     file = -1;
241 
242   if (file == -1 || fstat (file, &finfo) == -1)
243     goto truncate_exit;
244 
245   /* Don't try to truncate non-regular files. */
246   if (S_ISREG(finfo.st_mode) == 0)
247     goto truncate_exit;
248 
249   file_size = (size_t)finfo.st_size;
250 
251   /* check for overflow on very large files */
252   if (file_size != finfo.st_size || file_size + 1 < file_size)
253     {
254       close (file);
255 #if defined (EFBIG)
256       errno = EFBIG;
257 #endif
258       goto truncate_exit;
259     }
260 
261   buffer = xmalloc (file_size + 1);
262   chars_read = read (file, buffer, file_size);
263   close (file);
264 
265   if (chars_read <= 0)
266     goto truncate_exit;
267 
268   /* Count backwards from the end of buffer until we have passed
269      LINES lines. */
270   for (i = chars_read - 1; lines && i; i--)
271     {
272       if (buffer[i] == '\n')
273 	lines--;
274     }
275 
276   /* If this is the first line, then the file contains exactly the
277      number of lines we want to truncate to, so we don't need to do
278      anything.  It's the first line if we don't find a newline between
279      the current value of i and 0.  Otherwise, write from the start of
280      this line until the end of the buffer. */
281   for ( ; i; i--)
282     if (buffer[i] == '\n')
283       {
284 	i++;
285 	break;
286       }
287 
288   /* Write only if there are more lines in the file than we want to
289      truncate to. */
290   if (i && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
291     {
292       write (file, buffer + i, chars_read - i);
293 
294 #if defined (__BEOS__)
295       /* BeOS ignores O_TRUNC. */
296       ftruncate (file, chars_read - i);
297 #endif
298 
299       close (file);
300     }
301 
302  truncate_exit:
303 
304   FREE (buffer);
305 
306   free (filename);
307   return 0;
308 }
309 
310 /* Workhorse function for writing history.  Writes NELEMENT entries
311    from the history list to FILENAME.  OVERWRITE is non-zero if you
312    wish to replace FILENAME with the entries. */
313 static int
314 history_do_write (filename, nelements, overwrite)
315      char *filename;
316      int nelements, overwrite;
317 {
318   register int i;
319   char *output;
320   int file, mode;
321 
322   mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
323   output = history_filename (filename);
324 
325   if (!output || (file = open (output, mode, 0600)) == -1)
326     {
327       FREE (output);
328       return (errno);
329     }
330 
331   if (nelements > history_length)
332     nelements = history_length;
333 
334   /* Build a buffer of all the lines to write, and write them in one syscall.
335      Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
336   {
337     HIST_ENTRY **the_history;	/* local */
338     register int j;
339     int buffer_size;
340     char *buffer;
341 
342     the_history = history_list ();
343     /* Calculate the total number of bytes to write. */
344     for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
345       buffer_size += 1 + strlen (the_history[i]->line);
346 
347     /* Allocate the buffer, and fill it. */
348     buffer = xmalloc (buffer_size);
349 
350     for (j = 0, i = history_length - nelements; i < history_length; i++)
351       {
352 	strcpy (buffer + j, the_history[i]->line);
353 	j += strlen (the_history[i]->line);
354 	buffer[j++] = '\n';
355       }
356 
357     write (file, buffer, buffer_size);
358     free (buffer);
359   }
360 
361   close (file);
362 
363   FREE (output);
364 
365   return (0);
366 }
367 
368 /* Append NELEMENT entries to FILENAME.  The entries appended are from
369    the end of the list minus NELEMENTs up to the end of the list. */
370 int
371 append_history (nelements, filename)
372      int nelements;
373      char *filename;
374 {
375   return (history_do_write (filename, nelements, HISTORY_APPEND));
376 }
377 
378 /* Overwrite FILENAME with the current history.  If FILENAME is NULL,
379    then write the history list to ~/.history.  Values returned
380    are as in read_history ().*/
381 int
382 write_history (filename)
383      char *filename;
384 {
385   return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
386 }
387