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)
92extern 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() */
102int 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 (). */
111static char *
112history_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. */
149int
150read_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. */
161int
162read_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. */
305int
306history_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. */
426static int
427history_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 {
485mmap_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. */
534int
535append_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 ().*/
545int
546write_history (filename)
547 const char *filename;
548{
549 return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
550}
551