]> git.ipfire.org Git - thirdparty/bash.git/blob - lib/readline/histfile.c
Imported from ../bash-2.01.1.tar.gz.
[thirdparty/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, 0600)) != -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, 0600)) == -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 }