dc64bde1c5acf8c117080eb7c783a2cd3181311d
[deliverable/binutils-gdb.git] / 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 return 0; /* don't waste time if we don't have to */
309 }
310
311 #ifdef HISTORY_USE_MMAP
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)
316 {
317 errno = overflow_errno;
318 goto error_and_exit;
319 }
320 chars_read = file_size;
321 #else
322 buffer = (char *)malloc (file_size + 1);
323 if (buffer == 0)
324 {
325 errno = overflow_errno;
326 goto error_and_exit;
327 }
328
329 chars_read = read (file, buffer, file_size);
330 #endif
331 if (chars_read < 0)
332 {
333 error_and_exit:
334 if (errno != 0)
335 chars_read = errno;
336 else
337 chars_read = EIO;
338 if (file >= 0)
339 close (file);
340
341 FREE (input);
342 #ifndef HISTORY_USE_MMAP
343 FREE (buffer);
344 #endif
345
346 return (chars_read);
347 }
348
349 close (file);
350
351 /* Set TO to larger than end of file if negative. */
352 if (to < 0)
353 to = chars_read;
354
355 /* Start at beginning of file, work to end. */
356 bufend = buffer + chars_read;
357 *bufend = '\0'; /* null-terminate buffer for timestamp checks */
358 current_line = 0;
359
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
373 /* Skip lines until we are at FROM. */
374 for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
375 if (*line_end == '\n')
376 {
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;
383 }
384
385 /* If there are lines left to gobble, then gobble them now. */
386 for (line_end = line_start; line_end < bufend; line_end++)
387 if (*line_end == '\n')
388 {
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';
392 else
393 *line_end = '\0';
394
395 if (*line_start)
396 {
397 if (HIST_TIMESTAMP_START(line_start) == 0)
398 {
399 if (last_ts == NULL && history_length > 0 && history_multiline_entries)
400 _hs_append_history_line (history_length - 1, line_start);
401 else
402 add_history (line_start);
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 }
415
416 current_line++;
417
418 if (current_line >= to)
419 break;
420
421 line_start = line_end + 1;
422 }
423
424 history_lines_read_from_file = current_line;
425 if (reset_comment_char)
426 history_comment_char = '\0';
427
428 FREE (input);
429 #ifndef HISTORY_USE_MMAP
430 FREE (buffer);
431 #else
432 munmap (buffer, file_size);
433 #endif
434
435 return (0);
436 }
437
438 /* Save FILENAME to BACK, handling case where FILENAME is a symlink
439 (e.g., ~/.bash_history -> .histfiles/.bash_history.$HOSTNAME) */
440 static int
441 histfile_backup (const char *filename, const char *back)
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) */
459 static int
460 histfile_restore (const char *backup, const char *orig)
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
476 /* Truncate the history file FNAME, leaving only LINES trailing lines.
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. */
479 int
480 history_truncate_file (const char *fname, int lines)
481 {
482 char *buffer, *filename, *tempname, *bp, *bp1; /* bp1 == bp+1 */
483 int file, chars_read, rv, orig_lines, exists, r;
484 struct stat finfo;
485 size_t file_size;
486
487 history_lines_written_to_file = 0;
488
489 buffer = (char *)NULL;
490 filename = history_filename (fname);
491 tempname = 0;
492 file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
493 rv = exists = 0;
494
495 /* Don't try to truncate non-regular files. */
496 if (file == -1 || fstat (file, &finfo) == -1)
497 {
498 rv = errno;
499 if (file != -1)
500 close (file);
501 goto truncate_exit;
502 }
503 exists = 1;
504
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 }
515
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)
523 rv = errno = EFBIG;
524 #elif defined (EOVERFLOW)
525 rv = errno = EOVERFLOW;
526 #else
527 rv = errno = EINVAL;
528 #endif
529 goto truncate_exit;
530 }
531
532 buffer = (char *)malloc (file_size + 1);
533 if (buffer == 0)
534 {
535 rv = errno;
536 close (file);
537 goto truncate_exit;
538 }
539
540 chars_read = read (file, buffer, file_size);
541 close (file);
542
543 if (chars_read <= 0)
544 {
545 rv = (chars_read < 0) ? errno : 0;
546 goto truncate_exit;
547 }
548
549 orig_lines = lines;
550 /* Count backwards from the end of buffer until we have passed
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--)
555 {
556 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
557 lines--;
558 bp1 = bp;
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. */
566 for ( ; bp > buffer; bp--)
567 {
568 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
569 {
570 bp++;
571 break;
572 }
573 bp1 = bp;
574 }
575
576 /* Write only if there are more lines in the file than we want to
577 truncate to. */
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)
589 {
590 if (write (file, bp, chars_read - (bp - buffer)) < 0)
591 rv = errno;
592
593 if (close (file) < 0 && rv == 0)
594 rv = errno;
595 }
596 else
597 rv = errno;
598
599 truncate_exit:
600 FREE (buffer);
601
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
614 #if defined (HAVE_CHOWN)
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);
621 #endif
622
623 xfree (filename);
624 FREE (tempname);
625
626 return rv;
627 }
628
629 /* Workhorse function for writing history. Writes the last NELEMENT entries
630 from the history list to FILENAME. OVERWRITE is non-zero if you
631 wish to replace FILENAME with the entries. */
632 static int
633 history_do_write (const char *filename, int nelements, int overwrite)
634 {
635 register int i;
636 char *output, *tempname, *histname;
637 int file, mode, rv, exists;
638 struct stat finfo;
639 #ifdef HISTORY_USE_MMAP
640 size_t cursize;
641
642 history_lines_written_to_file = 0;
643
644 mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
645 #else
646 mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
647 #endif
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
654 file = output ? open (output, mode, 0600) : -1;
655 rv = 0;
656
657 if (file == -1)
658 {
659 rv = errno;
660 FREE (histname);
661 FREE (tempname);
662 return (rv);
663 }
664
665 #ifdef HISTORY_USE_MMAP
666 cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
667 #endif
668
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++)
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
692
693 /* Allocate the buffer, and fill it. */
694 #ifdef HISTORY_USE_MMAP
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 {
700 mmap_error:
701 rv = errno;
702 close (file);
703 if (tempname)
704 unlink (tempname);
705 FREE (histname);
706 FREE (tempname);
707 return rv;
708 }
709 #else
710 buffer = (char *)malloc (buffer_size);
711 if (buffer == 0)
712 {
713 rv = errno;
714 close (file);
715 if (tempname)
716 unlink (tempname);
717 FREE (histname);
718 FREE (tempname);
719 return rv;
720 }
721 #endif
722
723 for (j = 0, i = history_length - nelements; i < history_length; i++)
724 {
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 }
731 strcpy (buffer + j, the_history[i]->line);
732 j += strlen (the_history[i]->line);
733 buffer[j++] = '\n';
734 }
735
736 #ifdef HISTORY_USE_MMAP
737 if (msync (buffer, buffer_size, MS_ASYNC) != 0 || munmap (buffer, buffer_size) != 0)
738 rv = errno;
739 #else
740 if (write (file, buffer, buffer_size) < 0)
741 rv = errno;
742 xfree (buffer);
743 #endif
744 }
745
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
761 #if defined (HAVE_CHOWN)
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);
768 #endif
769
770 FREE (histname);
771 FREE (tempname);
772
773 return (rv);
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. */
778 int
779 append_history (int nelements, const char *filename)
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 ().*/
787 int
788 write_history (const char *filename)
789 {
790 return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
791 }
This page took 0.067333 seconds and 3 git commands to generate.