Readline-8.0 patch 1: fix file descriptor leak with zero-length history file
[deliverable/binutils-gdb.git] / readline / readline / histfile.c
1 /* histfile.c - functions to manipulate the history file. */
2
3 /* Copyright (C) 1989-2018 Free Software Foundation, Inc.
4
5 This file contains the GNU History Library (History), a set of
6 routines for managing the text of previously typed lines.
7
8 History is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
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 */
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. */
25
26 #define READLINE_LIBRARY
27
28 #if defined (__TANDEM)
29 # include <floss.h>
30 #endif
31
32 #if defined (HAVE_CONFIG_H)
33 # include <config.h>
34 #endif
35
36 #include <stdio.h>
37
38 #if defined (HAVE_LIMITS_H)
39 # include <limits.h>
40 #endif
41
42 #include <sys/types.h>
43 #if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H)
44 # include <sys/file.h>
45 #endif
46 #include "posixstat.h"
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
59 #include <ctype.h>
60
61 #if defined (__EMX__)
62 # undef HAVE_MMAP
63 #endif
64
65 #ifdef HISTORY_USE_MMAP
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
79
80 #endif /* HISTORY_USE_MMAP */
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__)
87 # ifndef O_BINARY
88 # define O_BINARY 0
89 # endif
90 #else /* !__EMX__ && !__CYGWIN__ */
91 # undef O_BINARY
92 # define O_BINARY 0
93 #endif /* !__EMX__ && !__CYGWIN__ */
94
95 #include <errno.h>
96 #if !defined (errno)
97 extern int errno;
98 #endif /* !errno */
99
100 #include "history.h"
101 #include "histlib.h"
102
103 #include "rlshell.h"
104 #include "xmalloc.h"
105
106 #if !defined (PATH_MAX)
107 # define PATH_MAX 1024 /* default */
108 #endif
109
110 extern void _hs_append_history_line PARAMS((int, const char *));
111
112 /* history file version; currently unused */
113 int history_file_version = 1;
114
115 /* If non-zero, we write timestamps to the history file in history_do_write() */
116 int history_write_timestamps = 0;
117
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 */
121 int 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. */
126 int 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. */
131 int history_lines_written_to_file = 0;
132
133 /* Does S look like the beginning of a history timestamp entry? Placeholder
134 for more extensive tests. */
135 #define HIST_TIMESTAMP_START(s) (*(s) == history_comment_char && isdigit ((unsigned char)(s)[1]) )
136
137 static char *history_backupfile PARAMS((const char *));
138 static char *history_tempfile PARAMS((const char *));
139 static int histfile_backup PARAMS((const char *, const char *));
140 static int histfile_restore PARAMS((const char *, const char *));
141
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 (). */
145 static char *
146 history_filename (const char *filename)
147 {
148 char *return_val;
149 const char *home;
150 int home_len;
151
152 return_val = filename ? savestring (filename) : (char *)NULL;
153
154 if (return_val)
155 return (return_val);
156
157 home = sh_get_env_value ("HOME");
158 #if defined (_WIN32)
159 if (home == 0)
160 home = sh_get_env_value ("APPDATA");
161 #endif
162
163 if (home == 0)
164 return (NULL);
165 else
166 home_len = strlen (home);
167
168 return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
169 strcpy (return_val, home);
170 return_val[home_len] = '/';
171 #if defined (__MSDOS__)
172 strcpy (return_val + home_len + 1, "_history");
173 #else
174 strcpy (return_val + home_len + 1, ".history");
175 #endif
176
177 return (return_val);
178 }
179
180 static char *
181 history_backupfile (const char *filename)
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
208 static char *
209 history_tempfile (const char *filename)
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
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. */
250 int
251 read_history (const char *filename)
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. */
261 int
262 read_history_range (const char *filename, int from, int to)
263 {
264 register char *line_start, *line_end, *p;
265 char *input, *buffer, *bufend, *last_ts;
266 int file, current_line, chars_read, has_timestamps, reset_comment_char;
267 struct stat finfo;
268 size_t file_size;
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
276
277 history_lines_read_from_file = 0;
278
279 buffer = last_ts = (char *)NULL;
280 input = history_filename (filename);
281 file = input ? open (input, O_RDONLY|O_BINARY, 0666) : -1;
282
283 if ((file < 0) || (fstat (file, &finfo) == -1))
284 goto error_and_exit;
285
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
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 {
301 errno = overflow_errno;
302 goto error_and_exit;
303 }
304
305 if (file_size == 0)
306 {
307 free (input);
308 close (file);
309 return 0; /* don't waste time if we don't have to */
310 }
311
312 #ifdef HISTORY_USE_MMAP
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)
317 {
318 errno = overflow_errno;
319 goto error_and_exit;
320 }
321 chars_read = file_size;
322 #else
323 buffer = (char *)malloc (file_size + 1);
324 if (buffer == 0)
325 {
326 errno = overflow_errno;
327 goto error_and_exit;
328 }
329
330 chars_read = read (file, buffer, file_size);
331 #endif
332 if (chars_read < 0)
333 {
334 error_and_exit:
335 if (errno != 0)
336 chars_read = errno;
337 else
338 chars_read = EIO;
339 if (file >= 0)
340 close (file);
341
342 FREE (input);
343 #ifndef HISTORY_USE_MMAP
344 FREE (buffer);
345 #endif
346
347 return (chars_read);
348 }
349
350 close (file);
351
352 /* Set TO to larger than end of file if negative. */
353 if (to < 0)
354 to = chars_read;
355
356 /* Start at beginning of file, work to end. */
357 bufend = buffer + chars_read;
358 *bufend = '\0'; /* null-terminate buffer for timestamp checks */
359 current_line = 0;
360
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
374 /* Skip lines until we are at FROM. */
375 for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
376 if (*line_end == '\n')
377 {
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;
384 }
385
386 /* If there are lines left to gobble, then gobble them now. */
387 for (line_end = line_start; line_end < bufend; line_end++)
388 if (*line_end == '\n')
389 {
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';
393 else
394 *line_end = '\0';
395
396 if (*line_start)
397 {
398 if (HIST_TIMESTAMP_START(line_start) == 0)
399 {
400 if (last_ts == NULL && history_length > 0 && history_multiline_entries)
401 _hs_append_history_line (history_length - 1, line_start);
402 else
403 add_history (line_start);
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 }
416
417 current_line++;
418
419 if (current_line >= to)
420 break;
421
422 line_start = line_end + 1;
423 }
424
425 history_lines_read_from_file = current_line;
426 if (reset_comment_char)
427 history_comment_char = '\0';
428
429 FREE (input);
430 #ifndef HISTORY_USE_MMAP
431 FREE (buffer);
432 #else
433 munmap (buffer, file_size);
434 #endif
435
436 return (0);
437 }
438
439 /* Save FILENAME to BACK, handling case where FILENAME is a symlink
440 (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */
441 static int
442 histfile_backup (const char *filename, const char *back)
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) */
460 static int
461 histfile_restore (const char *backup, const char *orig)
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
477 /* Truncate the history file FNAME, leaving only LINES trailing lines.
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. */
480 int
481 history_truncate_file (const char *fname, int lines)
482 {
483 char *buffer, *filename, *tempname, *bp, *bp1; /* bp1 == bp+1 */
484 int file, chars_read, rv, orig_lines, exists, r;
485 struct stat finfo;
486 size_t file_size;
487
488 history_lines_written_to_file = 0;
489
490 buffer = (char *)NULL;
491 filename = history_filename (fname);
492 tempname = 0;
493 file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
494 rv = exists = 0;
495
496 /* Don't try to truncate non-regular files. */
497 if (file == -1 || fstat (file, &finfo) == -1)
498 {
499 rv = errno;
500 if (file != -1)
501 close (file);
502 goto truncate_exit;
503 }
504 exists = 1;
505
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 }
516
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)
524 rv = errno = EFBIG;
525 #elif defined (EOVERFLOW)
526 rv = errno = EOVERFLOW;
527 #else
528 rv = errno = EINVAL;
529 #endif
530 goto truncate_exit;
531 }
532
533 buffer = (char *)malloc (file_size + 1);
534 if (buffer == 0)
535 {
536 rv = errno;
537 close (file);
538 goto truncate_exit;
539 }
540
541 chars_read = read (file, buffer, file_size);
542 close (file);
543
544 if (chars_read <= 0)
545 {
546 rv = (chars_read < 0) ? errno : 0;
547 goto truncate_exit;
548 }
549
550 orig_lines = lines;
551 /* Count backwards from the end of buffer until we have passed
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--)
556 {
557 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
558 lines--;
559 bp1 = bp;
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. */
567 for ( ; bp > buffer; bp--)
568 {
569 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
570 {
571 bp++;
572 break;
573 }
574 bp1 = bp;
575 }
576
577 /* Write only if there are more lines in the file than we want to
578 truncate to. */
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)
590 {
591 if (write (file, bp, chars_read - (bp - buffer)) < 0)
592 rv = errno;
593
594 if (close (file) < 0 && rv == 0)
595 rv = errno;
596 }
597 else
598 rv = errno;
599
600 truncate_exit:
601 FREE (buffer);
602
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
615 #if defined (HAVE_CHOWN)
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);
622 #endif
623
624 xfree (filename);
625 FREE (tempname);
626
627 return rv;
628 }
629
630 /* Workhorse function for writing history. Writes the last NELEMENT entries
631 from the history list to FILENAME. OVERWRITE is non-zero if you
632 wish to replace FILENAME with the entries. */
633 static int
634 history_do_write (const char *filename, int nelements, int overwrite)
635 {
636 register int i;
637 char *output, *tempname, *histname;
638 int file, mode, rv, exists;
639 struct stat finfo;
640 #ifdef HISTORY_USE_MMAP
641 size_t cursize;
642
643 history_lines_written_to_file = 0;
644
645 mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
646 #else
647 mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
648 #endif
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
655 file = output ? open (output, mode, 0600) : -1;
656 rv = 0;
657
658 if (file == -1)
659 {
660 rv = errno;
661 FREE (histname);
662 FREE (tempname);
663 return (rv);
664 }
665
666 #ifdef HISTORY_USE_MMAP
667 cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
668 #endif
669
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++)
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
693
694 /* Allocate the buffer, and fill it. */
695 #ifdef HISTORY_USE_MMAP
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 {
701 mmap_error:
702 rv = errno;
703 close (file);
704 if (tempname)
705 unlink (tempname);
706 FREE (histname);
707 FREE (tempname);
708 return rv;
709 }
710 #else
711 buffer = (char *)malloc (buffer_size);
712 if (buffer == 0)
713 {
714 rv = errno;
715 close (file);
716 if (tempname)
717 unlink (tempname);
718 FREE (histname);
719 FREE (tempname);
720 return rv;
721 }
722 #endif
723
724 for (j = 0, i = history_length - nelements; i < history_length; i++)
725 {
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 }
732 strcpy (buffer + j, the_history[i]->line);
733 j += strlen (the_history[i]->line);
734 buffer[j++] = '\n';
735 }
736
737 #ifdef HISTORY_USE_MMAP
738 if (msync (buffer, buffer_size, MS_ASYNC) != 0 || munmap (buffer, buffer_size) != 0)
739 rv = errno;
740 #else
741 if (write (file, buffer, buffer_size) < 0)
742 rv = errno;
743 xfree (buffer);
744 #endif
745 }
746
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
762 #if defined (HAVE_CHOWN)
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);
769 #endif
770
771 FREE (histname);
772 FREE (tempname);
773
774 return (rv);
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. */
779 int
780 append_history (int nelements, const char *filename)
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 ().*/
788 int
789 write_history (const char *filename)
790 {
791 return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
792 }
This page took 0.058418 seconds and 4 git commands to generate.