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