xref: /openbsd-src/gnu/lib/libreadline/histfile.c (revision d9924ce156773a00372e11e1e872e7676b997d5d)
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   char dot;
92 
93   return_val = filename ? savestring (filename) : (char *)NULL;
94 
95   if (return_val)
96     return (return_val);
97 
98   home = get_env_value ("HOME");
99 
100   if (home == 0 || *home == '\0') {
101     errno = ENOENT;
102     return (NULL);
103   }
104   home_len = strlen (home);
105 
106 #if defined (__MSDOS__)
107   dot = '_';
108 #else
109   dot = '.';
110 #endif
111   if (asprintf(&return_val, "%s/%c%s", home, dot, "history") == -1)
112 	  memory_error_and_abort("asprintf");
113   return (return_val);
114 }
115 
116 /* Add the contents of FILENAME to the history list, a line at a time.
117    If FILENAME is NULL, then read from ~/.history.  Returns 0 if
118    successful, or errno if not. */
119 int
120 read_history (filename)
121      char *filename;
122 {
123   return (read_history_range (filename, 0, -1));
124 }
125 
126 /* Read a range of lines from FILENAME, adding them to the history list.
127    Start reading at the FROM'th line and end at the TO'th.  If FROM
128    is zero, start at the beginning.  If TO is less than FROM, read
129    until the end of the file.  If FILENAME is NULL, then read from
130    ~/.history.  Returns 0 if successful, or errno if not. */
131 int
132 read_history_range (filename, from, to)
133      char *filename;
134      int from, to;
135 {
136   register int line_start, line_end;
137   char *input, *buffer;
138   int file, current_line, chars_read;
139   struct stat finfo;
140   size_t file_size;
141 
142   buffer = (char *)NULL;
143   if ((input = history_filename (filename)))
144     file = open (input, O_RDONLY|O_BINARY, 0666);
145   else
146     file = -1;
147 
148   if ((file < 0) || (fstat (file, &finfo) == -1))
149     goto error_and_exit;
150 
151   file_size = (size_t)finfo.st_size;
152 
153   /* check for overflow on very large files */
154   if (file_size != finfo.st_size || file_size + 1 < file_size)
155     {
156 #if defined (EFBIG)
157       errno = EFBIG;
158 #endif
159       goto error_and_exit;
160     }
161 
162   buffer = xmalloc (file_size + 1);
163 
164   chars_read = read (file, buffer, file_size);
165   if (chars_read < 0)
166     {
167   error_and_exit:
168       if (file >= 0)
169 	close (file);
170 
171       FREE (input);
172       FREE (buffer);
173 
174       return (errno);
175     }
176 
177   close (file);
178 
179   /* Set TO to larger than end of file if negative. */
180   if (to < 0)
181     to = chars_read;
182 
183   /* Start at beginning of file, work to end. */
184   line_start = line_end = current_line = 0;
185 
186   /* Skip lines until we are at FROM. */
187   while (line_start < chars_read && current_line < from)
188     {
189       for (line_end = line_start; line_end < chars_read; line_end++)
190 	if (buffer[line_end] == '\n')
191 	  {
192 	    current_line++;
193 	    line_start = line_end + 1;
194 	    if (current_line == from)
195 	      break;
196 	  }
197     }
198 
199   /* If there are lines left to gobble, then gobble them now. */
200   for (line_end = line_start; line_end < chars_read; line_end++)
201     if (buffer[line_end] == '\n')
202       {
203 	buffer[line_end] = '\0';
204 
205 	if (buffer[line_start])
206 	  add_history (buffer + line_start);
207 
208 	current_line++;
209 
210 	if (current_line >= to)
211 	  break;
212 
213 	line_start = line_end + 1;
214       }
215 
216   FREE (input);
217   FREE (buffer);
218 
219   return (0);
220 }
221 
222 /* Truncate the history file FNAME, leaving only LINES trailing lines.
223    If FNAME is NULL, then use ~/.history. */
224 int
225 history_truncate_file (fname, lines)
226      char *fname;
227      int lines;
228 {
229   register int i;
230   int file, chars_read;
231   char *buffer, *filename;
232   struct stat finfo;
233   size_t file_size;
234 
235   buffer = (char *)NULL;
236   if ((filename = history_filename (fname)))
237     file = open (filename, O_RDONLY|O_BINARY, 0666);
238   else
239     file = -1;
240 
241   if (file == -1 || fstat (file, &finfo) == -1)
242     goto truncate_exit;
243 
244   /* Don't try to truncate non-regular files. */
245   if (S_ISREG(finfo.st_mode) == 0)
246     goto truncate_exit;
247 
248   file_size = (size_t)finfo.st_size;
249 
250   /* check for overflow on very large files */
251   if (file_size != finfo.st_size || file_size + 1 < file_size)
252     {
253       close (file);
254 #if defined (EFBIG)
255       errno = EFBIG;
256 #endif
257       goto truncate_exit;
258     }
259 
260   buffer = xmalloc (file_size + 1);
261   chars_read = read (file, buffer, file_size);
262   close (file);
263 
264   if (chars_read <= 0)
265     goto truncate_exit;
266 
267   /* Count backwards from the end of buffer until we have passed
268      LINES lines. */
269   for (i = chars_read - 1; lines && i; i--)
270     {
271       if (buffer[i] == '\n')
272 	lines--;
273     }
274 
275   /* If this is the first line, then the file contains exactly the
276      number of lines we want to truncate to, so we don't need to do
277      anything.  It's the first line if we don't find a newline between
278      the current value of i and 0.  Otherwise, write from the start of
279      this line until the end of the buffer. */
280   for ( ; i; i--)
281     if (buffer[i] == '\n')
282       {
283 	i++;
284 	break;
285       }
286 
287   /* Write only if there are more lines in the file than we want to
288      truncate to. */
289   if (i && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
290     {
291       write (file, buffer + i, chars_read - i);
292 
293 #if defined (__BEOS__)
294       /* BeOS ignores O_TRUNC. */
295       ftruncate (file, chars_read - i);
296 #endif
297 
298       close (file);
299     }
300 
301  truncate_exit:
302 
303   FREE (buffer);
304 
305   free (filename);
306   return 0;
307 }
308 
309 /* Workhorse function for writing history.  Writes NELEMENT entries
310    from the history list to FILENAME.  OVERWRITE is non-zero if you
311    wish to replace FILENAME with the entries. */
312 static int
313 history_do_write (filename, nelements, overwrite)
314      char *filename;
315      int nelements, overwrite;
316 {
317   register int i;
318   char *output;
319   int file, mode;
320 
321   mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
322   output = history_filename (filename);
323 
324   if (!output || (file = open (output, mode, 0600)) == -1)
325     {
326       FREE (output);
327       return (errno);
328     }
329 
330   if (nelements > history_length)
331     nelements = history_length;
332 
333   /* Build a buffer of all the lines to write, and write them in one syscall.
334      Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
335   {
336     HIST_ENTRY **the_history;	/* local */
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 = 1, 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     buffer[0] = '\0';
348 
349     for (i = history_length - nelements; i < history_length; i++)
350       {
351 	strlcat (buffer, the_history[i]->line, buffer_size);
352 	strlcat (buffer, "\n", buffer_size);
353       }
354 
355     write (file, buffer, buffer_size - 1);
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 }
385