xref: /dflybsd-src/contrib/gdb-7/readline/histfile.c (revision 16003dcfd2baa152f5dd24794ec9f36e139eaeb8)
1*6b445a62SJohn Marino /* histfile.c - functions to manipulate the history file. */
2*6b445a62SJohn Marino 
3*6b445a62SJohn Marino /* Copyright (C) 1989-2010 Free Software Foundation, Inc.
4*6b445a62SJohn Marino 
5*6b445a62SJohn Marino    This file contains the GNU History Library (History), a set of
6*6b445a62SJohn Marino    routines for managing the text of previously typed lines.
7*6b445a62SJohn Marino 
8*6b445a62SJohn Marino    History is free software: you can redistribute it and/or modify
9*6b445a62SJohn Marino    it under the terms of the GNU General Public License as published by
10*6b445a62SJohn Marino    the Free Software Foundation, either version 3 of the License, or
11*6b445a62SJohn Marino    (at your option) any later version.
12*6b445a62SJohn Marino 
13*6b445a62SJohn Marino    History is distributed in the hope that it will be useful,
14*6b445a62SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
15*6b445a62SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16*6b445a62SJohn Marino    GNU General Public License for more details.
17*6b445a62SJohn Marino 
18*6b445a62SJohn Marino    You should have received a copy of the GNU General Public License
19*6b445a62SJohn Marino    along with History.  If not, see <http://www.gnu.org/licenses/>.
20*6b445a62SJohn Marino */
21*6b445a62SJohn Marino 
22*6b445a62SJohn Marino /* The goal is to make the implementation transparent, so that you
23*6b445a62SJohn Marino    don't have to know what data types are used, just what functions
24*6b445a62SJohn Marino    you can call.  I think I have done that. */
25*6b445a62SJohn Marino 
26*6b445a62SJohn Marino #define READLINE_LIBRARY
27*6b445a62SJohn Marino 
28*6b445a62SJohn Marino #if defined (__TANDEM)
29*6b445a62SJohn Marino #  include <floss.h>
30*6b445a62SJohn Marino #endif
31*6b445a62SJohn Marino 
32*6b445a62SJohn Marino #if defined (HAVE_CONFIG_H)
33*6b445a62SJohn Marino #  include <config.h>
34*6b445a62SJohn Marino #endif
35*6b445a62SJohn Marino 
36*6b445a62SJohn Marino #include <stdio.h>
37*6b445a62SJohn Marino 
38*6b445a62SJohn Marino #include <sys/types.h>
39*6b445a62SJohn Marino #if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H)
40*6b445a62SJohn Marino #  include <sys/file.h>
41*6b445a62SJohn Marino #endif
42*6b445a62SJohn Marino #include "posixstat.h"
43*6b445a62SJohn Marino #include <fcntl.h>
44*6b445a62SJohn Marino 
45*6b445a62SJohn Marino #if defined (HAVE_STDLIB_H)
46*6b445a62SJohn Marino #  include <stdlib.h>
47*6b445a62SJohn Marino #else
48*6b445a62SJohn Marino #  include "ansi_stdlib.h"
49*6b445a62SJohn Marino #endif /* HAVE_STDLIB_H */
50*6b445a62SJohn Marino 
51*6b445a62SJohn Marino #if defined (HAVE_UNISTD_H)
52*6b445a62SJohn Marino #  include <unistd.h>
53*6b445a62SJohn Marino #endif
54*6b445a62SJohn Marino 
55*6b445a62SJohn Marino #include <ctype.h>
56*6b445a62SJohn Marino 
57*6b445a62SJohn Marino #if defined (__EMX__)
58*6b445a62SJohn Marino #  undef HAVE_MMAP
59*6b445a62SJohn Marino #endif
60*6b445a62SJohn Marino 
61*6b445a62SJohn Marino #ifdef HISTORY_USE_MMAP
62*6b445a62SJohn Marino #  include <sys/mman.h>
63*6b445a62SJohn Marino 
64*6b445a62SJohn Marino #  ifdef MAP_FILE
65*6b445a62SJohn Marino #    define MAP_RFLAGS	(MAP_FILE|MAP_PRIVATE)
66*6b445a62SJohn Marino #    define MAP_WFLAGS	(MAP_FILE|MAP_SHARED)
67*6b445a62SJohn Marino #  else
68*6b445a62SJohn Marino #    define MAP_RFLAGS	MAP_PRIVATE
69*6b445a62SJohn Marino #    define MAP_WFLAGS	MAP_SHARED
70*6b445a62SJohn Marino #  endif
71*6b445a62SJohn Marino 
72*6b445a62SJohn Marino #  ifndef MAP_FAILED
73*6b445a62SJohn Marino #    define MAP_FAILED	((void *)-1)
74*6b445a62SJohn Marino #  endif
75*6b445a62SJohn Marino 
76*6b445a62SJohn Marino #endif /* HISTORY_USE_MMAP */
77*6b445a62SJohn Marino 
78*6b445a62SJohn Marino /* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
79*6b445a62SJohn Marino    on win 95/98/nt), we want to open files with O_BINARY mode so that there
80*6b445a62SJohn Marino    is no \n -> \r\n conversion performed.  On other systems, we don't want to
81*6b445a62SJohn Marino    mess around with O_BINARY at all, so we ensure that it's defined to 0. */
82*6b445a62SJohn Marino #if defined (__EMX__) || defined (__CYGWIN__)
83*6b445a62SJohn Marino #  ifndef O_BINARY
84*6b445a62SJohn Marino #    define O_BINARY 0
85*6b445a62SJohn Marino #  endif
86*6b445a62SJohn Marino #else /* !__EMX__ && !__CYGWIN__ */
87*6b445a62SJohn Marino #  undef O_BINARY
88*6b445a62SJohn Marino #  define O_BINARY 0
89*6b445a62SJohn Marino #endif /* !__EMX__ && !__CYGWIN__ */
90*6b445a62SJohn Marino 
91*6b445a62SJohn Marino #include <errno.h>
92*6b445a62SJohn Marino #if !defined (errno)
93*6b445a62SJohn Marino extern int errno;
94*6b445a62SJohn Marino #endif /* !errno */
95*6b445a62SJohn Marino 
96*6b445a62SJohn Marino #include "history.h"
97*6b445a62SJohn Marino #include "histlib.h"
98*6b445a62SJohn Marino 
99*6b445a62SJohn Marino #include "rlshell.h"
100*6b445a62SJohn Marino #include "xmalloc.h"
101*6b445a62SJohn Marino 
102*6b445a62SJohn Marino /* If non-zero, we write timestamps to the history file in history_do_write() */
103*6b445a62SJohn Marino int history_write_timestamps = 0;
104*6b445a62SJohn Marino 
105*6b445a62SJohn Marino /* Does S look like the beginning of a history timestamp entry?  Placeholder
106*6b445a62SJohn Marino    for more extensive tests. */
107*6b445a62SJohn Marino #define HIST_TIMESTAMP_START(s)		(*(s) == history_comment_char && isdigit ((s)[1]) )
108*6b445a62SJohn Marino 
109*6b445a62SJohn Marino /* Return the string that should be used in the place of this
110*6b445a62SJohn Marino    filename.  This only matters when you don't specify the
111*6b445a62SJohn Marino    filename to read_history (), or write_history (). */
112*6b445a62SJohn Marino static char *
history_filename(filename)113*6b445a62SJohn Marino history_filename (filename)
114*6b445a62SJohn Marino      const char *filename;
115*6b445a62SJohn Marino {
116*6b445a62SJohn Marino   char *return_val;
117*6b445a62SJohn Marino   const char *home;
118*6b445a62SJohn Marino   int home_len;
119*6b445a62SJohn Marino 
120*6b445a62SJohn Marino   return_val = filename ? savestring (filename) : (char *)NULL;
121*6b445a62SJohn Marino 
122*6b445a62SJohn Marino   if (return_val)
123*6b445a62SJohn Marino     return (return_val);
124*6b445a62SJohn Marino 
125*6b445a62SJohn Marino   home = sh_get_env_value ("HOME");
126*6b445a62SJohn Marino 
127*6b445a62SJohn Marino   if (home == 0)
128*6b445a62SJohn Marino     {
129*6b445a62SJohn Marino #if 0
130*6b445a62SJohn Marino       home = ".";
131*6b445a62SJohn Marino       home_len = 1;
132*6b445a62SJohn Marino #else
133*6b445a62SJohn Marino       return (NULL);
134*6b445a62SJohn Marino #endif
135*6b445a62SJohn Marino     }
136*6b445a62SJohn Marino   else
137*6b445a62SJohn Marino     home_len = strlen (home);
138*6b445a62SJohn Marino 
139*6b445a62SJohn Marino   return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
140*6b445a62SJohn Marino   strcpy (return_val, home);
141*6b445a62SJohn Marino   return_val[home_len] = '/';
142*6b445a62SJohn Marino #if defined (__MSDOS__)
143*6b445a62SJohn Marino   strcpy (return_val + home_len + 1, "_history");
144*6b445a62SJohn Marino #else
145*6b445a62SJohn Marino   strcpy (return_val + home_len + 1, ".history");
146*6b445a62SJohn Marino #endif
147*6b445a62SJohn Marino 
148*6b445a62SJohn Marino   return (return_val);
149*6b445a62SJohn Marino }
150*6b445a62SJohn Marino 
151*6b445a62SJohn Marino /* Add the contents of FILENAME to the history list, a line at a time.
152*6b445a62SJohn Marino    If FILENAME is NULL, then read from ~/.history.  Returns 0 if
153*6b445a62SJohn Marino    successful, or errno if not. */
154*6b445a62SJohn Marino int
read_history(filename)155*6b445a62SJohn Marino read_history (filename)
156*6b445a62SJohn Marino      const char *filename;
157*6b445a62SJohn Marino {
158*6b445a62SJohn Marino   return (read_history_range (filename, 0, -1));
159*6b445a62SJohn Marino }
160*6b445a62SJohn Marino 
161*6b445a62SJohn Marino /* Read a range of lines from FILENAME, adding them to the history list.
162*6b445a62SJohn Marino    Start reading at the FROM'th line and end at the TO'th.  If FROM
163*6b445a62SJohn Marino    is zero, start at the beginning.  If TO is less than FROM, read
164*6b445a62SJohn Marino    until the end of the file.  If FILENAME is NULL, then read from
165*6b445a62SJohn Marino    ~/.history.  Returns 0 if successful, or errno if not. */
166*6b445a62SJohn Marino int
read_history_range(filename,from,to)167*6b445a62SJohn Marino read_history_range (filename, from, to)
168*6b445a62SJohn Marino      const char *filename;
169*6b445a62SJohn Marino      int from, to;
170*6b445a62SJohn Marino {
171*6b445a62SJohn Marino   register char *line_start, *line_end, *p;
172*6b445a62SJohn Marino   char *input, *buffer, *bufend, *last_ts;
173*6b445a62SJohn Marino   int file, current_line, chars_read;
174*6b445a62SJohn Marino   struct stat finfo;
175*6b445a62SJohn Marino   size_t file_size;
176*6b445a62SJohn Marino #if defined (EFBIG)
177*6b445a62SJohn Marino   int overflow_errno = EFBIG;
178*6b445a62SJohn Marino #elif defined (EOVERFLOW)
179*6b445a62SJohn Marino   int overflow_errno = EOVERFLOW;
180*6b445a62SJohn Marino #else
181*6b445a62SJohn Marino   int overflow_errno = EIO;
182*6b445a62SJohn Marino #endif
183*6b445a62SJohn Marino 
184*6b445a62SJohn Marino   buffer = last_ts = (char *)NULL;
185*6b445a62SJohn Marino   input = history_filename (filename);
186*6b445a62SJohn Marino   file = input ? open (input, O_RDONLY|O_BINARY, 0666) : -1;
187*6b445a62SJohn Marino 
188*6b445a62SJohn Marino   if ((file < 0) || (fstat (file, &finfo) == -1))
189*6b445a62SJohn Marino     goto error_and_exit;
190*6b445a62SJohn Marino 
191*6b445a62SJohn Marino   file_size = (size_t)finfo.st_size;
192*6b445a62SJohn Marino 
193*6b445a62SJohn Marino   /* check for overflow on very large files */
194*6b445a62SJohn Marino   if (file_size != finfo.st_size || file_size + 1 < file_size)
195*6b445a62SJohn Marino     {
196*6b445a62SJohn Marino       errno = overflow_errno;
197*6b445a62SJohn Marino       goto error_and_exit;
198*6b445a62SJohn Marino     }
199*6b445a62SJohn Marino 
200*6b445a62SJohn Marino #ifdef HISTORY_USE_MMAP
201*6b445a62SJohn Marino   /* We map read/write and private so we can change newlines to NULs without
202*6b445a62SJohn Marino      affecting the underlying object. */
203*6b445a62SJohn Marino   buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
204*6b445a62SJohn Marino   if ((void *)buffer == MAP_FAILED)
205*6b445a62SJohn Marino     {
206*6b445a62SJohn Marino       errno = overflow_errno;
207*6b445a62SJohn Marino       goto error_and_exit;
208*6b445a62SJohn Marino     }
209*6b445a62SJohn Marino   chars_read = file_size;
210*6b445a62SJohn Marino #else
211*6b445a62SJohn Marino   buffer = (char *)malloc (file_size + 1);
212*6b445a62SJohn Marino   if (buffer == 0)
213*6b445a62SJohn Marino     {
214*6b445a62SJohn Marino       errno = overflow_errno;
215*6b445a62SJohn Marino       goto error_and_exit;
216*6b445a62SJohn Marino     }
217*6b445a62SJohn Marino 
218*6b445a62SJohn Marino   chars_read = read (file, buffer, file_size);
219*6b445a62SJohn Marino #endif
220*6b445a62SJohn Marino   if (chars_read < 0)
221*6b445a62SJohn Marino     {
222*6b445a62SJohn Marino   error_and_exit:
223*6b445a62SJohn Marino       if (errno != 0)
224*6b445a62SJohn Marino 	chars_read = errno;
225*6b445a62SJohn Marino       else
226*6b445a62SJohn Marino 	chars_read = EIO;
227*6b445a62SJohn Marino       if (file >= 0)
228*6b445a62SJohn Marino 	close (file);
229*6b445a62SJohn Marino 
230*6b445a62SJohn Marino       FREE (input);
231*6b445a62SJohn Marino #ifndef HISTORY_USE_MMAP
232*6b445a62SJohn Marino       FREE (buffer);
233*6b445a62SJohn Marino #endif
234*6b445a62SJohn Marino 
235*6b445a62SJohn Marino       return (chars_read);
236*6b445a62SJohn Marino     }
237*6b445a62SJohn Marino 
238*6b445a62SJohn Marino   close (file);
239*6b445a62SJohn Marino 
240*6b445a62SJohn Marino   /* Set TO to larger than end of file if negative. */
241*6b445a62SJohn Marino   if (to < 0)
242*6b445a62SJohn Marino     to = chars_read;
243*6b445a62SJohn Marino 
244*6b445a62SJohn Marino   /* Start at beginning of file, work to end. */
245*6b445a62SJohn Marino   bufend = buffer + chars_read;
246*6b445a62SJohn Marino   current_line = 0;
247*6b445a62SJohn Marino 
248*6b445a62SJohn Marino   /* Skip lines until we are at FROM. */
249*6b445a62SJohn Marino   for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
250*6b445a62SJohn Marino     if (*line_end == '\n')
251*6b445a62SJohn Marino       {
252*6b445a62SJohn Marino       	p = line_end + 1;
253*6b445a62SJohn Marino       	/* If we see something we think is a timestamp, continue with this
254*6b445a62SJohn Marino 	   line.  We should check more extensively here... */
255*6b445a62SJohn Marino 	if (HIST_TIMESTAMP_START(p) == 0)
256*6b445a62SJohn Marino 	  current_line++;
257*6b445a62SJohn Marino 	line_start = p;
258*6b445a62SJohn Marino       }
259*6b445a62SJohn Marino 
260*6b445a62SJohn Marino   /* If there are lines left to gobble, then gobble them now. */
261*6b445a62SJohn Marino   for (line_end = line_start; line_end < bufend; line_end++)
262*6b445a62SJohn Marino     if (*line_end == '\n')
263*6b445a62SJohn Marino       {
264*6b445a62SJohn Marino 	/* Change to allow Windows-like \r\n end of line delimiter. */
265*6b445a62SJohn Marino 	if (line_end > line_start && line_end[-1] == '\r')
266*6b445a62SJohn Marino 	  line_end[-1] = '\0';
267*6b445a62SJohn Marino 	else
268*6b445a62SJohn Marino 	  *line_end = '\0';
269*6b445a62SJohn Marino 
270*6b445a62SJohn Marino 	if (*line_start)
271*6b445a62SJohn Marino 	  {
272*6b445a62SJohn Marino 	    if (HIST_TIMESTAMP_START(line_start) == 0)
273*6b445a62SJohn Marino 	      {
274*6b445a62SJohn Marino 		add_history (line_start);
275*6b445a62SJohn Marino 		if (last_ts)
276*6b445a62SJohn Marino 		  {
277*6b445a62SJohn Marino 		    add_history_time (last_ts);
278*6b445a62SJohn Marino 		    last_ts = NULL;
279*6b445a62SJohn Marino 		  }
280*6b445a62SJohn Marino 	      }
281*6b445a62SJohn Marino 	    else
282*6b445a62SJohn Marino 	      {
283*6b445a62SJohn Marino 		last_ts = line_start;
284*6b445a62SJohn Marino 		current_line--;
285*6b445a62SJohn Marino 	      }
286*6b445a62SJohn Marino 	  }
287*6b445a62SJohn Marino 
288*6b445a62SJohn Marino 	current_line++;
289*6b445a62SJohn Marino 
290*6b445a62SJohn Marino 	if (current_line >= to)
291*6b445a62SJohn Marino 	  break;
292*6b445a62SJohn Marino 
293*6b445a62SJohn Marino 	line_start = line_end + 1;
294*6b445a62SJohn Marino       }
295*6b445a62SJohn Marino 
296*6b445a62SJohn Marino   FREE (input);
297*6b445a62SJohn Marino #ifndef HISTORY_USE_MMAP
298*6b445a62SJohn Marino   FREE (buffer);
299*6b445a62SJohn Marino #else
300*6b445a62SJohn Marino   munmap (buffer, file_size);
301*6b445a62SJohn Marino #endif
302*6b445a62SJohn Marino 
303*6b445a62SJohn Marino   return (0);
304*6b445a62SJohn Marino }
305*6b445a62SJohn Marino 
306*6b445a62SJohn Marino /* Truncate the history file FNAME, leaving only LINES trailing lines.
307*6b445a62SJohn Marino    If FNAME is NULL, then use ~/.history.  Returns 0 on success, errno
308*6b445a62SJohn Marino    on failure. */
309*6b445a62SJohn Marino int
history_truncate_file(fname,lines)310*6b445a62SJohn Marino history_truncate_file (fname, lines)
311*6b445a62SJohn Marino      const char *fname;
312*6b445a62SJohn Marino      int lines;
313*6b445a62SJohn Marino {
314*6b445a62SJohn Marino   char *buffer, *filename, *bp, *bp1;		/* bp1 == bp+1 */
315*6b445a62SJohn Marino   int file, chars_read, rv;
316*6b445a62SJohn Marino   struct stat finfo;
317*6b445a62SJohn Marino   size_t file_size;
318*6b445a62SJohn Marino 
319*6b445a62SJohn Marino   buffer = (char *)NULL;
320*6b445a62SJohn Marino   filename = history_filename (fname);
321*6b445a62SJohn Marino   file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
322*6b445a62SJohn Marino   rv = 0;
323*6b445a62SJohn Marino 
324*6b445a62SJohn Marino   /* Don't try to truncate non-regular files. */
325*6b445a62SJohn Marino   if (file == -1 || fstat (file, &finfo) == -1)
326*6b445a62SJohn Marino     {
327*6b445a62SJohn Marino       rv = errno;
328*6b445a62SJohn Marino       if (file != -1)
329*6b445a62SJohn Marino 	close (file);
330*6b445a62SJohn Marino       goto truncate_exit;
331*6b445a62SJohn Marino     }
332*6b445a62SJohn Marino 
333*6b445a62SJohn Marino   if (S_ISREG (finfo.st_mode) == 0)
334*6b445a62SJohn Marino     {
335*6b445a62SJohn Marino       close (file);
336*6b445a62SJohn Marino #ifdef EFTYPE
337*6b445a62SJohn Marino       rv = EFTYPE;
338*6b445a62SJohn Marino #else
339*6b445a62SJohn Marino       rv = EINVAL;
340*6b445a62SJohn Marino #endif
341*6b445a62SJohn Marino       goto truncate_exit;
342*6b445a62SJohn Marino     }
343*6b445a62SJohn Marino 
344*6b445a62SJohn Marino   file_size = (size_t)finfo.st_size;
345*6b445a62SJohn Marino 
346*6b445a62SJohn Marino   /* check for overflow on very large files */
347*6b445a62SJohn Marino   if (file_size != finfo.st_size || file_size + 1 < file_size)
348*6b445a62SJohn Marino     {
349*6b445a62SJohn Marino       close (file);
350*6b445a62SJohn Marino #if defined (EFBIG)
351*6b445a62SJohn Marino       rv = errno = EFBIG;
352*6b445a62SJohn Marino #elif defined (EOVERFLOW)
353*6b445a62SJohn Marino       rv = errno = EOVERFLOW;
354*6b445a62SJohn Marino #else
355*6b445a62SJohn Marino       rv = errno = EINVAL;
356*6b445a62SJohn Marino #endif
357*6b445a62SJohn Marino       goto truncate_exit;
358*6b445a62SJohn Marino     }
359*6b445a62SJohn Marino 
360*6b445a62SJohn Marino   buffer = (char *)malloc (file_size + 1);
361*6b445a62SJohn Marino   if (buffer == 0)
362*6b445a62SJohn Marino     {
363*6b445a62SJohn Marino       close (file);
364*6b445a62SJohn Marino       goto truncate_exit;
365*6b445a62SJohn Marino     }
366*6b445a62SJohn Marino 
367*6b445a62SJohn Marino   chars_read = read (file, buffer, file_size);
368*6b445a62SJohn Marino   close (file);
369*6b445a62SJohn Marino 
370*6b445a62SJohn Marino   if (chars_read <= 0)
371*6b445a62SJohn Marino     {
372*6b445a62SJohn Marino       rv = (chars_read < 0) ? errno : 0;
373*6b445a62SJohn Marino       goto truncate_exit;
374*6b445a62SJohn Marino     }
375*6b445a62SJohn Marino 
376*6b445a62SJohn Marino   /* Count backwards from the end of buffer until we have passed
377*6b445a62SJohn Marino      LINES lines.  bp1 is set funny initially.  But since bp[1] can't
378*6b445a62SJohn Marino      be a comment character (since it's off the end) and *bp can't be
379*6b445a62SJohn Marino      both a newline and the history comment character, it should be OK. */
380*6b445a62SJohn Marino   for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
381*6b445a62SJohn Marino     {
382*6b445a62SJohn Marino       if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
383*6b445a62SJohn Marino 	lines--;
384*6b445a62SJohn Marino       bp1 = bp;
385*6b445a62SJohn Marino     }
386*6b445a62SJohn Marino 
387*6b445a62SJohn Marino   /* If this is the first line, then the file contains exactly the
388*6b445a62SJohn Marino      number of lines we want to truncate to, so we don't need to do
389*6b445a62SJohn Marino      anything.  It's the first line if we don't find a newline between
390*6b445a62SJohn Marino      the current value of i and 0.  Otherwise, write from the start of
391*6b445a62SJohn Marino      this line until the end of the buffer. */
392*6b445a62SJohn Marino   for ( ; bp > buffer; bp--)
393*6b445a62SJohn Marino     {
394*6b445a62SJohn Marino       if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
395*6b445a62SJohn Marino         {
396*6b445a62SJohn Marino 	  bp++;
397*6b445a62SJohn Marino 	  break;
398*6b445a62SJohn Marino         }
399*6b445a62SJohn Marino       bp1 = bp;
400*6b445a62SJohn Marino     }
401*6b445a62SJohn Marino 
402*6b445a62SJohn Marino   /* Write only if there are more lines in the file than we want to
403*6b445a62SJohn Marino      truncate to. */
404*6b445a62SJohn Marino   if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
405*6b445a62SJohn Marino     {
406*6b445a62SJohn Marino       write (file, bp, chars_read - (bp - buffer));
407*6b445a62SJohn Marino 
408*6b445a62SJohn Marino #if defined (__BEOS__)
409*6b445a62SJohn Marino       /* BeOS ignores O_TRUNC. */
410*6b445a62SJohn Marino       ftruncate (file, chars_read - (bp - buffer));
411*6b445a62SJohn Marino #endif
412*6b445a62SJohn Marino 
413*6b445a62SJohn Marino       close (file);
414*6b445a62SJohn Marino     }
415*6b445a62SJohn Marino 
416*6b445a62SJohn Marino  truncate_exit:
417*6b445a62SJohn Marino 
418*6b445a62SJohn Marino   FREE (buffer);
419*6b445a62SJohn Marino 
420*6b445a62SJohn Marino   xfree (filename);
421*6b445a62SJohn Marino   return rv;
422*6b445a62SJohn Marino }
423*6b445a62SJohn Marino 
424*6b445a62SJohn Marino /* Workhorse function for writing history.  Writes NELEMENT entries
425*6b445a62SJohn Marino    from the history list to FILENAME.  OVERWRITE is non-zero if you
426*6b445a62SJohn Marino    wish to replace FILENAME with the entries. */
427*6b445a62SJohn Marino static int
history_do_write(filename,nelements,overwrite)428*6b445a62SJohn Marino history_do_write (filename, nelements, overwrite)
429*6b445a62SJohn Marino      const char *filename;
430*6b445a62SJohn Marino      int nelements, overwrite;
431*6b445a62SJohn Marino {
432*6b445a62SJohn Marino   register int i;
433*6b445a62SJohn Marino   char *output;
434*6b445a62SJohn Marino   int file, mode, rv;
435*6b445a62SJohn Marino #ifdef HISTORY_USE_MMAP
436*6b445a62SJohn Marino   size_t cursize;
437*6b445a62SJohn Marino 
438*6b445a62SJohn Marino   mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
439*6b445a62SJohn Marino #else
440*6b445a62SJohn Marino   mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
441*6b445a62SJohn Marino #endif
442*6b445a62SJohn Marino   output = history_filename (filename);
443*6b445a62SJohn Marino   file = output ? open (output, mode, 0600) : -1;
444*6b445a62SJohn Marino   rv = 0;
445*6b445a62SJohn Marino 
446*6b445a62SJohn Marino   if (file == -1)
447*6b445a62SJohn Marino     {
448*6b445a62SJohn Marino       FREE (output);
449*6b445a62SJohn Marino       return (errno);
450*6b445a62SJohn Marino     }
451*6b445a62SJohn Marino 
452*6b445a62SJohn Marino #ifdef HISTORY_USE_MMAP
453*6b445a62SJohn Marino   cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
454*6b445a62SJohn Marino #endif
455*6b445a62SJohn Marino 
456*6b445a62SJohn Marino   if (nelements > history_length)
457*6b445a62SJohn Marino     nelements = history_length;
458*6b445a62SJohn Marino 
459*6b445a62SJohn Marino   /* Build a buffer of all the lines to write, and write them in one syscall.
460*6b445a62SJohn Marino      Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
461*6b445a62SJohn Marino   {
462*6b445a62SJohn Marino     HIST_ENTRY **the_history;	/* local */
463*6b445a62SJohn Marino     register int j;
464*6b445a62SJohn Marino     int buffer_size;
465*6b445a62SJohn Marino     char *buffer;
466*6b445a62SJohn Marino 
467*6b445a62SJohn Marino     the_history = history_list ();
468*6b445a62SJohn Marino     /* Calculate the total number of bytes to write. */
469*6b445a62SJohn Marino     for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
470*6b445a62SJohn Marino #if 0
471*6b445a62SJohn Marino       buffer_size += 2 + HISTENT_BYTES (the_history[i]);
472*6b445a62SJohn Marino #else
473*6b445a62SJohn Marino       {
474*6b445a62SJohn Marino 	if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
475*6b445a62SJohn Marino 	  buffer_size += strlen (the_history[i]->timestamp) + 1;
476*6b445a62SJohn Marino 	buffer_size += strlen (the_history[i]->line) + 1;
477*6b445a62SJohn Marino       }
478*6b445a62SJohn Marino #endif
479*6b445a62SJohn Marino 
480*6b445a62SJohn Marino     /* Allocate the buffer, and fill it. */
481*6b445a62SJohn Marino #ifdef HISTORY_USE_MMAP
482*6b445a62SJohn Marino     if (ftruncate (file, buffer_size+cursize) == -1)
483*6b445a62SJohn Marino       goto mmap_error;
484*6b445a62SJohn Marino     buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
485*6b445a62SJohn Marino     if ((void *)buffer == MAP_FAILED)
486*6b445a62SJohn Marino       {
487*6b445a62SJohn Marino mmap_error:
488*6b445a62SJohn Marino 	rv = errno;
489*6b445a62SJohn Marino 	FREE (output);
490*6b445a62SJohn Marino 	close (file);
491*6b445a62SJohn Marino 	return rv;
492*6b445a62SJohn Marino       }
493*6b445a62SJohn Marino #else
494*6b445a62SJohn Marino     buffer = (char *)malloc (buffer_size);
495*6b445a62SJohn Marino     if (buffer == 0)
496*6b445a62SJohn Marino       {
497*6b445a62SJohn Marino       	rv = errno;
498*6b445a62SJohn Marino 	FREE (output);
499*6b445a62SJohn Marino 	close (file);
500*6b445a62SJohn Marino 	return rv;
501*6b445a62SJohn Marino       }
502*6b445a62SJohn Marino #endif
503*6b445a62SJohn Marino 
504*6b445a62SJohn Marino     for (j = 0, i = history_length - nelements; i < history_length; i++)
505*6b445a62SJohn Marino       {
506*6b445a62SJohn Marino 	if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
507*6b445a62SJohn Marino 	  {
508*6b445a62SJohn Marino 	    strcpy (buffer + j, the_history[i]->timestamp);
509*6b445a62SJohn Marino 	    j += strlen (the_history[i]->timestamp);
510*6b445a62SJohn Marino 	    buffer[j++] = '\n';
511*6b445a62SJohn Marino 	  }
512*6b445a62SJohn Marino 	strcpy (buffer + j, the_history[i]->line);
513*6b445a62SJohn Marino 	j += strlen (the_history[i]->line);
514*6b445a62SJohn Marino 	buffer[j++] = '\n';
515*6b445a62SJohn Marino       }
516*6b445a62SJohn Marino 
517*6b445a62SJohn Marino #ifdef HISTORY_USE_MMAP
518*6b445a62SJohn Marino     if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0)
519*6b445a62SJohn Marino       rv = errno;
520*6b445a62SJohn Marino #else
521*6b445a62SJohn Marino     if (write (file, buffer, buffer_size) < 0)
522*6b445a62SJohn Marino       rv = errno;
523*6b445a62SJohn Marino     xfree (buffer);
524*6b445a62SJohn Marino #endif
525*6b445a62SJohn Marino   }
526*6b445a62SJohn Marino 
527*6b445a62SJohn Marino   close (file);
528*6b445a62SJohn Marino 
529*6b445a62SJohn Marino   FREE (output);
530*6b445a62SJohn Marino 
531*6b445a62SJohn Marino   return (rv);
532*6b445a62SJohn Marino }
533*6b445a62SJohn Marino 
534*6b445a62SJohn Marino /* Append NELEMENT entries to FILENAME.  The entries appended are from
535*6b445a62SJohn Marino    the end of the list minus NELEMENTs up to the end of the list. */
536*6b445a62SJohn Marino int
append_history(nelements,filename)537*6b445a62SJohn Marino append_history (nelements, filename)
538*6b445a62SJohn Marino      int nelements;
539*6b445a62SJohn Marino      const char *filename;
540*6b445a62SJohn Marino {
541*6b445a62SJohn Marino   return (history_do_write (filename, nelements, HISTORY_APPEND));
542*6b445a62SJohn Marino }
543*6b445a62SJohn Marino 
544*6b445a62SJohn Marino /* Overwrite FILENAME with the current history.  If FILENAME is NULL,
545*6b445a62SJohn Marino    then write the history list to ~/.history.  Values returned
546*6b445a62SJohn Marino    are as in read_history ().*/
547*6b445a62SJohn Marino int
write_history(filename)548*6b445a62SJohn Marino write_history (filename)
549*6b445a62SJohn Marino      const char *filename;
550*6b445a62SJohn Marino {
551*6b445a62SJohn Marino   return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
552*6b445a62SJohn Marino }
553