histfile.c revision 30971
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 1, 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   675 Mass Ave, Cambridge, MA 02139, 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#include <sys/file.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38
39#if defined (HAVE_STDLIB_H)
40#  include <stdlib.h>
41#else
42#  include "ansi_stdlib.h"
43#endif /* HAVE_STDLIB_H */
44
45#if defined (HAVE_UNISTD_H)
46#  include <unistd.h>
47#endif
48
49#if defined (HAVE_STRING_H)
50#  include <string.h>
51#else
52#  include <strings.h>
53#endif /* !HAVE_STRING_H */
54
55#if defined (__EMX__)
56#  ifndef O_BINARY
57#    define O_BINARY 0
58#  endif
59#else /* !__EMX__ */
60   /* If we're not compiling for __EMX__, we don't want this at all.  Ever. */
61#  undef O_BINARY
62#  define O_BINARY 0
63#endif /* !__EMX__ */
64
65#include <errno.h>
66#if !defined (errno)
67extern int errno;
68#endif /* !errno */
69
70#include "history.h"
71#include "histlib.h"
72
73/* Functions imported from shell.c */
74extern char *get_env_value ();
75
76extern char *xmalloc (), *xrealloc ();
77
78/* Return the string that should be used in the place of this
79   filename.  This only matters when you don't specify the
80   filename to read_history (), or write_history (). */
81static char *
82history_filename (filename)
83     char *filename;
84{
85  char *return_val, *home;
86  int home_len;
87
88  return_val = filename ? savestring (filename) : (char *)NULL;
89
90  if (return_val)
91    return (return_val);
92
93  home = get_env_value ("HOME");
94
95  if (home == 0)
96    {
97      home = ".";
98      home_len = 1;
99    }
100  else
101    home_len = strlen (home);
102
103  return_val = xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
104  strcpy (return_val, home);
105  return_val[home_len] = '/';
106  strcpy (return_val + home_len + 1, ".history");
107
108  return (return_val);
109}
110
111/* Add the contents of FILENAME to the history list, a line at a time.
112   If FILENAME is NULL, then read from ~/.history.  Returns 0 if
113   successful, or errno if not. */
114int
115read_history (filename)
116     char *filename;
117{
118  return (read_history_range (filename, 0, -1));
119}
120
121/* Read a range of lines from FILENAME, adding them to the history list.
122   Start reading at the FROM'th line and end at the TO'th.  If FROM
123   is zero, start at the beginning.  If TO is less than FROM, read
124   until the end of the file.  If FILENAME is NULL, then read from
125   ~/.history.  Returns 0 if successful, or errno if not. */
126int
127read_history_range (filename, from, to)
128     char *filename;
129     int from, to;
130{
131  register int line_start, line_end;
132  char *input, *buffer = (char *)NULL;
133  int file, current_line;
134  struct stat finfo;
135
136  input = history_filename (filename);
137  file = open (input, O_RDONLY|O_BINARY, 0666);
138
139  if ((file < 0) || (fstat (file, &finfo) == -1))
140    goto error_and_exit;
141
142  buffer = xmalloc ((int)finfo.st_size + 1);
143
144  if (read (file, buffer, finfo.st_size) != finfo.st_size)
145    {
146  error_and_exit:
147      if (file >= 0)
148	close (file);
149
150      FREE (input);
151      FREE (buffer);
152
153      return (errno);
154    }
155
156  close (file);
157
158  /* Set TO to larger than end of file if negative. */
159  if (to < 0)
160    to = finfo.st_size;
161
162  /* Start at beginning of file, work to end. */
163  line_start = line_end = current_line = 0;
164
165  /* Skip lines until we are at FROM. */
166  while (line_start < finfo.st_size && current_line < from)
167    {
168      for (line_end = line_start; line_end < finfo.st_size; line_end++)
169	if (buffer[line_end] == '\n')
170	  {
171	    current_line++;
172	    line_start = line_end + 1;
173	    if (current_line == from)
174	      break;
175	  }
176    }
177
178  /* If there are lines left to gobble, then gobble them now. */
179  for (line_end = line_start; line_end < finfo.st_size; line_end++)
180    if (buffer[line_end] == '\n')
181      {
182	buffer[line_end] = '\0';
183
184	if (buffer[line_start])
185	  add_history (buffer + line_start);
186
187	current_line++;
188
189	if (current_line >= to)
190	  break;
191
192	line_start = line_end + 1;
193      }
194
195  FREE (input);
196  FREE (buffer);
197
198  return (0);
199}
200
201/* Truncate the history file FNAME, leaving only LINES trailing lines.
202   If FNAME is NULL, then use ~/.history. */
203int
204history_truncate_file (fname, lines)
205     char *fname;
206     register int lines;
207{
208  register int i;
209  int file, chars_read;
210  char *buffer, *filename;
211  struct stat finfo;
212
213  buffer = (char *)NULL;
214  filename = history_filename (fname);
215  file = open (filename, O_RDONLY|O_BINARY, 0666);
216
217  if (file == -1 || fstat (file, &finfo) == -1)
218    goto truncate_exit;
219
220  buffer = xmalloc ((int)finfo.st_size + 1);
221  chars_read = read (file, buffer, finfo.st_size);
222  close (file);
223
224  if (chars_read <= 0)
225    goto truncate_exit;
226
227  /* Count backwards from the end of buffer until we have passed
228     LINES lines. */
229  for (i = chars_read - 1; lines && i; i--)
230    {
231      if (buffer[i] == '\n')
232	lines--;
233    }
234
235  /* If this is the first line, then the file contains exactly the
236     number of lines we want to truncate to, so we don't need to do
237     anything.  It's the first line if we don't find a newline between
238     the current value of i and 0.  Otherwise, write from the start of
239     this line until the end of the buffer. */
240  for ( ; i; i--)
241    if (buffer[i] == '\n')
242      {
243	i++;
244	break;
245      }
246
247  /* Write only if there are more lines in the file than we want to
248     truncate to. */
249  if (i && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
250    {
251      write (file, buffer + i, finfo.st_size - i);
252      close (file);
253    }
254
255 truncate_exit:
256
257  FREE (buffer);
258
259  free (filename);
260  return 0;
261}
262
263/* Workhorse function for writing history.  Writes NELEMENT entries
264   from the history list to FILENAME.  OVERWRITE is non-zero if you
265   wish to replace FILENAME with the entries. */
266static int
267history_do_write (filename, nelements, overwrite)
268     char *filename;
269     int nelements, overwrite;
270{
271  register int i;
272  char *output;
273  int file, mode;
274
275  mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
276  output = history_filename (filename);
277
278  if ((file = open (output, mode, 0600)) == -1)
279    {
280      FREE (output);
281      return (errno);
282    }
283
284  if (nelements > history_length)
285    nelements = history_length;
286
287  /* Build a buffer of all the lines to write, and write them in one syscall.
288     Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
289  {
290    HIST_ENTRY **the_history;	/* local */
291    register int j;
292    int buffer_size;
293    char *buffer;
294
295    the_history = history_list ();
296    /* Calculate the total number of bytes to write. */
297    for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
298      buffer_size += 1 + strlen (the_history[i]->line);
299
300    /* Allocate the buffer, and fill it. */
301    buffer = xmalloc (buffer_size);
302
303    for (j = 0, i = history_length - nelements; i < history_length; i++)
304      {
305	strcpy (buffer + j, the_history[i]->line);
306	j += strlen (the_history[i]->line);
307	buffer[j++] = '\n';
308      }
309
310    write (file, buffer, buffer_size);
311    free (buffer);
312  }
313
314  close (file);
315
316  FREE (output);
317
318  return (0);
319}
320
321/* Append NELEMENT entries to FILENAME.  The entries appended are from
322   the end of the list minus NELEMENTs up to the end of the list. */
323int
324append_history (nelements, filename)
325     int nelements;
326     char *filename;
327{
328  return (history_do_write (filename, nelements, HISTORY_APPEND));
329}
330
331/* Overwrite FILENAME with the current history.  If FILENAME is NULL,
332   then write the history list to ~/.history.  Values returned
333   are as in read_history ().*/
334int
335write_history (filename)
336     char *filename;
337{
338  return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
339}
340