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