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   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
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.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 (file_size != finfo.st_size || file_size + 1 < file_size)
190    {
191      errno = overflow_errno;
192      goto error_and_exit;
193    }
194
195#ifdef HISTORY_USE_MMAP
196  /* We map read/write and private so we can change newlines to NULs without
197     affecting the underlying object. */
198  buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
199  if ((void *)buffer == MAP_FAILED)
200    {
201      errno = overflow_errno;
202      goto error_and_exit;
203    }
204  chars_read = file_size;
205#else
206  buffer = (char *)malloc (file_size + 1);
207  if (buffer == 0)
208    {
209      errno = overflow_errno;
210      goto error_and_exit;
211    }
212
213  chars_read = read (file, buffer, file_size);
214#endif
215  if (chars_read < 0)
216    {
217  error_and_exit:
218      if (errno != 0)
219	chars_read = errno;
220      else
221	chars_read = EIO;
222      if (file >= 0)
223	close (file);
224
225      FREE (input);
226#ifndef HISTORY_USE_MMAP
227      FREE (buffer);
228#endif
229
230      return (chars_read);
231    }
232
233  close (file);
234
235  /* Set TO to larger than end of file if negative. */
236  if (to < 0)
237    to = chars_read;
238
239  /* Start at beginning of file, work to end. */
240  bufend = buffer + chars_read;
241  current_line = 0;
242
243  /* Skip lines until we are at FROM. */
244  for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
245    if (*line_end == '\n')
246      {
247      	p = line_end + 1;
248      	/* If we see something we think is a timestamp, continue with this
249	   line.  We should check more extensively here... */
250	if (HIST_TIMESTAMP_START(p) == 0)
251	  current_line++;
252	line_start = p;
253      }
254
255  /* If there are lines left to gobble, then gobble them now. */
256  for (line_end = line_start; line_end < bufend; line_end++)
257    if (*line_end == '\n')
258      {
259	/* Change to allow Windows-like \r\n end of line delimiter. */
260	if (line_end > line_start && line_end[-1] == '\r')
261	  line_end[-1] = '\0';
262	else
263	  *line_end = '\0';
264
265	if (*line_start)
266	  {
267	    if (HIST_TIMESTAMP_START(line_start) == 0)
268	      {
269		add_history (line_start);
270		if (last_ts)
271		  {
272		    add_history_time (last_ts);
273		    last_ts = NULL;
274		  }
275	      }
276	    else
277	      {
278		last_ts = line_start;
279		current_line--;
280	      }
281	  }
282
283	current_line++;
284
285	if (current_line >= to)
286	  break;
287
288	line_start = line_end + 1;
289      }
290
291  FREE (input);
292#ifndef HISTORY_USE_MMAP
293  FREE (buffer);
294#else
295  munmap (buffer, file_size);
296#endif
297
298  return (0);
299}
300
301/* Truncate the history file FNAME, leaving only LINES trailing lines.
302   If FNAME is NULL, then use ~/.history.  Returns 0 on success, errno
303   on failure. */
304int
305history_truncate_file (fname, lines)
306     const char *fname;
307     int lines;
308{
309  char *buffer, *filename, *bp, *bp1;		/* bp1 == bp+1 */
310  int file, chars_read, rv;
311  struct stat finfo;
312  size_t file_size;
313
314  buffer = (char *)NULL;
315  filename = history_filename (fname);
316  file = open (filename, O_RDONLY|O_BINARY, 0666);
317  rv = 0;
318
319  /* Don't try to truncate non-regular files. */
320  if (file == -1 || fstat (file, &finfo) == -1)
321    {
322      rv = errno;
323      if (file != -1)
324	close (file);
325      goto truncate_exit;
326    }
327
328  if (S_ISREG (finfo.st_mode) == 0)
329    {
330      close (file);
331#ifdef EFTYPE
332      rv = EFTYPE;
333#else
334      rv = EINVAL;
335#endif
336      goto truncate_exit;
337    }
338
339  file_size = (size_t)finfo.st_size;
340
341  /* check for overflow on very large files */
342  if (file_size != finfo.st_size || file_size + 1 < file_size)
343    {
344      close (file);
345#if defined (EFBIG)
346      rv = errno = EFBIG;
347#elif defined (EOVERFLOW)
348      rv = errno = EOVERFLOW;
349#else
350      rv = errno = EINVAL;
351#endif
352      goto truncate_exit;
353    }
354
355  buffer = (char *)malloc (file_size + 1);
356  if (buffer == 0)
357    {
358      close (file);
359      goto truncate_exit;
360    }
361
362  chars_read = read (file, buffer, file_size);
363  close (file);
364
365  if (chars_read <= 0)
366    {
367      rv = (chars_read < 0) ? errno : 0;
368      goto truncate_exit;
369    }
370
371  /* Count backwards from the end of buffer until we have passed
372     LINES lines.  bp1 is set funny initially.  But since bp[1] can't
373     be a comment character (since it's off the end) and *bp can't be
374     both a newline and the history comment character, it should be OK. */
375  for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
376    {
377      if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
378	lines--;
379      bp1 = bp;
380    }
381
382  /* If this is the first line, then the file contains exactly the
383     number of lines we want to truncate to, so we don't need to do
384     anything.  It's the first line if we don't find a newline between
385     the current value of i and 0.  Otherwise, write from the start of
386     this line until the end of the buffer. */
387  for ( ; bp > buffer; bp--)
388    {
389      if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
390        {
391	  bp++;
392	  break;
393        }
394      bp1 = bp;
395    }
396
397  /* Write only if there are more lines in the file than we want to
398     truncate to. */
399  if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
400    {
401      write (file, bp, chars_read - (bp - buffer));
402
403#if defined (__BEOS__)
404      /* BeOS ignores O_TRUNC. */
405      ftruncate (file, chars_read - (bp - buffer));
406#endif
407
408      close (file);
409    }
410
411 truncate_exit:
412
413  FREE (buffer);
414
415  free (filename);
416  return rv;
417}
418
419/* Workhorse function for writing history.  Writes NELEMENT entries
420   from the history list to FILENAME.  OVERWRITE is non-zero if you
421   wish to replace FILENAME with the entries. */
422static int
423history_do_write (filename, nelements, overwrite)
424     const char *filename;
425     int nelements, overwrite;
426{
427  register int i;
428  char *output;
429  int file, mode, rv;
430#ifdef HISTORY_USE_MMAP
431  size_t cursize;
432
433  mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
434#else
435  mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
436#endif
437  output = history_filename (filename);
438  rv = 0;
439
440  if ((file = open (output, mode, 0600)) == -1)
441    {
442      FREE (output);
443      return (errno);
444    }
445
446#ifdef HISTORY_USE_MMAP
447  cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
448#endif
449
450  if (nelements > history_length)
451    nelements = history_length;
452
453  /* Build a buffer of all the lines to write, and write them in one syscall.
454     Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
455  {
456    HIST_ENTRY **the_history;	/* local */
457    register int j;
458    int buffer_size;
459    char *buffer;
460
461    the_history = history_list ();
462    /* Calculate the total number of bytes to write. */
463    for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
464#if 0
465      buffer_size += 2 + HISTENT_BYTES (the_history[i]);
466#else
467      {
468	if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
469	  buffer_size += strlen (the_history[i]->timestamp) + 1;
470	buffer_size += strlen (the_history[i]->line) + 1;
471      }
472#endif
473
474    /* Allocate the buffer, and fill it. */
475#ifdef HISTORY_USE_MMAP
476    if (ftruncate (file, buffer_size+cursize) == -1)
477      goto mmap_error;
478    buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
479    if ((void *)buffer == MAP_FAILED)
480      {
481mmap_error:
482	rv = errno;
483	FREE (output);
484	close (file);
485	return rv;
486      }
487#else
488    buffer = (char *)malloc (buffer_size);
489    if (buffer == 0)
490      {
491      	rv = errno;
492	FREE (output);
493	close (file);
494	return rv;
495      }
496#endif
497
498    for (j = 0, i = history_length - nelements; i < history_length; i++)
499      {
500	if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
501	  {
502	    strcpy (buffer + j, the_history[i]->timestamp);
503	    j += strlen (the_history[i]->timestamp);
504	    buffer[j++] = '\n';
505	  }
506	strcpy (buffer + j, the_history[i]->line);
507	j += strlen (the_history[i]->line);
508	buffer[j++] = '\n';
509      }
510
511#ifdef HISTORY_USE_MMAP
512    if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0)
513      rv = errno;
514#else
515    if (write (file, buffer, buffer_size) < 0)
516      rv = errno;
517    free (buffer);
518#endif
519  }
520
521  close (file);
522
523  FREE (output);
524
525  return (rv);
526}
527
528/* Append NELEMENT entries to FILENAME.  The entries appended are from
529   the end of the list minus NELEMENTs up to the end of the list. */
530int
531append_history (nelements, filename)
532     int nelements;
533     const char *filename;
534{
535  return (history_do_write (filename, nelements, HISTORY_APPEND));
536}
537
538/* Overwrite FILENAME with the current history.  If FILENAME is NULL,
539   then write the history list to ~/.history.  Values returned
540   are as in read_history ().*/
541int
542write_history (filename)
543     const char *filename;
544{
545  return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
546}
547