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