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