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