Readline-8.0 patch 1: fix file descriptor leak with zero-length history file
[deliverable/binutils-gdb.git] / readline / 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);
8cfb541a 308 close (file);
cb41b9e7
TT
309 return 0; /* don't waste time if we don't have to */
310 }
311
5bdf8622 312#ifdef HISTORY_USE_MMAP
9255ee31
EZ
313 /* We map read/write and private so we can change newlines to NULs without
314 affecting the underlying object. */
315 buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
316 if ((void *)buffer == MAP_FAILED)
5bdf8622
DJ
317 {
318 errno = overflow_errno;
319 goto error_and_exit;
320 }
9255ee31
EZ
321 chars_read = file_size;
322#else
323 buffer = (char *)malloc (file_size + 1);
324 if (buffer == 0)
5bdf8622
DJ
325 {
326 errno = overflow_errno;
327 goto error_and_exit;
328 }
1b17e766
EZ
329
330 chars_read = read (file, buffer, file_size);
9255ee31 331#endif
1b17e766 332 if (chars_read < 0)
d60d9f65
SS
333 {
334 error_and_exit:
5bdf8622
DJ
335 if (errno != 0)
336 chars_read = errno;
337 else
338 chars_read = EIO;
d60d9f65
SS
339 if (file >= 0)
340 close (file);
341
342 FREE (input);
5bdf8622 343#ifndef HISTORY_USE_MMAP
d60d9f65 344 FREE (buffer);
9255ee31 345#endif
d60d9f65 346
9255ee31 347 return (chars_read);
d60d9f65
SS
348 }
349
350 close (file);
351
352 /* Set TO to larger than end of file if negative. */
353 if (to < 0)
1b17e766 354 to = chars_read;
d60d9f65
SS
355
356 /* Start at beginning of file, work to end. */
9255ee31 357 bufend = buffer + chars_read;
cb41b9e7 358 *bufend = '\0'; /* null-terminate buffer for timestamp checks */
9255ee31 359 current_line = 0;
d60d9f65 360
775e241e
TT
361 /* Heuristic: the history comment character rarely changes, so assume we
362 have timestamps if the buffer starts with `#[:digit:]' and temporarily
363 set history_comment_char so timestamp parsing works right */
364 reset_comment_char = 0;
365 if (history_comment_char == '\0' && buffer[0] == '#' && isdigit ((unsigned char)buffer[1]))
366 {
367 history_comment_char = '#';
368 reset_comment_char = 1;
369 }
370
371 has_timestamps = HIST_TIMESTAMP_START (buffer);
372 history_multiline_entries += has_timestamps && history_write_timestamps;
373
d60d9f65 374 /* Skip lines until we are at FROM. */
9255ee31
EZ
375 for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
376 if (*line_end == '\n')
377 {
5bdf8622
DJ
378 p = line_end + 1;
379 /* If we see something we think is a timestamp, continue with this
380 line. We should check more extensively here... */
381 if (HIST_TIMESTAMP_START(p) == 0)
382 current_line++;
383 line_start = p;
9255ee31 384 }
d60d9f65
SS
385
386 /* If there are lines left to gobble, then gobble them now. */
9255ee31
EZ
387 for (line_end = line_start; line_end < bufend; line_end++)
388 if (*line_end == '\n')
d60d9f65 389 {
cc88a640
JK
390 /* Change to allow Windows-like \r\n end of line delimiter. */
391 if (line_end > line_start && line_end[-1] == '\r')
392 line_end[-1] = '\0';
a75b402a
DJ
393 else
394 *line_end = '\0';
d60d9f65 395
9255ee31 396 if (*line_start)
5bdf8622
DJ
397 {
398 if (HIST_TIMESTAMP_START(line_start) == 0)
399 {
cb41b9e7 400 if (last_ts == NULL && history_length > 0 && history_multiline_entries)
775e241e
TT
401 _hs_append_history_line (history_length - 1, line_start);
402 else
403 add_history (line_start);
5bdf8622
DJ
404 if (last_ts)
405 {
406 add_history_time (last_ts);
407 last_ts = NULL;
408 }
409 }
410 else
411 {
412 last_ts = line_start;
413 current_line--;
414 }
415 }
d60d9f65
SS
416
417 current_line++;
418
419 if (current_line >= to)
420 break;
421
422 line_start = line_end + 1;
423 }
424
775e241e
TT
425 history_lines_read_from_file = current_line;
426 if (reset_comment_char)
427 history_comment_char = '\0';
428
d60d9f65 429 FREE (input);
5bdf8622 430#ifndef HISTORY_USE_MMAP
d60d9f65 431 FREE (buffer);
9255ee31
EZ
432#else
433 munmap (buffer, file_size);
434#endif
d60d9f65
SS
435
436 return (0);
437}
438
775e241e
TT
439/* Save FILENAME to BACK, handling case where FILENAME is a symlink
440 (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */
441static int
cb41b9e7 442histfile_backup (const char *filename, const char *back)
775e241e
TT
443{
444#if defined (HAVE_READLINK)
445 char linkbuf[PATH_MAX+1];
446 ssize_t n;
447
448 /* Follow to target of symlink to avoid renaming symlink itself */
449 if ((n = readlink (filename, linkbuf, sizeof (linkbuf) - 1)) > 0)
450 {
451 linkbuf[n] = '\0';
452 return (rename (linkbuf, back));
453 }
454#endif
455 return (rename (filename, back));
456}
457
458/* Restore ORIG from BACKUP handling case where ORIG is a symlink
459 (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */
460static int
cb41b9e7 461histfile_restore (const char *backup, const char *orig)
775e241e
TT
462{
463#if defined (HAVE_READLINK)
464 char linkbuf[PATH_MAX+1];
465 ssize_t n;
466
467 /* Follow to target of symlink to avoid renaming symlink itself */
468 if ((n = readlink (orig, linkbuf, sizeof (linkbuf) - 1)) > 0)
469 {
470 linkbuf[n] = '\0';
471 return (rename (backup, linkbuf));
472 }
473#endif
474 return (rename (backup, orig));
475}
476
d60d9f65 477/* Truncate the history file FNAME, leaving only LINES trailing lines.
775e241e
TT
478 If FNAME is NULL, then use ~/.history. Writes a new file and renames
479 it to the original name. Returns 0 on success, errno on failure. */
d60d9f65 480int
cb41b9e7 481history_truncate_file (const char *fname, int lines)
d60d9f65 482{
775e241e
TT
483 char *buffer, *filename, *tempname, *bp, *bp1; /* bp1 == bp+1 */
484 int file, chars_read, rv, orig_lines, exists, r;
d60d9f65
SS
485 struct stat finfo;
486 size_t file_size;
487
775e241e
TT
488 history_lines_written_to_file = 0;
489
d60d9f65
SS
490 buffer = (char *)NULL;
491 filename = history_filename (fname);
775e241e 492 tempname = 0;
cc88a640 493 file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
775e241e 494 rv = exists = 0;
d60d9f65 495
9255ee31 496 /* Don't try to truncate non-regular files. */
d60d9f65 497 if (file == -1 || fstat (file, &finfo) == -1)
9255ee31
EZ
498 {
499 rv = errno;
500 if (file != -1)
501 close (file);
502 goto truncate_exit;
503 }
775e241e 504 exists = 1;
d60d9f65 505
9255ee31
EZ
506 if (S_ISREG (finfo.st_mode) == 0)
507 {
508 close (file);
509#ifdef EFTYPE
510 rv = EFTYPE;
511#else
512 rv = EINVAL;
513#endif
514 goto truncate_exit;
515 }
1b17e766 516
d60d9f65
SS
517 file_size = (size_t)finfo.st_size;
518
519 /* check for overflow on very large files */
520 if (file_size != finfo.st_size || file_size + 1 < file_size)
521 {
522 close (file);
523#if defined (EFBIG)
9255ee31
EZ
524 rv = errno = EFBIG;
525#elif defined (EOVERFLOW)
526 rv = errno = EOVERFLOW;
527#else
528 rv = errno = EINVAL;
d60d9f65
SS
529#endif
530 goto truncate_exit;
531 }
532
9255ee31
EZ
533 buffer = (char *)malloc (file_size + 1);
534 if (buffer == 0)
535 {
775e241e 536 rv = errno;
9255ee31
EZ
537 close (file);
538 goto truncate_exit;
539 }
540
d60d9f65
SS
541 chars_read = read (file, buffer, file_size);
542 close (file);
543
544 if (chars_read <= 0)
9255ee31
EZ
545 {
546 rv = (chars_read < 0) ? errno : 0;
547 goto truncate_exit;
548 }
d60d9f65 549
775e241e 550 orig_lines = lines;
d60d9f65 551 /* Count backwards from the end of buffer until we have passed
5bdf8622
DJ
552 LINES lines. bp1 is set funny initially. But since bp[1] can't
553 be a comment character (since it's off the end) and *bp can't be
554 both a newline and the history comment character, it should be OK. */
555 for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
d60d9f65 556 {
5bdf8622 557 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
d60d9f65 558 lines--;
5bdf8622 559 bp1 = bp;
d60d9f65
SS
560 }
561
562 /* If this is the first line, then the file contains exactly the
563 number of lines we want to truncate to, so we don't need to do
564 anything. It's the first line if we don't find a newline between
565 the current value of i and 0. Otherwise, write from the start of
566 this line until the end of the buffer. */
9255ee31 567 for ( ; bp > buffer; bp--)
5bdf8622
DJ
568 {
569 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
570 {
571 bp++;
572 break;
573 }
574 bp1 = bp;
575 }
d60d9f65
SS
576
577 /* Write only if there are more lines in the file than we want to
578 truncate to. */
775e241e
TT
579 if (bp <= buffer)
580 {
581 rv = 0;
582 /* No-op if LINES == 0 at this point */
583 history_lines_written_to_file = orig_lines - lines;
584 goto truncate_exit;
585 }
586
587 tempname = history_tempfile (filename);
588
589 if ((file = open (tempname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600)) != -1)
d60d9f65 590 {
16bfc2f9
AH
591 if (write (file, bp, chars_read - (bp - buffer)) < 0)
592 rv = errno;
c862e87b 593
775e241e
TT
594 if (close (file) < 0 && rv == 0)
595 rv = errno;
d60d9f65 596 }
775e241e
TT
597 else
598 rv = errno;
d60d9f65
SS
599
600 truncate_exit:
5836a818 601 FREE (buffer);
4a11f206 602
775e241e
TT
603 history_lines_written_to_file = orig_lines - lines;
604
605 if (rv == 0 && filename && tempname)
606 rv = histfile_restore (tempname, filename);
607
608 if (rv != 0)
609 {
610 if (tempname)
611 unlink (tempname);
612 history_lines_written_to_file = 0;
613 }
614
cb41b9e7 615#if defined (HAVE_CHOWN)
775e241e
TT
616 /* Make sure the new filename is owned by the same user as the old. If one
617 user is running this, it's a no-op. If the shell is running after sudo
618 with a shared history file, we don't want to leave the history file
619 owned by root. */
620 if (rv == 0 && exists)
621 r = chown (filename, finfo.st_uid, finfo.st_gid);
cb41b9e7 622#endif
775e241e 623
cc88a640 624 xfree (filename);
775e241e
TT
625 FREE (tempname);
626
9255ee31 627 return rv;
d60d9f65
SS
628}
629
775e241e 630/* Workhorse function for writing history. Writes the last NELEMENT entries
d60d9f65
SS
631 from the history list to FILENAME. OVERWRITE is non-zero if you
632 wish to replace FILENAME with the entries. */
633static int
cb41b9e7 634history_do_write (const char *filename, int nelements, int overwrite)
d60d9f65
SS
635{
636 register int i;
775e241e
TT
637 char *output, *tempname, *histname;
638 int file, mode, rv, exists;
639 struct stat finfo;
5bdf8622 640#ifdef HISTORY_USE_MMAP
9255ee31 641 size_t cursize;
d60d9f65 642
775e241e
TT
643 history_lines_written_to_file = 0;
644
9255ee31
EZ
645 mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
646#else
d60d9f65 647 mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
9255ee31 648#endif
775e241e
TT
649 histname = history_filename (filename);
650 exists = histname ? (stat (histname, &finfo) == 0) : 0;
651
652 tempname = (overwrite && exists && S_ISREG (finfo.st_mode)) ? history_tempfile (histname) : 0;
653 output = tempname ? tempname : histname;
654
cc88a640 655 file = output ? open (output, mode, 0600) : -1;
9255ee31 656 rv = 0;
d60d9f65 657
cc88a640 658 if (file == -1)
d60d9f65 659 {
775e241e
TT
660 rv = errno;
661 FREE (histname);
662 FREE (tempname);
663 return (rv);
d60d9f65
SS
664 }
665
5bdf8622 666#ifdef HISTORY_USE_MMAP
9255ee31
EZ
667 cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
668#endif
669
d60d9f65
SS
670 if (nelements > history_length)
671 nelements = history_length;
672
673 /* Build a buffer of all the lines to write, and write them in one syscall.
674 Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
675 {
676 HIST_ENTRY **the_history; /* local */
677 register int j;
678 int buffer_size;
679 char *buffer;
680
681 the_history = history_list ();
682 /* Calculate the total number of bytes to write. */
683 for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
5bdf8622
DJ
684#if 0
685 buffer_size += 2 + HISTENT_BYTES (the_history[i]);
686#else
687 {
688 if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
689 buffer_size += strlen (the_history[i]->timestamp) + 1;
690 buffer_size += strlen (the_history[i]->line) + 1;
691 }
692#endif
d60d9f65
SS
693
694 /* Allocate the buffer, and fill it. */
5bdf8622 695#ifdef HISTORY_USE_MMAP
9255ee31
EZ
696 if (ftruncate (file, buffer_size+cursize) == -1)
697 goto mmap_error;
698 buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
699 if ((void *)buffer == MAP_FAILED)
700 {
701mmap_error:
702 rv = errno;
9255ee31 703 close (file);
775e241e
TT
704 if (tempname)
705 unlink (tempname);
706 FREE (histname);
707 FREE (tempname);
9255ee31
EZ
708 return rv;
709 }
710#else
711 buffer = (char *)malloc (buffer_size);
712 if (buffer == 0)
713 {
714 rv = errno;
9255ee31 715 close (file);
775e241e
TT
716 if (tempname)
717 unlink (tempname);
718 FREE (histname);
719 FREE (tempname);
9255ee31
EZ
720 return rv;
721 }
722#endif
d60d9f65
SS
723
724 for (j = 0, i = history_length - nelements; i < history_length; i++)
725 {
5bdf8622
DJ
726 if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
727 {
728 strcpy (buffer + j, the_history[i]->timestamp);
729 j += strlen (the_history[i]->timestamp);
730 buffer[j++] = '\n';
731 }
d60d9f65
SS
732 strcpy (buffer + j, the_history[i]->line);
733 j += strlen (the_history[i]->line);
734 buffer[j++] = '\n';
735 }
736
5bdf8622 737#ifdef HISTORY_USE_MMAP
775e241e 738 if (msync (buffer, buffer_size, MS_ASYNC) != 0 || munmap (buffer, buffer_size) != 0)
9255ee31
EZ
739 rv = errno;
740#else
741 if (write (file, buffer, buffer_size) < 0)
742 rv = errno;
cc88a640 743 xfree (buffer);
9255ee31 744#endif
d60d9f65
SS
745 }
746
775e241e
TT
747 history_lines_written_to_file = nelements;
748
749 if (close (file) < 0 && rv == 0)
750 rv = errno;
751
752 if (rv == 0 && histname && tempname)
753 rv = histfile_restore (tempname, histname);
754
755 if (rv != 0)
756 {
757 if (tempname)
758 unlink (tempname);
759 history_lines_written_to_file = 0;
760 }
761
cb41b9e7 762#if defined (HAVE_CHOWN)
775e241e
TT
763 /* Make sure the new filename is owned by the same user as the old. If one
764 user is running this, it's a no-op. If the shell is running after sudo
765 with a shared history file, we don't want to leave the history file
766 owned by root. */
767 if (rv == 0 && exists)
768 mode = chown (histname, finfo.st_uid, finfo.st_gid);
cb41b9e7 769#endif
d60d9f65 770
775e241e
TT
771 FREE (histname);
772 FREE (tempname);
d60d9f65 773
9255ee31 774 return (rv);
d60d9f65
SS
775}
776
777/* Append NELEMENT entries to FILENAME. The entries appended are from
778 the end of the list minus NELEMENTs up to the end of the list. */
779int
cb41b9e7 780append_history (int nelements, const char *filename)
d60d9f65
SS
781{
782 return (history_do_write (filename, nelements, HISTORY_APPEND));
783}
784
785/* Overwrite FILENAME with the current history. If FILENAME is NULL,
786 then write the history list to ~/.history. Values returned
787 are as in read_history ().*/
788int
cb41b9e7 789write_history (const char *filename)
d60d9f65
SS
790{
791 return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
792}
This page took 1.033589 seconds and 4 git commands to generate.