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