]> git.ipfire.org Git - thirdparty/bash.git/blame - lib/readline/histfile.c
final set of ANSI C changes
[thirdparty/bash.git] / lib / readline / histfile.c
CommitLineData
ccc6cda3
JA
1/* histfile.c - functions to manipulate the history file. */
2
03e4274f 3/* Copyright (C) 1989-2019 Free Software Foundation, Inc.
ccc6cda3 4
2e4498b3 5 This file contains the GNU History Library (History), a set of
ccc6cda3
JA
6 routines for managing the text of previously typed lines.
7
2e4498b3 8 History is free software: you can redistribute it and/or modify
ccc6cda3 9 it under the terms of the GNU General Public License as published by
2e4498b3
CR
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 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.
17
18 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*/
ccc6cda3
JA
21
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. */
5565fb1a 25
ccc6cda3
JA
26#define READLINE_LIBRARY
27
5565fb1a 28#if defined (__TANDEM)
fc35c477
CR
29# define _XOPEN_SOURCE_EXTENDED 1
30# include <unistd.h>
5565fb1a
CR
31# include <floss.h>
32#endif
33
ccc6cda3
JA
34#if defined (HAVE_CONFIG_H)
35# include <config.h>
36#endif
37
38#include <stdio.h>
39
8a0829e9
CR
40#if defined (HAVE_LIMITS_H)
41# include <limits.h>
42#endif
43
ccc6cda3 44#include <sys/types.h>
d3a24ed2 45#if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H)
cce855bc
JA
46# include <sys/file.h>
47#endif
bb70624e 48#include "posixstat.h"
ccc6cda3
JA
49#include <fcntl.h>
50
51#if defined (HAVE_STDLIB_H)
52# include <stdlib.h>
53#else
54# include "ansi_stdlib.h"
55#endif /* HAVE_STDLIB_H */
56
57#if defined (HAVE_UNISTD_H)
58# include <unistd.h>
59#endif
60
d3ad40de
CR
61#include <ctype.h>
62
63#if defined (__EMX__)
7117c2d2
JA
64# undef HAVE_MMAP
65#endif
ccc6cda3 66
d3a24ed2 67#ifdef HISTORY_USE_MMAP
7117c2d2
JA
68# include <sys/mman.h>
69
70# ifdef MAP_FILE
71# define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE)
72# define MAP_WFLAGS (MAP_FILE|MAP_SHARED)
73# else
74# define MAP_RFLAGS MAP_PRIVATE
75# define MAP_WFLAGS MAP_SHARED
76# endif
77
78# ifndef MAP_FAILED
79# define MAP_FAILED ((void *)-1)
80# endif
81
d3a24ed2 82#endif /* HISTORY_USE_MMAP */
bb70624e 83
03e4274f
CR
84#if defined(_WIN32)
85# define WIN32_LEAN_AND_MEAN
86# include <windows.h>
87#endif
88
bb70624e
JA
89/* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
90 on win 95/98/nt), we want to open files with O_BINARY mode so that there
91 is no \n -> \r\n conversion performed. On other systems, we don't want to
92 mess around with O_BINARY at all, so we ensure that it's defined to 0. */
93#if defined (__EMX__) || defined (__CYGWIN__)
d166f048
JA
94# ifndef O_BINARY
95# define O_BINARY 0
96# endif
bb70624e 97#else /* !__EMX__ && !__CYGWIN__ */
d166f048
JA
98# undef O_BINARY
99# define O_BINARY 0
bb70624e 100#endif /* !__EMX__ && !__CYGWIN__ */
d166f048 101
ccc6cda3
JA
102#include <errno.h>
103#if !defined (errno)
104extern int errno;
105#endif /* !errno */
106
107#include "history.h"
108#include "histlib.h"
109
bb70624e
JA
110#include "rlshell.h"
111#include "xmalloc.h"
ccc6cda3 112
8a0829e9
CR
113#if !defined (PATH_MAX)
114# define PATH_MAX 1024 /* default */
115#endif
116
6f82653c
CR
117/* history file version; currently unused */
118int history_file_version = 1;
119
d3a24ed2
CR
120/* If non-zero, we write timestamps to the history file in history_do_write() */
121int history_write_timestamps = 0;
122
6f82653c
CR
123/* If non-zero, we assume that a history file that starts with a timestamp
124 uses timestamp-delimited entries and can include multi-line history
125 entries. Used by read_history_range */
126int history_multiline_entries = 0;
127
03d922b1
CR
128/* Immediately after a call to read_history() or read_history_range(), this
129 will return the number of lines just read from the history file in that
130 call. */
131int history_lines_read_from_file = 0;
132
133/* Immediately after a call to write_history() or history_do_write(), this
134 will return the number of lines just written to the history file in that
135 call. This also works with history_truncate_file. */
136int history_lines_written_to_file = 0;
137
d3a24ed2
CR
138/* Does S look like the beginning of a history timestamp entry? Placeholder
139 for more extensive tests. */
c4c90ef8 140#define HIST_TIMESTAMP_START(s) (*(s) == history_comment_char && isdigit ((unsigned char)(s)[1]) )
d3a24ed2 141
38881450
CR
142static char *history_backupfile (const char *);
143static char *history_tempfile (const char *);
144static int histfile_backup (const char *, const char *);
145static int histfile_restore (const char *, const char *);
146static int history_rename (const char *, const char *);
de608191 147
ccc6cda3
JA
148/* Return the string that should be used in the place of this
149 filename. This only matters when you don't specify the
150 filename to read_history (), or write_history (). */
151static char *
10729c7b 152history_filename (const char *filename)
ccc6cda3 153{
28ef6c31
JA
154 char *return_val;
155 const char *home;
2e725f73 156 size_t home_len;
ccc6cda3
JA
157
158 return_val = filename ? savestring (filename) : (char *)NULL;
159
160 if (return_val)
161 return (return_val);
162
28ef6c31 163 home = sh_get_env_value ("HOME");
8f50a023
CR
164#if defined (_WIN32)
165 if (home == 0)
166 home = sh_get_env_value ("APPDATA");
167#endif
ccc6cda3
JA
168
169 if (home == 0)
276cb932 170 return (NULL);
ccc6cda3
JA
171 else
172 home_len = strlen (home);
173
f73dda09 174 return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
ccc6cda3
JA
175 strcpy (return_val, home);
176 return_val[home_len] = '/';
bb70624e
JA
177#if defined (__MSDOS__)
178 strcpy (return_val + home_len + 1, "_history");
179#else
ccc6cda3 180 strcpy (return_val + home_len + 1, ".history");
bb70624e 181#endif
ccc6cda3
JA
182
183 return (return_val);
184}
185
51f7ea36 186static char *
10729c7b 187history_backupfile (const char *filename)
51f7ea36 188{
f8b90b7b
CR
189 const char *fn;
190 char *ret, linkbuf[PATH_MAX+1];
51f7ea36 191 size_t len;
f8b90b7b 192 ssize_t n;
51f7ea36 193
de608191
CR
194 fn = filename;
195#if defined (HAVE_READLINK)
196 /* Follow symlink to avoid backing up symlink itself; call will fail if
197 not a symlink */
198 if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0)
f8b90b7b 199 {
de608191
CR
200 linkbuf[n] = '\0';
201 fn = linkbuf;
f8b90b7b
CR
202 }
203#endif
204
205 len = strlen (fn);
51f7ea36 206 ret = xmalloc (len + 2);
f8b90b7b 207 strcpy (ret, fn);
51f7ea36
CR
208 ret[len] = '-';
209 ret[len+1] = '\0';
210 return ret;
211}
212
1573ba78 213static char *
10729c7b 214history_tempfile (const char *filename)
1573ba78
CR
215{
216 const char *fn;
217 char *ret, linkbuf[PATH_MAX+1];
218 size_t len;
219 ssize_t n;
1573ba78
CR
220 int pid;
221
222 fn = filename;
223#if defined (HAVE_READLINK)
224 /* Follow symlink so tempfile created in the same directory as any symlinked
225 history file; call will fail if not a symlink */
226 if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0)
227 {
228 linkbuf[n] = '\0';
229 fn = linkbuf;
230 }
231#endif
232
233 len = strlen (fn);
234 ret = xmalloc (len + 11);
235 strcpy (ret, fn);
236
237 pid = (int)getpid ();
238
239 /* filename-PID.tmp */
240 ret[len] = '-';
241 ret[len+1] = (pid / 10000 % 10) + '0';
242 ret[len+2] = (pid / 1000 % 10) + '0';
243 ret[len+3] = (pid / 100 % 10) + '0';
244 ret[len+4] = (pid / 10 % 10) + '0';
245 ret[len+5] = (pid % 10) + '0';
246 strcpy (ret + len + 6, ".tmp");
247
248 return ret;
249}
250
ccc6cda3
JA
251/* Add the contents of FILENAME to the history list, a line at a time.
252 If FILENAME is NULL, then read from ~/.history. Returns 0 if
253 successful, or errno if not. */
254int
10729c7b 255read_history (const char *filename)
ccc6cda3
JA
256{
257 return (read_history_range (filename, 0, -1));
258}
259
260/* Read a range of lines from FILENAME, adding them to the history list.
261 Start reading at the FROM'th line and end at the TO'th. If FROM
262 is zero, start at the beginning. If TO is less than FROM, read
263 until the end of the file. If FILENAME is NULL, then read from
264 ~/.history. Returns 0 if successful, or errno if not. */
265int
10729c7b 266read_history_range (const char *filename, int from, int to)
ccc6cda3 267{
d3a24ed2
CR
268 register char *line_start, *line_end, *p;
269 char *input, *buffer, *bufend, *last_ts;
6f82653c 270 int file, current_line, chars_read, has_timestamps, reset_comment_char;
ccc6cda3 271 struct stat finfo;
cce855bc 272 size_t file_size;
d3a24ed2
CR
273#if defined (EFBIG)
274 int overflow_errno = EFBIG;
275#elif defined (EOVERFLOW)
276 int overflow_errno = EOVERFLOW;
277#else
278 int overflow_errno = EIO;
279#endif
ccc6cda3 280
03d922b1
CR
281 history_lines_read_from_file = 0;
282
d3a24ed2 283 buffer = last_ts = (char *)NULL;
ccc6cda3 284 input = history_filename (filename);
64419627 285 file = input ? open (input, O_RDONLY|O_BINARY, 0666) : -1;
ccc6cda3
JA
286
287 if ((file < 0) || (fstat (file, &finfo) == -1))
288 goto error_and_exit;
289
8b6b8f60
CR
290 if (S_ISREG (finfo.st_mode) == 0)
291 {
292#ifdef EFTYPE
293 errno = EFTYPE;
294#else
295 errno = EINVAL;
296#endif
297 goto error_and_exit;
298 }
299
cce855bc
JA
300 file_size = (size_t)finfo.st_size;
301
302 /* check for overflow on very large files */
303 if (file_size != finfo.st_size || file_size + 1 < file_size)
304 {
d3a24ed2 305 errno = overflow_errno;
cce855bc
JA
306 goto error_and_exit;
307 }
ccc6cda3 308
f698849a 309 if (file_size == 0)
2a391577 310 {
0b9a4b3a 311 xfree (input);
439b8c2c 312 close (file);
2a391577
CR
313 return 0; /* don't waste time if we don't have to */
314 }
f698849a 315
d3a24ed2 316#ifdef HISTORY_USE_MMAP
7117c2d2
JA
317 /* We map read/write and private so we can change newlines to NULs without
318 affecting the underlying object. */
319 buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
320 if ((void *)buffer == MAP_FAILED)
d3a24ed2
CR
321 {
322 errno = overflow_errno;
323 goto error_and_exit;
324 }
7117c2d2
JA
325 chars_read = file_size;
326#else
327 buffer = (char *)malloc (file_size + 1);
328 if (buffer == 0)
d3a24ed2
CR
329 {
330 errno = overflow_errno;
331 goto error_and_exit;
332 }
bb70624e
JA
333
334 chars_read = read (file, buffer, file_size);
7117c2d2 335#endif
bb70624e 336 if (chars_read < 0)
ccc6cda3
JA
337 {
338 error_and_exit:
d3a24ed2
CR
339 if (errno != 0)
340 chars_read = errno;
341 else
342 chars_read = EIO;
ccc6cda3
JA
343 if (file >= 0)
344 close (file);
345
346 FREE (input);
d3a24ed2 347#ifndef HISTORY_USE_MMAP
ccc6cda3 348 FREE (buffer);
7117c2d2 349#endif
ccc6cda3 350
7117c2d2 351 return (chars_read);
ccc6cda3
JA
352 }
353
354 close (file);
355
356 /* Set TO to larger than end of file if negative. */
357 if (to < 0)
bb70624e 358 to = chars_read;
ccc6cda3
JA
359
360 /* Start at beginning of file, work to end. */
7117c2d2 361 bufend = buffer + chars_read;
f698849a 362 *bufend = '\0'; /* null-terminate buffer for timestamp checks */
7117c2d2 363 current_line = 0;
ccc6cda3 364
6f82653c
CR
365 /* Heuristic: the history comment character rarely changes, so assume we
366 have timestamps if the buffer starts with `#[:digit:]' and temporarily
367 set history_comment_char so timestamp parsing works right */
368 reset_comment_char = 0;
369 if (history_comment_char == '\0' && buffer[0] == '#' && isdigit ((unsigned char)buffer[1]))
370 {
371 history_comment_char = '#';
372 reset_comment_char = 1;
373 }
374
375 has_timestamps = HIST_TIMESTAMP_START (buffer);
376c9fe5 376 history_multiline_entries += has_timestamps && history_write_timestamps;
6f82653c 377
ccc6cda3 378 /* Skip lines until we are at FROM. */
376c9fe5
CR
379 if (has_timestamps)
380 last_ts = buffer;
7117c2d2
JA
381 for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
382 if (*line_end == '\n')
383 {
d3a24ed2
CR
384 p = line_end + 1;
385 /* If we see something we think is a timestamp, continue with this
386 line. We should check more extensively here... */
387 if (HIST_TIMESTAMP_START(p) == 0)
388 current_line++;
376c9fe5
CR
389 else
390 last_ts = p;
d3a24ed2 391 line_start = p;
376c9fe5
CR
392 /* If we are at the last line (current_line == from) but we have
393 timestamps (has_timestamps), then line_start points to the
394 text of the last command, and we need to skip to its end. */
395 if (current_line >= from && has_timestamps)
396 {
397 for (line_end = p; line_end < bufend && *line_end != '\n'; line_end++)
398 ;
399 line_start = (*line_end == '\n') ? line_end + 1 : line_end;
400 }
7117c2d2 401 }
ccc6cda3
JA
402
403 /* If there are lines left to gobble, then gobble them now. */
7117c2d2
JA
404 for (line_end = line_start; line_end < bufend; line_end++)
405 if (*line_end == '\n')
ccc6cda3 406 {
d3ad40de
CR
407 /* Change to allow Windows-like \r\n end of line delimiter. */
408 if (line_end > line_start && line_end[-1] == '\r')
409 line_end[-1] = '\0';
410 else
411 *line_end = '\0';
ccc6cda3 412
7117c2d2 413 if (*line_start)
d3a24ed2
CR
414 {
415 if (HIST_TIMESTAMP_START(line_start) == 0)
416 {
12beeabf 417 if (last_ts == NULL && history_length > 0 && history_multiline_entries)
6f82653c
CR
418 _hs_append_history_line (history_length - 1, line_start);
419 else
420 add_history (line_start);
d3a24ed2
CR
421 if (last_ts)
422 {
423 add_history_time (last_ts);
424 last_ts = NULL;
425 }
426 }
427 else
428 {
429 last_ts = line_start;
430 current_line--;
431 }
432 }
ccc6cda3
JA
433
434 current_line++;
435
436 if (current_line >= to)
437 break;
438
439 line_start = line_end + 1;
440 }
441
03d922b1 442 history_lines_read_from_file = current_line;
6f82653c
CR
443 if (reset_comment_char)
444 history_comment_char = '\0';
03d922b1 445
ccc6cda3 446 FREE (input);
d3a24ed2 447#ifndef HISTORY_USE_MMAP
ccc6cda3 448 FREE (buffer);
7117c2d2
JA
449#else
450 munmap (buffer, file_size);
451#endif
ccc6cda3
JA
452
453 return (0);
454}
455
03e4274f
CR
456/* We need a special version for WIN32 because Windows rename() refuses to
457 overwrite an existing file. */
458static int
459history_rename (const char *old, const char *new)
460{
461#if defined (_WIN32)
462 return (MoveFileEx (old, new, MOVEFILE_REPLACE_EXISTING) == 0 ? -1 : 0);
463#else
464 return (rename (old, new));
465#endif
466}
467
1573ba78
CR
468/* Save FILENAME to BACK, handling case where FILENAME is a symlink
469 (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */
f8b90b7b 470static int
10729c7b 471histfile_backup (const char *filename, const char *back)
f8b90b7b 472{
de608191
CR
473#if defined (HAVE_READLINK)
474 char linkbuf[PATH_MAX+1];
475 ssize_t n;
476
477 /* Follow to target of symlink to avoid renaming symlink itself */
478 if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0)
479 {
480 linkbuf[n] = '\0';
03e4274f 481 return (history_rename (linkbuf, back));
de608191
CR
482 }
483#endif
03e4274f 484 return (history_rename (filename, back));
de608191
CR
485}
486
1573ba78
CR
487/* Restore ORIG from BACKUP handling case where ORIG is a symlink
488 (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */
de608191 489static int
10729c7b 490histfile_restore (const char *backup, const char *orig)
de608191
CR
491{
492#if defined (HAVE_READLINK)
493 char linkbuf[PATH_MAX+1];
494 ssize_t n;
495
496 /* Follow to target of symlink to avoid renaming symlink itself */
497 if ((n = readlink (orig, linkbuf, sizeof (linkbuf) - 1)) > 0)
498 {
499 linkbuf[n] = '\0';
03e4274f 500 return (history_rename (backup, linkbuf));
de608191
CR
501 }
502#endif
03e4274f 503 return (history_rename (backup, orig));
f8b90b7b
CR
504}
505
cf58e12c
CR
506/* Should we call chown, based on whether finfo and nfinfo describe different
507 files with different owners? */
508
509#define SHOULD_CHOWN(finfo, nfinfo) \
510 (finfo.st_uid != nfinfo.st_uid || finfo.st_gid != nfinfo.st_gid)
511
ccc6cda3 512/* Truncate the history file FNAME, leaving only LINES trailing lines.
1573ba78
CR
513 If FNAME is NULL, then use ~/.history. Writes a new file and renames
514 it to the original name. Returns 0 on success, errno on failure. */
ccc6cda3 515int
10729c7b 516history_truncate_file (const char *fname, int lines)
ccc6cda3 517{
1573ba78 518 char *buffer, *filename, *tempname, *bp, *bp1; /* bp1 == bp+1 */
1b05a005 519 int file, chars_read, rv, orig_lines, exists, r;
cf58e12c 520 struct stat finfo, nfinfo;
cce855bc 521 size_t file_size;
ccc6cda3 522
03d922b1
CR
523 history_lines_written_to_file = 0;
524
d166f048 525 buffer = (char *)NULL;
ccc6cda3 526 filename = history_filename (fname);
1573ba78 527 tempname = 0;
64419627 528 file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
03d922b1 529 rv = exists = 0;
ccc6cda3 530
28ef6c31 531 /* Don't try to truncate non-regular files. */
ccc6cda3 532 if (file == -1 || fstat (file, &finfo) == -1)
28ef6c31
JA
533 {
534 rv = errno;
535 if (file != -1)
536 close (file);
537 goto truncate_exit;
538 }
03d922b1 539 exists = 1;
ccc6cda3 540
cf58e12c
CR
541 nfinfo.st_uid = finfo.st_uid;
542 nfinfo.st_gid = finfo.st_gid;
543
28ef6c31
JA
544 if (S_ISREG (finfo.st_mode) == 0)
545 {
546 close (file);
547#ifdef EFTYPE
548 rv = EFTYPE;
549#else
550 rv = EINVAL;
551#endif
552 goto truncate_exit;
553 }
bb70624e 554
cce855bc
JA
555 file_size = (size_t)finfo.st_size;
556
557 /* check for overflow on very large files */
558 if (file_size != finfo.st_size || file_size + 1 < file_size)
559 {
560 close (file);
561#if defined (EFBIG)
28ef6c31
JA
562 rv = errno = EFBIG;
563#elif defined (EOVERFLOW)
564 rv = errno = EOVERFLOW;
565#else
566 rv = errno = EINVAL;
cce855bc
JA
567#endif
568 goto truncate_exit;
569 }
570
7117c2d2
JA
571 buffer = (char *)malloc (file_size + 1);
572 if (buffer == 0)
573 {
fbbc416f 574 rv = errno;
7117c2d2
JA
575 close (file);
576 goto truncate_exit;
577 }
578
cce855bc 579 chars_read = read (file, buffer, file_size);
ccc6cda3
JA
580 close (file);
581
582 if (chars_read <= 0)
28ef6c31
JA
583 {
584 rv = (chars_read < 0) ? errno : 0;
585 goto truncate_exit;
586 }
ccc6cda3 587
03d922b1 588 orig_lines = lines;
ccc6cda3 589 /* Count backwards from the end of buffer until we have passed
d3a24ed2
CR
590 LINES lines. bp1 is set funny initially. But since bp[1] can't
591 be a comment character (since it's off the end) and *bp can't be
592 both a newline and the history comment character, it should be OK. */
593 for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
ccc6cda3 594 {
d3a24ed2 595 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
ccc6cda3 596 lines--;
d3a24ed2 597 bp1 = bp;
ccc6cda3
JA
598 }
599
600 /* If this is the first line, then the file contains exactly the
601 number of lines we want to truncate to, so we don't need to do
602 anything. It's the first line if we don't find a newline between
603 the current value of i and 0. Otherwise, write from the start of
604 this line until the end of the buffer. */
7117c2d2 605 for ( ; bp > buffer; bp--)
d3a24ed2
CR
606 {
607 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
608 {
609 bp++;
610 break;
611 }
612 bp1 = bp;
613 }
ccc6cda3
JA
614
615 /* Write only if there are more lines in the file than we want to
616 truncate to. */
25a0eacf
CR
617 if (bp <= buffer)
618 {
619 rv = 0;
03d922b1
CR
620 /* No-op if LINES == 0 at this point */
621 history_lines_written_to_file = orig_lines - lines;
25a0eacf
CR
622 goto truncate_exit;
623 }
624
1573ba78 625 tempname = history_tempfile (filename);
25a0eacf 626
1573ba78 627 if ((file = open (tempname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600)) != -1)
ccc6cda3 628 {
7571d3f4
CR
629 if (write (file, bp, chars_read - (bp - buffer)) < 0)
630 rv = errno;
b72432fd 631
cf58e12c
CR
632 if (fstat (file, &nfinfo) < 0 && rv == 0)
633 rv = errno;
634
7571d3f4
CR
635 if (close (file) < 0 && rv == 0)
636 rv = errno;
ccc6cda3 637 }
25a0eacf
CR
638 else
639 rv = errno;
ccc6cda3
JA
640
641 truncate_exit:
ccc6cda3
JA
642 FREE (buffer);
643
03d922b1
CR
644 history_lines_written_to_file = orig_lines - lines;
645
1573ba78
CR
646 if (rv == 0 && filename && tempname)
647 rv = histfile_restore (tempname, filename);
648
649 if (rv != 0)
8a0829e9 650 {
10db6565 651 rv = errno;
1573ba78
CR
652 if (tempname)
653 unlink (tempname);
8a0829e9
CR
654 history_lines_written_to_file = 0;
655 }
25a0eacf 656
553a7d66 657#if defined (HAVE_CHOWN)
03d922b1
CR
658 /* Make sure the new filename is owned by the same user as the old. If one
659 user is running this, it's a no-op. If the shell is running after sudo
660 with a shared history file, we don't want to leave the history file
661 owned by root. */
cf58e12c 662 if (rv == 0 && exists && SHOULD_CHOWN (finfo, nfinfo))
1b05a005 663 r = chown (filename, finfo.st_uid, finfo.st_gid);
553a7d66 664#endif
03d922b1 665
9ec5ed66 666 xfree (filename);
1573ba78 667 FREE (tempname);
25a0eacf 668
28ef6c31 669 return rv;
ccc6cda3
JA
670}
671
03d922b1 672/* Workhorse function for writing history. Writes the last NELEMENT entries
ccc6cda3
JA
673 from the history list to FILENAME. OVERWRITE is non-zero if you
674 wish to replace FILENAME with the entries. */
675static int
10729c7b 676history_do_write (const char *filename, int nelements, int overwrite)
ccc6cda3
JA
677{
678 register int i;
1573ba78 679 char *output, *tempname, *histname;
03d922b1 680 int file, mode, rv, exists;
2e725f73 681 struct stat finfo;
d3a24ed2 682#ifdef HISTORY_USE_MMAP
7117c2d2 683 size_t cursize;
ccc6cda3 684
03d922b1
CR
685 history_lines_written_to_file = 0;
686
7117c2d2
JA
687 mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
688#else
d166f048 689 mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
7117c2d2 690#endif
1573ba78 691 histname = history_filename (filename);
1573ba78 692 exists = histname ? (stat (histname, &finfo) == 0) : 0;
51f7ea36 693
07357ec2
CR
694 tempname = (overwrite && exists && S_ISREG (finfo.st_mode)) ? history_tempfile (histname) : 0;
695 output = tempname ? tempname : histname;
696
64419627 697 file = output ? open (output, mode, 0600) : -1;
28ef6c31 698 rv = 0;
ccc6cda3 699
64419627 700 if (file == -1)
ccc6cda3 701 {
51f7ea36 702 rv = errno;
1573ba78
CR
703 FREE (histname);
704 FREE (tempname);
51f7ea36 705 return (rv);
ccc6cda3
JA
706 }
707
d3a24ed2 708#ifdef HISTORY_USE_MMAP
7117c2d2
JA
709 cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
710#endif
711
ccc6cda3
JA
712 if (nelements > history_length)
713 nelements = history_length;
714
715 /* Build a buffer of all the lines to write, and write them in one syscall.
716 Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
717 {
718 HIST_ENTRY **the_history; /* local */
2e725f73
CR
719 size_t j;
720 size_t buffer_size;
ccc6cda3
JA
721 char *buffer;
722
723 the_history = history_list ();
724 /* Calculate the total number of bytes to write. */
725 for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
d3a24ed2
CR
726 {
727 if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
728 buffer_size += strlen (the_history[i]->timestamp) + 1;
729 buffer_size += strlen (the_history[i]->line) + 1;
730 }
ccc6cda3
JA
731
732 /* Allocate the buffer, and fill it. */
d3a24ed2 733#ifdef HISTORY_USE_MMAP
7117c2d2
JA
734 if (ftruncate (file, buffer_size+cursize) == -1)
735 goto mmap_error;
736 buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
737 if ((void *)buffer == MAP_FAILED)
738 {
739mmap_error:
740 rv = errno;
7117c2d2 741 close (file);
1573ba78
CR
742 if (tempname)
743 unlink (tempname);
744 FREE (histname);
745 FREE (tempname);
7117c2d2
JA
746 return rv;
747 }
748#else
749 buffer = (char *)malloc (buffer_size);
750 if (buffer == 0)
751 {
752 rv = errno;
7117c2d2 753 close (file);
1573ba78
CR
754 if (tempname)
755 unlink (tempname);
756 FREE (histname);
757 FREE (tempname);
7117c2d2
JA
758 return rv;
759 }
760#endif
ccc6cda3
JA
761
762 for (j = 0, i = history_length - nelements; i < history_length; i++)
763 {
d3a24ed2
CR
764 if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
765 {
766 strcpy (buffer + j, the_history[i]->timestamp);
767 j += strlen (the_history[i]->timestamp);
768 buffer[j++] = '\n';
769 }
ccc6cda3
JA
770 strcpy (buffer + j, the_history[i]->line);
771 j += strlen (the_history[i]->line);
772 buffer[j++] = '\n';
773 }
774
d3a24ed2 775#ifdef HISTORY_USE_MMAP
a31bf37d 776 if (msync (buffer, buffer_size, MS_ASYNC) != 0 || munmap (buffer, buffer_size) != 0)
7117c2d2
JA
777 rv = errno;
778#else
28ef6c31
JA
779 if (write (file, buffer, buffer_size) < 0)
780 rv = errno;
9ec5ed66 781 xfree (buffer);
7117c2d2 782#endif
ccc6cda3
JA
783 }
784
03d922b1
CR
785 history_lines_written_to_file = nelements;
786
7571d3f4
CR
787 if (close (file) < 0 && rv == 0)
788 rv = errno;
ccc6cda3 789
1573ba78
CR
790 if (rv == 0 && histname && tempname)
791 rv = histfile_restore (tempname, histname);
792
793 if (rv != 0)
8a0829e9 794 {
10db6565 795 rv = errno;
1573ba78
CR
796 if (tempname)
797 unlink (tempname);
8a0829e9
CR
798 history_lines_written_to_file = 0;
799 }
51f7ea36 800
553a7d66 801#if defined (HAVE_CHOWN)
03d922b1
CR
802 /* Make sure the new filename is owned by the same user as the old. If one
803 user is running this, it's a no-op. If the shell is running after sudo
804 with a shared history file, we don't want to leave the history file
805 owned by root. */
806 if (rv == 0 && exists)
1b05a005 807 mode = chown (histname, finfo.st_uid, finfo.st_gid);
553a7d66 808#endif
03d922b1 809
1573ba78
CR
810 FREE (histname);
811 FREE (tempname);
ccc6cda3 812
28ef6c31 813 return (rv);
ccc6cda3
JA
814}
815
816/* Append NELEMENT entries to FILENAME. The entries appended are from
817 the end of the list minus NELEMENTs up to the end of the list. */
818int
10729c7b 819append_history (int nelements, const char *filename)
ccc6cda3
JA
820{
821 return (history_do_write (filename, nelements, HISTORY_APPEND));
822}
823
824/* Overwrite FILENAME with the current history. If FILENAME is NULL,
825 then write the history list to ~/.history. Values returned
826 are as in read_history ().*/
827int
10729c7b 828write_history (const char *filename)
ccc6cda3
JA
829{
830 return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
831}