blob: 845c2435654c1b5faa6b630c2eba8823b7f5f14f [file] [log] [blame]
Jari Aaltoccc6cda1996-12-23 17:02:34 +00001/* histfile.c - functions to manipulate the history file. */
2
Chet Ramey495aee42011-11-22 19:11:26 -05003/* Copyright (C) 1989-2010 Free Software Foundation, Inc.
Jari Aaltoccc6cda1996-12-23 17:02:34 +00004
Jari Aalto31859422009-01-12 13:36:28 +00005 This file contains the GNU History Library (History), a set of
Jari Aaltoccc6cda1996-12-23 17:02:34 +00006 routines for managing the text of previously typed lines.
7
Jari Aalto31859422009-01-12 13:36:28 +00008 History is free software: you can redistribute it and/or modify
Jari Aaltoccc6cda1996-12-23 17:02:34 +00009 it under the terms of the GNU General Public License as published by
Jari Aalto31859422009-01-12 13:36:28 +000010 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
Jari Aaltoccc6cda1996-12-23 17:02:34 +000012
Jari Aalto31859422009-01-12 13:36:28 +000013 History is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
Jari Aaltoccc6cda1996-12-23 17:02:34 +000017
Jari Aalto31859422009-01-12 13:36:28 +000018 You should have received a copy of the GNU General Public License
19 along with History. If not, see <http://www.gnu.org/licenses/>.
20*/
Jari Aaltoccc6cda1996-12-23 17:02:34 +000021
22/* The goal is to make the implementation transparent, so that you
23 don't have to know what data types are used, just what functions
24 you can call. I think I have done that. */
Jari Aaltob80f6442004-07-27 13:29:18 +000025
Jari Aaltoccc6cda1996-12-23 17:02:34 +000026#define READLINE_LIBRARY
27
Jari Aaltob80f6442004-07-27 13:29:18 +000028#if defined (__TANDEM)
29# include <floss.h>
30#endif
31
Jari Aaltoccc6cda1996-12-23 17:02:34 +000032#if defined (HAVE_CONFIG_H)
33# include <config.h>
34#endif
35
36#include <stdio.h>
37
38#include <sys/types.h>
Jari Aaltob80f6442004-07-27 13:29:18 +000039#if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H)
Jari Aaltocce855b1998-04-17 19:52:44 +000040# include <sys/file.h>
41#endif
Jari Aaltobb706242000-03-17 21:46:59 +000042#include "posixstat.h"
Jari Aaltoccc6cda1996-12-23 17:02:34 +000043#include <fcntl.h>
44
45#if defined (HAVE_STDLIB_H)
46# include <stdlib.h>
47#else
48# include "ansi_stdlib.h"
49#endif /* HAVE_STDLIB_H */
50
51#if defined (HAVE_UNISTD_H)
52# include <unistd.h>
53#endif
54
Jari Aalto31859422009-01-12 13:36:28 +000055#include <ctype.h>
56
57#if defined (__EMX__)
Jari Aalto7117c2d2002-07-17 14:10:11 +000058# undef HAVE_MMAP
59#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +000060
Jari Aaltob80f6442004-07-27 13:29:18 +000061#ifdef HISTORY_USE_MMAP
Jari Aalto7117c2d2002-07-17 14:10:11 +000062# include <sys/mman.h>
63
64# ifdef MAP_FILE
65# define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE)
66# define MAP_WFLAGS (MAP_FILE|MAP_SHARED)
67# else
68# define MAP_RFLAGS MAP_PRIVATE
69# define MAP_WFLAGS MAP_SHARED
70# endif
71
72# ifndef MAP_FAILED
73# define MAP_FAILED ((void *)-1)
74# endif
75
Jari Aaltob80f6442004-07-27 13:29:18 +000076#endif /* HISTORY_USE_MMAP */
Jari Aaltobb706242000-03-17 21:46:59 +000077
78/* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
79 on win 95/98/nt), we want to open files with O_BINARY mode so that there
80 is no \n -> \r\n conversion performed. On other systems, we don't want to
81 mess around with O_BINARY at all, so we ensure that it's defined to 0. */
82#if defined (__EMX__) || defined (__CYGWIN__)
Jari Aaltod166f041997-06-05 14:59:13 +000083# ifndef O_BINARY
84# define O_BINARY 0
85# endif
Jari Aaltobb706242000-03-17 21:46:59 +000086#else /* !__EMX__ && !__CYGWIN__ */
Jari Aaltod166f041997-06-05 14:59:13 +000087# undef O_BINARY
88# define O_BINARY 0
Jari Aaltobb706242000-03-17 21:46:59 +000089#endif /* !__EMX__ && !__CYGWIN__ */
Jari Aaltod166f041997-06-05 14:59:13 +000090
Jari Aaltoccc6cda1996-12-23 17:02:34 +000091#include <errno.h>
92#if !defined (errno)
Ricardo Cerqueiraa02fbff2013-07-25 22:35:34 +010093#include <errno.h>
Jari Aaltoccc6cda1996-12-23 17:02:34 +000094#endif /* !errno */
95
96#include "history.h"
97#include "histlib.h"
98
Jari Aaltobb706242000-03-17 21:46:59 +000099#include "rlshell.h"
100#include "xmalloc.h"
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000101
Jari Aaltob80f6442004-07-27 13:29:18 +0000102/* If non-zero, we write timestamps to the history file in history_do_write() */
103int history_write_timestamps = 0;
104
105/* Does S look like the beginning of a history timestamp entry? Placeholder
106 for more extensive tests. */
Jari Aalto31859422009-01-12 13:36:28 +0000107#define HIST_TIMESTAMP_START(s) (*(s) == history_comment_char && isdigit ((s)[1]) )
Jari Aaltob80f6442004-07-27 13:29:18 +0000108
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000109/* Return the string that should be used in the place of this
110 filename. This only matters when you don't specify the
111 filename to read_history (), or write_history (). */
112static char *
113history_filename (filename)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000114 const char *filename;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000115{
Jari Aalto28ef6c32001-04-06 19:14:31 +0000116 char *return_val;
117 const char *home;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000118 int home_len;
119
120 return_val = filename ? savestring (filename) : (char *)NULL;
121
122 if (return_val)
123 return (return_val);
124
Jari Aalto28ef6c32001-04-06 19:14:31 +0000125 home = sh_get_env_value ("HOME");
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000126
127 if (home == 0)
Chet Rameyac50fba2014-02-26 09:36:43 -0500128 return (NULL);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000129 else
130 home_len = strlen (home);
131
Jari Aaltof73dda02001-11-13 17:56:06 +0000132 return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000133 strcpy (return_val, home);
134 return_val[home_len] = '/';
Jari Aaltobb706242000-03-17 21:46:59 +0000135#if defined (__MSDOS__)
136 strcpy (return_val + home_len + 1, "_history");
137#else
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000138 strcpy (return_val + home_len + 1, ".history");
Jari Aaltobb706242000-03-17 21:46:59 +0000139#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000140
141 return (return_val);
142}
143
Chet Rameyac50fba2014-02-26 09:36:43 -0500144static char *
145history_backupfile (filename)
146 const char *filename;
147{
148 char *ret;
149 size_t len;
150
151 len = strlen (filename);
152 ret = xmalloc (len + 2);
153 strcpy (ret, filename);
154 ret[len] = '-';
155 ret[len+1] = '\0';
156 return ret;
157}
158
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000159/* Add the contents of FILENAME to the history list, a line at a time.
160 If FILENAME is NULL, then read from ~/.history. Returns 0 if
161 successful, or errno if not. */
162int
163read_history (filename)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000164 const char *filename;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000165{
166 return (read_history_range (filename, 0, -1));
167}
168
169/* Read a range of lines from FILENAME, adding them to the history list.
170 Start reading at the FROM'th line and end at the TO'th. If FROM
171 is zero, start at the beginning. If TO is less than FROM, read
172 until the end of the file. If FILENAME is NULL, then read from
173 ~/.history. Returns 0 if successful, or errno if not. */
174int
175read_history_range (filename, from, to)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000176 const char *filename;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000177 int from, to;
178{
Jari Aaltob80f6442004-07-27 13:29:18 +0000179 register char *line_start, *line_end, *p;
180 char *input, *buffer, *bufend, *last_ts;
Jari Aaltobb706242000-03-17 21:46:59 +0000181 int file, current_line, chars_read;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000182 struct stat finfo;
Jari Aaltocce855b1998-04-17 19:52:44 +0000183 size_t file_size;
Jari Aaltob80f6442004-07-27 13:29:18 +0000184#if defined (EFBIG)
185 int overflow_errno = EFBIG;
186#elif defined (EOVERFLOW)
187 int overflow_errno = EOVERFLOW;
188#else
189 int overflow_errno = EIO;
190#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000191
Jari Aaltob80f6442004-07-27 13:29:18 +0000192 buffer = last_ts = (char *)NULL;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000193 input = history_filename (filename);
Chet Ramey495aee42011-11-22 19:11:26 -0500194 file = input ? open (input, O_RDONLY|O_BINARY, 0666) : -1;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000195
196 if ((file < 0) || (fstat (file, &finfo) == -1))
197 goto error_and_exit;
198
Jari Aaltocce855b1998-04-17 19:52:44 +0000199 file_size = (size_t)finfo.st_size;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000200
Jari Aaltocce855b1998-04-17 19:52:44 +0000201 /* check for overflow on very large files */
202 if (file_size != finfo.st_size || file_size + 1 < file_size)
203 {
Jari Aaltob80f6442004-07-27 13:29:18 +0000204 errno = overflow_errno;
Jari Aaltocce855b1998-04-17 19:52:44 +0000205 goto error_and_exit;
206 }
207
Jari Aaltob80f6442004-07-27 13:29:18 +0000208#ifdef HISTORY_USE_MMAP
Jari Aalto7117c2d2002-07-17 14:10:11 +0000209 /* We map read/write and private so we can change newlines to NULs without
210 affecting the underlying object. */
211 buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
212 if ((void *)buffer == MAP_FAILED)
Jari Aaltob80f6442004-07-27 13:29:18 +0000213 {
214 errno = overflow_errno;
215 goto error_and_exit;
216 }
Jari Aalto7117c2d2002-07-17 14:10:11 +0000217 chars_read = file_size;
218#else
219 buffer = (char *)malloc (file_size + 1);
220 if (buffer == 0)
Jari Aaltob80f6442004-07-27 13:29:18 +0000221 {
222 errno = overflow_errno;
223 goto error_and_exit;
224 }
Jari Aaltobb706242000-03-17 21:46:59 +0000225
226 chars_read = read (file, buffer, file_size);
Jari Aalto7117c2d2002-07-17 14:10:11 +0000227#endif
Jari Aaltobb706242000-03-17 21:46:59 +0000228 if (chars_read < 0)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000229 {
230 error_and_exit:
Jari Aaltob80f6442004-07-27 13:29:18 +0000231 if (errno != 0)
232 chars_read = errno;
233 else
234 chars_read = EIO;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000235 if (file >= 0)
236 close (file);
237
238 FREE (input);
Jari Aaltob80f6442004-07-27 13:29:18 +0000239#ifndef HISTORY_USE_MMAP
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000240 FREE (buffer);
Jari Aalto7117c2d2002-07-17 14:10:11 +0000241#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000242
Jari Aalto7117c2d2002-07-17 14:10:11 +0000243 return (chars_read);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000244 }
245
246 close (file);
247
248 /* Set TO to larger than end of file if negative. */
249 if (to < 0)
Jari Aaltobb706242000-03-17 21:46:59 +0000250 to = chars_read;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000251
252 /* Start at beginning of file, work to end. */
Jari Aalto7117c2d2002-07-17 14:10:11 +0000253 bufend = buffer + chars_read;
254 current_line = 0;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000255
256 /* Skip lines until we are at FROM. */
Jari Aalto7117c2d2002-07-17 14:10:11 +0000257 for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
258 if (*line_end == '\n')
259 {
Jari Aaltob80f6442004-07-27 13:29:18 +0000260 p = line_end + 1;
261 /* If we see something we think is a timestamp, continue with this
262 line. We should check more extensively here... */
263 if (HIST_TIMESTAMP_START(p) == 0)
264 current_line++;
265 line_start = p;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000266 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000267
268 /* If there are lines left to gobble, then gobble them now. */
Jari Aalto7117c2d2002-07-17 14:10:11 +0000269 for (line_end = line_start; line_end < bufend; line_end++)
270 if (*line_end == '\n')
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000271 {
Jari Aalto06285672006-10-10 14:15:34 +0000272 /* Change to allow Windows-like \r\n end of line delimiter. */
273 if (line_end > line_start && line_end[-1] == '\r')
274 line_end[-1] = '\0';
275 else
276 *line_end = '\0';
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000277
Jari Aalto7117c2d2002-07-17 14:10:11 +0000278 if (*line_start)
Jari Aaltob80f6442004-07-27 13:29:18 +0000279 {
280 if (HIST_TIMESTAMP_START(line_start) == 0)
281 {
282 add_history (line_start);
283 if (last_ts)
284 {
285 add_history_time (last_ts);
286 last_ts = NULL;
287 }
288 }
289 else
290 {
291 last_ts = line_start;
292 current_line--;
293 }
294 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000295
296 current_line++;
297
298 if (current_line >= to)
299 break;
300
301 line_start = line_end + 1;
302 }
303
304 FREE (input);
Jari Aaltob80f6442004-07-27 13:29:18 +0000305#ifndef HISTORY_USE_MMAP
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000306 FREE (buffer);
Jari Aalto7117c2d2002-07-17 14:10:11 +0000307#else
308 munmap (buffer, file_size);
309#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000310
311 return (0);
312}
313
314/* Truncate the history file FNAME, leaving only LINES trailing lines.
Jari Aalto28ef6c32001-04-06 19:14:31 +0000315 If FNAME is NULL, then use ~/.history. Returns 0 on success, errno
316 on failure. */
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000317int
318history_truncate_file (fname, lines)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000319 const char *fname;
Jari Aaltob72432f1999-02-19 17:11:39 +0000320 int lines;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000321{
Jari Aaltob80f6442004-07-27 13:29:18 +0000322 char *buffer, *filename, *bp, *bp1; /* bp1 == bp+1 */
Jari Aalto28ef6c32001-04-06 19:14:31 +0000323 int file, chars_read, rv;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000324 struct stat finfo;
Jari Aaltocce855b1998-04-17 19:52:44 +0000325 size_t file_size;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000326
Jari Aaltod166f041997-06-05 14:59:13 +0000327 buffer = (char *)NULL;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000328 filename = history_filename (fname);
Chet Ramey495aee42011-11-22 19:11:26 -0500329 file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000330 rv = 0;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000331
Jari Aaltobb706242000-03-17 21:46:59 +0000332 /* Don't try to truncate non-regular files. */
Jari Aalto28ef6c32001-04-06 19:14:31 +0000333 if (file == -1 || fstat (file, &finfo) == -1)
334 {
335 rv = errno;
336 if (file != -1)
337 close (file);
338 goto truncate_exit;
339 }
340
341 if (S_ISREG (finfo.st_mode) == 0)
342 {
343 close (file);
344#ifdef EFTYPE
345 rv = EFTYPE;
346#else
347 rv = EINVAL;
348#endif
349 goto truncate_exit;
350 }
Jari Aaltobb706242000-03-17 21:46:59 +0000351
Jari Aaltocce855b1998-04-17 19:52:44 +0000352 file_size = (size_t)finfo.st_size;
353
354 /* check for overflow on very large files */
355 if (file_size != finfo.st_size || file_size + 1 < file_size)
356 {
357 close (file);
358#if defined (EFBIG)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000359 rv = errno = EFBIG;
360#elif defined (EOVERFLOW)
361 rv = errno = EOVERFLOW;
362#else
363 rv = errno = EINVAL;
Jari Aaltocce855b1998-04-17 19:52:44 +0000364#endif
365 goto truncate_exit;
366 }
367
Jari Aalto7117c2d2002-07-17 14:10:11 +0000368 buffer = (char *)malloc (file_size + 1);
369 if (buffer == 0)
370 {
371 close (file);
372 goto truncate_exit;
373 }
374
Jari Aaltocce855b1998-04-17 19:52:44 +0000375 chars_read = read (file, buffer, file_size);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000376 close (file);
377
378 if (chars_read <= 0)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000379 {
380 rv = (chars_read < 0) ? errno : 0;
381 goto truncate_exit;
382 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000383
384 /* Count backwards from the end of buffer until we have passed
Jari Aaltob80f6442004-07-27 13:29:18 +0000385 LINES lines. bp1 is set funny initially. But since bp[1] can't
386 be a comment character (since it's off the end) and *bp can't be
387 both a newline and the history comment character, it should be OK. */
388 for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000389 {
Jari Aaltob80f6442004-07-27 13:29:18 +0000390 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000391 lines--;
Jari Aaltob80f6442004-07-27 13:29:18 +0000392 bp1 = bp;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000393 }
394
395 /* If this is the first line, then the file contains exactly the
396 number of lines we want to truncate to, so we don't need to do
397 anything. It's the first line if we don't find a newline between
398 the current value of i and 0. Otherwise, write from the start of
399 this line until the end of the buffer. */
Jari Aalto7117c2d2002-07-17 14:10:11 +0000400 for ( ; bp > buffer; bp--)
Jari Aaltob80f6442004-07-27 13:29:18 +0000401 {
402 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
403 {
404 bp++;
405 break;
406 }
407 bp1 = bp;
408 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000409
410 /* Write only if there are more lines in the file than we want to
411 truncate to. */
Jari Aalto7117c2d2002-07-17 14:10:11 +0000412 if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000413 {
Chet Rameyac50fba2014-02-26 09:36:43 -0500414 if (write (file, bp, chars_read - (bp - buffer)) < 0)
415 rv = errno;
Jari Aaltob72432f1999-02-19 17:11:39 +0000416
417#if defined (__BEOS__)
418 /* BeOS ignores O_TRUNC. */
Jari Aalto7117c2d2002-07-17 14:10:11 +0000419 ftruncate (file, chars_read - (bp - buffer));
Jari Aaltob72432f1999-02-19 17:11:39 +0000420#endif
421
Chet Rameyac50fba2014-02-26 09:36:43 -0500422 if (close (file) < 0 && rv == 0)
423 rv = errno;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000424 }
425
426 truncate_exit:
427
428 FREE (buffer);
429
Chet Ramey495aee42011-11-22 19:11:26 -0500430 xfree (filename);
Jari Aalto28ef6c32001-04-06 19:14:31 +0000431 return rv;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000432}
433
434/* Workhorse function for writing history. Writes NELEMENT entries
435 from the history list to FILENAME. OVERWRITE is non-zero if you
436 wish to replace FILENAME with the entries. */
437static int
438history_do_write (filename, nelements, overwrite)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000439 const char *filename;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000440 int nelements, overwrite;
441{
442 register int i;
Chet Rameyac50fba2014-02-26 09:36:43 -0500443 char *output, *bakname;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000444 int file, mode, rv;
Jari Aaltob80f6442004-07-27 13:29:18 +0000445#ifdef HISTORY_USE_MMAP
Jari Aalto7117c2d2002-07-17 14:10:11 +0000446 size_t cursize;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000447
Jari Aalto7117c2d2002-07-17 14:10:11 +0000448 mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
449#else
Jari Aaltod166f041997-06-05 14:59:13 +0000450 mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000451#endif
Jari Aaltod166f041997-06-05 14:59:13 +0000452 output = history_filename (filename);
Chet Rameyac50fba2014-02-26 09:36:43 -0500453 bakname = (overwrite && output) ? history_backupfile (output) : 0;
454
455 if (output && bakname)
456 rename (output, bakname);
457
Chet Ramey495aee42011-11-22 19:11:26 -0500458 file = output ? open (output, mode, 0600) : -1;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000459 rv = 0;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000460
Chet Ramey495aee42011-11-22 19:11:26 -0500461 if (file == -1)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000462 {
Chet Rameyac50fba2014-02-26 09:36:43 -0500463 rv = errno;
464 if (output && bakname)
465 rename (bakname, output);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000466 FREE (output);
Chet Rameyac50fba2014-02-26 09:36:43 -0500467 FREE (bakname);
468 return (rv);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000469 }
470
Jari Aaltob80f6442004-07-27 13:29:18 +0000471#ifdef HISTORY_USE_MMAP
Jari Aalto7117c2d2002-07-17 14:10:11 +0000472 cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
473#endif
474
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000475 if (nelements > history_length)
476 nelements = history_length;
477
478 /* Build a buffer of all the lines to write, and write them in one syscall.
479 Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
480 {
481 HIST_ENTRY **the_history; /* local */
482 register int j;
483 int buffer_size;
484 char *buffer;
485
486 the_history = history_list ();
487 /* Calculate the total number of bytes to write. */
488 for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
Jari Aaltob80f6442004-07-27 13:29:18 +0000489#if 0
490 buffer_size += 2 + HISTENT_BYTES (the_history[i]);
491#else
492 {
493 if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
494 buffer_size += strlen (the_history[i]->timestamp) + 1;
495 buffer_size += strlen (the_history[i]->line) + 1;
496 }
497#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000498
499 /* Allocate the buffer, and fill it. */
Jari Aaltob80f6442004-07-27 13:29:18 +0000500#ifdef HISTORY_USE_MMAP
Jari Aalto7117c2d2002-07-17 14:10:11 +0000501 if (ftruncate (file, buffer_size+cursize) == -1)
502 goto mmap_error;
503 buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
504 if ((void *)buffer == MAP_FAILED)
505 {
506mmap_error:
507 rv = errno;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000508 close (file);
Chet Rameyac50fba2014-02-26 09:36:43 -0500509 if (output && bakname)
510 rename (bakname, output);
511 FREE (output);
512 FREE (bakname);
Jari Aalto7117c2d2002-07-17 14:10:11 +0000513 return rv;
514 }
515#else
516 buffer = (char *)malloc (buffer_size);
517 if (buffer == 0)
518 {
519 rv = errno;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000520 close (file);
Chet Rameyac50fba2014-02-26 09:36:43 -0500521 if (output && bakname)
522 rename (bakname, output);
523 FREE (output);
524 FREE (bakname);
Jari Aalto7117c2d2002-07-17 14:10:11 +0000525 return rv;
526 }
527#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000528
529 for (j = 0, i = history_length - nelements; i < history_length; i++)
530 {
Jari Aaltob80f6442004-07-27 13:29:18 +0000531 if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
532 {
533 strcpy (buffer + j, the_history[i]->timestamp);
534 j += strlen (the_history[i]->timestamp);
535 buffer[j++] = '\n';
536 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000537 strcpy (buffer + j, the_history[i]->line);
538 j += strlen (the_history[i]->line);
539 buffer[j++] = '\n';
540 }
541
Jari Aaltob80f6442004-07-27 13:29:18 +0000542#ifdef HISTORY_USE_MMAP
Jari Aalto7117c2d2002-07-17 14:10:11 +0000543 if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0)
544 rv = errno;
545#else
Jari Aalto28ef6c32001-04-06 19:14:31 +0000546 if (write (file, buffer, buffer_size) < 0)
547 rv = errno;
Chet Ramey495aee42011-11-22 19:11:26 -0500548 xfree (buffer);
Jari Aalto7117c2d2002-07-17 14:10:11 +0000549#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000550 }
551
Chet Rameyac50fba2014-02-26 09:36:43 -0500552 if (close (file) < 0 && rv == 0)
553 rv = errno;
554
555 if (rv != 0 && output && bakname)
556 rename (bakname, output);
557 else if (rv == 0 && bakname)
558 unlink (bakname);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000559
560 FREE (output);
Chet Rameyac50fba2014-02-26 09:36:43 -0500561 FREE (bakname);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000562
Jari Aalto28ef6c32001-04-06 19:14:31 +0000563 return (rv);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000564}
565
566/* Append NELEMENT entries to FILENAME. The entries appended are from
567 the end of the list minus NELEMENTs up to the end of the list. */
568int
569append_history (nelements, filename)
570 int nelements;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000571 const char *filename;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000572{
573 return (history_do_write (filename, nelements, HISTORY_APPEND));
574}
575
576/* Overwrite FILENAME with the current history. If FILENAME is NULL,
577 then write the history list to ~/.history. Values returned
578 are as in read_history ().*/
579int
580write_history (filename)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000581 const char *filename;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000582{
583 return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
584}