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#if !defined(_MINIX) && !(defined(__BEOS__) || defined(__HAIKU__))
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
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. */
137int
138read_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. */
149int
150read_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. */
257int
258history_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__) || defined(__HAIKU__))
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. */
369static int
370history_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      {
420mmap_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. */
463int
464append_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 ().*/
474int
475write_history (filename)
476     const char *filename;
477{
478  return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
479}
480