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 (__EMX__) || defined (__CYGWIN__)
52 # undef HAVE_MMAP
53 #endif
54
55 #ifdef HAVE_MMAP
56 # include <sys/mman.h>
57
58 # ifdef MAP_FILE
59 # define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE)
60 # define MAP_WFLAGS (MAP_FILE|MAP_SHARED)
61 # else
62 # define MAP_RFLAGS MAP_PRIVATE
63 # define MAP_WFLAGS MAP_SHARED
64 # endif
65
66 # ifndef MAP_FAILED
67 # define MAP_FAILED ((void *)-1)
68 # endif
69
70 #endif /* HAVE_MMAP */
71
72 /* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
73 on win 95/98/nt), we want to open files with O_BINARY mode so that there
74 is no \n -> \r\n conversion performed. On other systems, we don't want to
75 mess around with O_BINARY at all, so we ensure that it's defined to 0. */
76 #if defined (__EMX__) || defined (__CYGWIN__)
77 # ifndef O_BINARY
78 # define O_BINARY 0
79 # endif
80 #else /* !__EMX__ && !__CYGWIN__ */
81 # undef O_BINARY
82 # define O_BINARY 0
83 #endif /* !__EMX__ && !__CYGWIN__ */
84
85 #include <errno.h>
86 #if !defined (errno)
87 extern int errno;
88 #endif /* !errno */
89
90 #include "history.h"
91 #include "histlib.h"
92
93 #include "rlshell.h"
94 #include "xmalloc.h"
95
96 /* Return the string that should be used in the place of this
97 filename. This only matters when you don't specify the
98 filename to read_history (), or write_history (). */
99 static char *
history_filename(filename)100 history_filename (filename)
101 const char *filename;
102 {
103 char *return_val;
104 const char *home;
105 int home_len;
106
107 return_val = filename ? savestring (filename) : (char *)NULL;
108
109 if (return_val)
110 return (return_val);
111
112 home = sh_get_env_value ("HOME");
113
114 if (home == 0)
115 {
116 home = ".";
117 home_len = 1;
118 }
119 else
120 home_len = strlen (home);
121
122 return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
123 strcpy (return_val, home);
124 return_val[home_len] = '/';
125 #if defined (__MSDOS__)
126 strcpy (return_val + home_len + 1, "_history");
127 #else
128 strcpy (return_val + home_len + 1, ".history");
129 #endif
130
131 return (return_val);
132 }
133
134 /* Add the contents of FILENAME to the history list, a line at a time.
135 If FILENAME is NULL, then read from ~/.history. Returns 0 if
136 successful, or errno if not. */
137 int
read_history(filename)138 read_history (filename)
139 const char *filename;
140 {
141 return (read_history_range (filename, 0, -1));
142 }
143
144 /* Read a range of lines from FILENAME, adding them to the history list.
145 Start reading at the FROM'th line and end at the TO'th. If FROM
146 is zero, start at the beginning. If TO is less than FROM, read
147 until the end of the file. If FILENAME is NULL, then read from
148 ~/.history. Returns 0 if successful, or errno if not. */
149 int
read_history_range(filename,from,to)150 read_history_range (filename, from, to)
151 const char *filename;
152 int from, to;
153 {
154 register char *line_start, *line_end;
155 char *input, *buffer, *bufend;
156 int file, current_line, chars_read;
157 struct stat finfo;
158 size_t file_size;
159
160 buffer = (char *)NULL;
161 input = history_filename (filename);
162 file = open (input, O_RDONLY|O_BINARY, 0666);
163
164 if ((file < 0) || (fstat (file, &finfo) == -1))
165 goto error_and_exit;
166
167 file_size = (size_t)finfo.st_size;
168
169 /* check for overflow on very large files */
170 if (file_size != finfo.st_size || file_size + 1 < file_size)
171 {
172 #if defined (EFBIG)
173 errno = EFBIG;
174 #elif defined (EOVERFLOW)
175 errno = EOVERFLOW;
176 #endif
177 goto error_and_exit;
178 }
179
180 #ifdef HAVE_MMAP
181 /* We map read/write and private so we can change newlines to NULs without
182 affecting the underlying object. */
183 buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
184 if ((void *)buffer == MAP_FAILED)
185 goto error_and_exit;
186 chars_read = file_size;
187 #else
188 buffer = (char *)malloc (file_size + 1);
189 if (buffer == 0)
190 goto error_and_exit;
191
192 chars_read = read (file, buffer, file_size);
193 #endif
194 if (chars_read < 0)
195 {
196 error_and_exit:
197 chars_read = errno;
198 if (file >= 0)
199 close (file);
200
201 FREE (input);
202 #ifndef HAVE_MMAP
203 FREE (buffer);
204 #endif
205
206 return (chars_read);
207 }
208
209 close (file);
210
211 /* Set TO to larger than end of file if negative. */
212 if (to < 0)
213 to = chars_read;
214
215 /* Start at beginning of file, work to end. */
216 bufend = buffer + chars_read;
217 current_line = 0;
218
219 /* Skip lines until we are at FROM. */
220 for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
221 if (*line_end == '\n')
222 {
223 current_line++;
224 line_start = line_end + 1;
225 }
226
227 /* If there are lines left to gobble, then gobble them now. */
228 for (line_end = line_start; line_end < bufend; line_end++)
229 if (*line_end == '\n')
230 {
231 *line_end = '\0';
232
233 if (*line_start)
234 add_history (line_start);
235
236 current_line++;
237
238 if (current_line >= to)
239 break;
240
241 line_start = line_end + 1;
242 }
243
244 FREE (input);
245 #ifndef HAVE_MMAP
246 FREE (buffer);
247 #else
248 munmap (buffer, file_size);
249 #endif
250
251 return (0);
252 }
253
254 /* Truncate the history file FNAME, leaving only LINES trailing lines.
255 If FNAME is NULL, then use ~/.history. Returns 0 on success, errno
256 on failure. */
257 int
history_truncate_file(fname,lines)258 history_truncate_file (fname, lines)
259 const char *fname;
260 int lines;
261 {
262 char *buffer, *filename, *bp;
263 int file, chars_read, rv;
264 struct stat finfo;
265 size_t file_size;
266
267 buffer = (char *)NULL;
268 filename = history_filename (fname);
269 file = open (filename, O_RDONLY|O_BINARY, 0666);
270 rv = 0;
271
272 /* Don't try to truncate non-regular files. */
273 if (file == -1 || fstat (file, &finfo) == -1)
274 {
275 rv = errno;
276 if (file != -1)
277 close (file);
278 goto truncate_exit;
279 }
280
281 if (S_ISREG (finfo.st_mode) == 0)
282 {
283 close (file);
284 #ifdef EFTYPE
285 rv = EFTYPE;
286 #else
287 rv = EINVAL;
288 #endif
289 goto truncate_exit;
290 }
291
292 file_size = (size_t)finfo.st_size;
293
294 /* check for overflow on very large files */
295 if (file_size != finfo.st_size || file_size + 1 < file_size)
296 {
297 close (file);
298 #if defined (EFBIG)
299 rv = errno = EFBIG;
300 #elif defined (EOVERFLOW)
301 rv = errno = EOVERFLOW;
302 #else
303 rv = errno = EINVAL;
304 #endif
305 goto truncate_exit;
306 }
307
308 buffer = (char *)malloc (file_size + 1);
309 if (buffer == 0)
310 {
311 close (file);
312 goto truncate_exit;
313 }
314
315 chars_read = read (file, buffer, file_size);
316 close (file);
317
318 if (chars_read <= 0)
319 {
320 rv = (chars_read < 0) ? errno : 0;
321 goto truncate_exit;
322 }
323
324 /* Count backwards from the end of buffer until we have passed
325 LINES lines. */
326 for (bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
327 {
328 if (*bp == '\n')
329 lines--;
330 }
331
332 /* If this is the first line, then the file contains exactly the
333 number of lines we want to truncate to, so we don't need to do
334 anything. It's the first line if we don't find a newline between
335 the current value of i and 0. Otherwise, write from the start of
336 this line until the end of the buffer. */
337 for ( ; bp > buffer; bp--)
338 if (*bp == '\n')
339 {
340 bp++;
341 break;
342 }
343
344 /* Write only if there are more lines in the file than we want to
345 truncate to. */
346 if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
347 {
348 write (file, bp, chars_read - (bp - buffer));
349
350 #if defined (__BEOS__)
351 /* BeOS ignores O_TRUNC. */
352 ftruncate (file, chars_read - (bp - buffer));
353 #endif
354
355 close (file);
356 }
357
358 truncate_exit:
359
360 FREE (buffer);
361
362 free (filename);
363 return rv;
364 }
365
366 /* Workhorse function for writing history. Writes NELEMENT entries
367 from the history list to FILENAME. OVERWRITE is non-zero if you
368 wish to replace FILENAME with the entries. */
369 static int
history_do_write(filename,nelements,overwrite)370 history_do_write (filename, nelements, overwrite)
371 const char *filename;
372 int nelements, overwrite;
373 {
374 register int i;
375 char *output;
376 int file, mode, rv;
377 size_t cursize;
378
379 #ifdef HAVE_MMAP
380 mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
381 #else
382 mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
383 #endif
384 output = history_filename (filename);
385 rv = 0;
386
387 if ((file = open (output, mode, 0600)) == -1)
388 {
389 FREE (output);
390 return (errno);
391 }
392
393 #ifdef HAVE_MMAP
394 cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
395 #endif
396
397 if (nelements > history_length)
398 nelements = history_length;
399
400 /* Build a buffer of all the lines to write, and write them in one syscall.
401 Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
402 {
403 HIST_ENTRY **the_history; /* local */
404 register int j;
405 int buffer_size;
406 char *buffer;
407
408 the_history = history_list ();
409 /* Calculate the total number of bytes to write. */
410 for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
411 buffer_size += 1 + strlen (the_history[i]->line);
412
413 /* Allocate the buffer, and fill it. */
414 #ifdef HAVE_MMAP
415 if (ftruncate (file, buffer_size+cursize) == -1)
416 goto mmap_error;
417 buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
418 if ((void *)buffer == MAP_FAILED)
419 {
420 mmap_error:
421 rv = errno;
422 FREE (output);
423 close (file);
424 return rv;
425 }
426 #else
427 buffer = (char *)malloc (buffer_size);
428 if (buffer == 0)
429 {
430 rv = errno;
431 FREE (output);
432 close (file);
433 return rv;
434 }
435 #endif
436
437 for (j = 0, i = history_length - nelements; i < history_length; i++)
438 {
439 strcpy (buffer + j, the_history[i]->line);
440 j += strlen (the_history[i]->line);
441 buffer[j++] = '\n';
442 }
443
444 #ifdef HAVE_MMAP
445 if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0)
446 rv = errno;
447 #else
448 if (write (file, buffer, buffer_size) < 0)
449 rv = errno;
450 free (buffer);
451 #endif
452 }
453
454 close (file);
455
456 FREE (output);
457
458 return (rv);
459 }
460
461 /* Append NELEMENT entries to FILENAME. The entries appended are from
462 the end of the list minus NELEMENTs up to the end of the list. */
463 int
append_history(nelements,filename)464 append_history (nelements, filename)
465 int nelements;
466 const char *filename;
467 {
468 return (history_do_write (filename, nelements, HISTORY_APPEND));
469 }
470
471 /* Overwrite FILENAME with the current history. If FILENAME is NULL,
472 then write the history list to ~/.history. Values returned
473 are as in read_history ().*/
474 int
write_history(filename)475 write_history (filename)
476 const char *filename;
477 {
478 return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
479 }
480