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