xref: /openbsd-src/gnu/lib/libreadline/histfile.c (revision 9704b281e65e1189747652d0ba55eee892cff5f7)
11acd27e7Smillert /* histfile.c - functions to manipulate the history file. */
21acd27e7Smillert 
31acd27e7Smillert /* Copyright (C) 1989, 1992 Free Software Foundation, Inc.
41acd27e7Smillert 
51acd27e7Smillert    This file contains the GNU History Library (the Library), a set of
61acd27e7Smillert    routines for managing the text of previously typed lines.
71acd27e7Smillert 
81acd27e7Smillert    The Library is free software; you can redistribute it and/or modify
91acd27e7Smillert    it under the terms of the GNU General Public License as published by
101acd27e7Smillert    the Free Software Foundation; either version 2, or (at your option)
111acd27e7Smillert    any later version.
121acd27e7Smillert 
131acd27e7Smillert    The Library is distributed in the hope that it will be useful, but
141acd27e7Smillert    WITHOUT ANY WARRANTY; without even the implied warranty of
151acd27e7Smillert    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
161acd27e7Smillert    General Public License for more details.
171acd27e7Smillert 
181acd27e7Smillert    The GNU General Public License is often shipped with GNU software, and
191acd27e7Smillert    is generally kept in a file called COPYING or LICENSE.  If you do not
201acd27e7Smillert    have a copy of the license, write to the Free Software Foundation,
211acd27e7Smillert    59 Temple Place, Suite 330, Boston, MA 02111 USA. */
221acd27e7Smillert 
231acd27e7Smillert /* The goal is to make the implementation transparent, so that you
241acd27e7Smillert    don't have to know what data types are used, just what functions
251acd27e7Smillert    you can call.  I think I have done that. */
261acd27e7Smillert #define READLINE_LIBRARY
271acd27e7Smillert 
281acd27e7Smillert #if defined (HAVE_CONFIG_H)
291acd27e7Smillert #  include <config.h>
301acd27e7Smillert #endif
311acd27e7Smillert 
321acd27e7Smillert #include <stdio.h>
331acd27e7Smillert 
341acd27e7Smillert #include <sys/types.h>
351acd27e7Smillert #ifndef _MINIX
361acd27e7Smillert #  include <sys/file.h>
371acd27e7Smillert #endif
381acd27e7Smillert #include "posixstat.h"
391acd27e7Smillert #include <fcntl.h>
401acd27e7Smillert 
411acd27e7Smillert #if defined (HAVE_STDLIB_H)
421acd27e7Smillert #  include <stdlib.h>
431acd27e7Smillert #else
441acd27e7Smillert #  include "ansi_stdlib.h"
451acd27e7Smillert #endif /* HAVE_STDLIB_H */
461acd27e7Smillert 
471acd27e7Smillert #if defined (HAVE_UNISTD_H)
481acd27e7Smillert #  include <unistd.h>
491acd27e7Smillert #endif
501acd27e7Smillert 
51af70c2dfSkettenis #if defined (__EMX__) || defined (__CYGWIN__)
52af70c2dfSkettenis #  undef HAVE_MMAP
53af70c2dfSkettenis #endif
541acd27e7Smillert 
55af70c2dfSkettenis #ifdef HAVE_MMAP
56af70c2dfSkettenis #  include <sys/mman.h>
57af70c2dfSkettenis 
58af70c2dfSkettenis #  ifdef MAP_FILE
59af70c2dfSkettenis #    define MAP_RFLAGS	(MAP_FILE|MAP_PRIVATE)
60af70c2dfSkettenis #    define MAP_WFLAGS	(MAP_FILE|MAP_SHARED)
61af70c2dfSkettenis #  else
62af70c2dfSkettenis #    define MAP_RFLAGS	MAP_PRIVATE
63af70c2dfSkettenis #    define MAP_WFLAGS	MAP_SHARED
64af70c2dfSkettenis #  endif
65af70c2dfSkettenis 
66af70c2dfSkettenis #  ifndef MAP_FAILED
67af70c2dfSkettenis #    define MAP_FAILED	((void *)-1)
68af70c2dfSkettenis #  endif
69af70c2dfSkettenis 
70af70c2dfSkettenis #endif /* HAVE_MMAP */
711acd27e7Smillert 
721acd27e7Smillert /* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
731acd27e7Smillert    on win 95/98/nt), we want to open files with O_BINARY mode so that there
741acd27e7Smillert    is no \n -> \r\n conversion performed.  On other systems, we don't want to
751acd27e7Smillert    mess around with O_BINARY at all, so we ensure that it's defined to 0. */
761acd27e7Smillert #if defined (__EMX__) || defined (__CYGWIN__)
771acd27e7Smillert #  ifndef O_BINARY
781acd27e7Smillert #    define O_BINARY 0
791acd27e7Smillert #  endif
801acd27e7Smillert #else /* !__EMX__ && !__CYGWIN__ */
811acd27e7Smillert #  undef O_BINARY
821acd27e7Smillert #  define O_BINARY 0
831acd27e7Smillert #endif /* !__EMX__ && !__CYGWIN__ */
841acd27e7Smillert 
851acd27e7Smillert #include <errno.h>
861acd27e7Smillert #if !defined (errno)
871acd27e7Smillert extern int errno;
881acd27e7Smillert #endif /* !errno */
891acd27e7Smillert 
901acd27e7Smillert #include "history.h"
911acd27e7Smillert #include "histlib.h"
921acd27e7Smillert 
931acd27e7Smillert #include "rlshell.h"
941acd27e7Smillert #include "xmalloc.h"
951acd27e7Smillert 
961acd27e7Smillert /* Return the string that should be used in the place of this
971acd27e7Smillert    filename.  This only matters when you don't specify the
981acd27e7Smillert    filename to read_history (), or write_history (). */
991acd27e7Smillert static char *
history_filename(filename)1001acd27e7Smillert history_filename (filename)
101af70c2dfSkettenis      const char *filename;
1021acd27e7Smillert {
103af70c2dfSkettenis   char *return_val;
104af70c2dfSkettenis   const char *home;
1051acd27e7Smillert   int home_len;
10663bef317Sbeck   char dot;
1071acd27e7Smillert 
1081acd27e7Smillert   return_val = filename ? savestring (filename) : (char *)NULL;
1091acd27e7Smillert 
1101acd27e7Smillert   if (return_val)
1111acd27e7Smillert     return (return_val);
1121acd27e7Smillert 
113af70c2dfSkettenis   home = sh_get_env_value ("HOME");
1141acd27e7Smillert 
11583295b13Smillert   if (home == 0 || *home == '\0') {
11683295b13Smillert     errno = ENOENT;
11775dce849Smillert     return (NULL);
11883295b13Smillert   }
1191acd27e7Smillert   home_len = strlen (home);
1201acd27e7Smillert 
1211acd27e7Smillert #if defined (__MSDOS__)
12263bef317Sbeck   dot = '_';
1231acd27e7Smillert #else
12463bef317Sbeck   dot = '.';
1251acd27e7Smillert #endif
12663bef317Sbeck   if (asprintf(&return_val, "%s/%c%s", home, dot, "history") == -1)
12763bef317Sbeck 	  memory_error_and_abort("asprintf");
1281acd27e7Smillert   return (return_val);
1291acd27e7Smillert }
1301acd27e7Smillert 
1311acd27e7Smillert /* Add the contents of FILENAME to the history list, a line at a time.
1321acd27e7Smillert    If FILENAME is NULL, then read from ~/.history.  Returns 0 if
1331acd27e7Smillert    successful, or errno if not. */
1341acd27e7Smillert int
read_history(filename)1351acd27e7Smillert read_history (filename)
136af70c2dfSkettenis      const char *filename;
1371acd27e7Smillert {
1381acd27e7Smillert   return (read_history_range (filename, 0, -1));
1391acd27e7Smillert }
1401acd27e7Smillert 
1411acd27e7Smillert /* Read a range of lines from FILENAME, adding them to the history list.
1421acd27e7Smillert    Start reading at the FROM'th line and end at the TO'th.  If FROM
1431acd27e7Smillert    is zero, start at the beginning.  If TO is less than FROM, read
1441acd27e7Smillert    until the end of the file.  If FILENAME is NULL, then read from
1451acd27e7Smillert    ~/.history.  Returns 0 if successful, or errno if not. */
1461acd27e7Smillert int
read_history_range(filename,from,to)1471acd27e7Smillert read_history_range (filename, from, to)
148af70c2dfSkettenis      const char *filename;
1491acd27e7Smillert      int from, to;
1501acd27e7Smillert {
151af70c2dfSkettenis   register char *line_start, *line_end;
152af70c2dfSkettenis   char *input, *buffer, *bufend;
1531acd27e7Smillert   int file, current_line, chars_read;
1541acd27e7Smillert   struct stat finfo;
1551acd27e7Smillert   size_t file_size;
1561acd27e7Smillert 
1571acd27e7Smillert   buffer = (char *)NULL;
15883295b13Smillert   if ((input = history_filename (filename)))
1591acd27e7Smillert     file = open (input, O_RDONLY|O_BINARY, 0666);
16083295b13Smillert   else
16183295b13Smillert     file = -1;
1621acd27e7Smillert 
1631acd27e7Smillert   if ((file < 0) || (fstat (file, &finfo) == -1))
1641acd27e7Smillert     goto error_and_exit;
1651acd27e7Smillert 
1661acd27e7Smillert   file_size = (size_t)finfo.st_size;
1671acd27e7Smillert 
1681acd27e7Smillert   /* check for overflow on very large files */
1691acd27e7Smillert   if (file_size != finfo.st_size || file_size + 1 < file_size)
1701acd27e7Smillert     {
1711acd27e7Smillert #if defined (EFBIG)
1721acd27e7Smillert       errno = EFBIG;
173af70c2dfSkettenis #elif defined (EOVERFLOW)
174af70c2dfSkettenis       errno = EOVERFLOW;
1751acd27e7Smillert #endif
1761acd27e7Smillert       goto error_and_exit;
1771acd27e7Smillert     }
1781acd27e7Smillert 
179af70c2dfSkettenis #ifdef HAVE_MMAP
180af70c2dfSkettenis   /* We map read/write and private so we can change newlines to NULs without
181af70c2dfSkettenis      affecting the underlying object. */
182af70c2dfSkettenis   buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
183af70c2dfSkettenis   if ((void *)buffer == MAP_FAILED)
184af70c2dfSkettenis     goto error_and_exit;
185af70c2dfSkettenis   chars_read = file_size;
186af70c2dfSkettenis #else
187af70c2dfSkettenis   buffer = (char *)malloc (file_size + 1);
188af70c2dfSkettenis   if (buffer == 0)
189af70c2dfSkettenis     goto error_and_exit;
1901acd27e7Smillert 
1911acd27e7Smillert   chars_read = read (file, buffer, file_size);
192af70c2dfSkettenis #endif
1931acd27e7Smillert   if (chars_read < 0)
1941acd27e7Smillert     {
1951acd27e7Smillert   error_and_exit:
196af70c2dfSkettenis       chars_read = errno;
1971acd27e7Smillert       if (file >= 0)
1981acd27e7Smillert 	close (file);
1991acd27e7Smillert 
2001acd27e7Smillert       FREE (input);
201af70c2dfSkettenis #ifndef HAVE_MMAP
2021acd27e7Smillert       FREE (buffer);
203af70c2dfSkettenis #endif
2041acd27e7Smillert 
205af70c2dfSkettenis       return (chars_read);
2061acd27e7Smillert     }
2071acd27e7Smillert 
2081acd27e7Smillert   close (file);
2091acd27e7Smillert 
2101acd27e7Smillert   /* Set TO to larger than end of file if negative. */
2111acd27e7Smillert   if (to < 0)
2121acd27e7Smillert     to = chars_read;
2131acd27e7Smillert 
2141acd27e7Smillert   /* Start at beginning of file, work to end. */
215af70c2dfSkettenis   bufend = buffer + chars_read;
216af70c2dfSkettenis   current_line = 0;
2171acd27e7Smillert 
2181acd27e7Smillert   /* Skip lines until we are at FROM. */
219af70c2dfSkettenis   for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
220af70c2dfSkettenis     if (*line_end == '\n')
2211acd27e7Smillert       {
2221acd27e7Smillert 	current_line++;
2231acd27e7Smillert 	line_start = line_end + 1;
2241acd27e7Smillert       }
2251acd27e7Smillert 
2261acd27e7Smillert   /* If there are lines left to gobble, then gobble them now. */
227af70c2dfSkettenis   for (line_end = line_start; line_end < bufend; line_end++)
228af70c2dfSkettenis     if (*line_end == '\n')
2291acd27e7Smillert       {
230af70c2dfSkettenis 	*line_end = '\0';
2311acd27e7Smillert 
232af70c2dfSkettenis 	if (*line_start)
233af70c2dfSkettenis 	  add_history (line_start);
2341acd27e7Smillert 
2351acd27e7Smillert 	current_line++;
2361acd27e7Smillert 
2371acd27e7Smillert 	if (current_line >= to)
2381acd27e7Smillert 	  break;
2391acd27e7Smillert 
2401acd27e7Smillert 	line_start = line_end + 1;
2411acd27e7Smillert       }
2421acd27e7Smillert 
2431acd27e7Smillert   FREE (input);
244af70c2dfSkettenis #ifndef HAVE_MMAP
2451acd27e7Smillert   FREE (buffer);
246af70c2dfSkettenis #else
247af70c2dfSkettenis   munmap (buffer, file_size);
248af70c2dfSkettenis #endif
2491acd27e7Smillert 
2501acd27e7Smillert   return (0);
2511acd27e7Smillert }
2521acd27e7Smillert 
2531acd27e7Smillert /* Truncate the history file FNAME, leaving only LINES trailing lines.
254af70c2dfSkettenis    If FNAME is NULL, then use ~/.history.  Returns 0 on success, errno
255af70c2dfSkettenis    on failure. */
2561acd27e7Smillert int
history_truncate_file(fname,lines)2571acd27e7Smillert history_truncate_file (fname, lines)
258af70c2dfSkettenis      const char *fname;
2591acd27e7Smillert      int lines;
2601acd27e7Smillert {
261af70c2dfSkettenis   char *buffer, *filename, *bp;
262af70c2dfSkettenis   int file, chars_read, rv;
2631acd27e7Smillert   struct stat finfo;
2641acd27e7Smillert   size_t file_size;
2651acd27e7Smillert 
2661acd27e7Smillert   buffer = (char *)NULL;
26783295b13Smillert   if ((filename = history_filename (fname)))
2681acd27e7Smillert     file = open (filename, O_RDONLY|O_BINARY, 0666);
26983295b13Smillert   else
27083295b13Smillert     file = -1;
271af70c2dfSkettenis   rv = 0;
2721acd27e7Smillert 
2731acd27e7Smillert   /* Don't try to truncate non-regular files. */
274af70c2dfSkettenis   if (file == -1 || fstat (file, &finfo) == -1)
275af70c2dfSkettenis     {
276af70c2dfSkettenis       rv = errno;
277af70c2dfSkettenis       if (file != -1)
278af70c2dfSkettenis 	close (file);
2791acd27e7Smillert       goto truncate_exit;
280af70c2dfSkettenis     }
281af70c2dfSkettenis 
282af70c2dfSkettenis   if (S_ISREG (finfo.st_mode) == 0)
283af70c2dfSkettenis     {
284af70c2dfSkettenis       close (file);
285af70c2dfSkettenis #ifdef EFTYPE
286af70c2dfSkettenis       rv = EFTYPE;
287af70c2dfSkettenis #else
288af70c2dfSkettenis       rv = EINVAL;
289af70c2dfSkettenis #endif
290af70c2dfSkettenis       goto truncate_exit;
291af70c2dfSkettenis     }
2921acd27e7Smillert 
2931acd27e7Smillert   file_size = (size_t)finfo.st_size;
2941acd27e7Smillert 
2951acd27e7Smillert   /* check for overflow on very large files */
2961acd27e7Smillert   if (file_size != finfo.st_size || file_size + 1 < file_size)
2971acd27e7Smillert     {
2981acd27e7Smillert       close (file);
2991acd27e7Smillert #if defined (EFBIG)
300af70c2dfSkettenis       rv = errno = EFBIG;
301af70c2dfSkettenis #elif defined (EOVERFLOW)
302af70c2dfSkettenis       rv = errno = EOVERFLOW;
303af70c2dfSkettenis #else
304af70c2dfSkettenis       rv = errno = EINVAL;
3051acd27e7Smillert #endif
3061acd27e7Smillert       goto truncate_exit;
3071acd27e7Smillert     }
3081acd27e7Smillert 
309af70c2dfSkettenis   buffer = (char *)malloc (file_size + 1);
310af70c2dfSkettenis   if (buffer == 0)
311af70c2dfSkettenis     {
312af70c2dfSkettenis       close (file);
313af70c2dfSkettenis       goto truncate_exit;
314af70c2dfSkettenis     }
315af70c2dfSkettenis 
3161acd27e7Smillert   chars_read = read (file, buffer, file_size);
3171acd27e7Smillert   close (file);
3181acd27e7Smillert 
3191acd27e7Smillert   if (chars_read <= 0)
320af70c2dfSkettenis     {
321af70c2dfSkettenis       rv = (chars_read < 0) ? errno : 0;
3221acd27e7Smillert       goto truncate_exit;
323af70c2dfSkettenis     }
3241acd27e7Smillert 
3251acd27e7Smillert   /* Count backwards from the end of buffer until we have passed
3261acd27e7Smillert      LINES lines. */
327af70c2dfSkettenis   for (bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
3281acd27e7Smillert     {
329af70c2dfSkettenis       if (*bp == '\n')
3301acd27e7Smillert 	lines--;
3311acd27e7Smillert     }
3321acd27e7Smillert 
3331acd27e7Smillert   /* If this is the first line, then the file contains exactly the
3341acd27e7Smillert      number of lines we want to truncate to, so we don't need to do
3351acd27e7Smillert      anything.  It's the first line if we don't find a newline between
3361acd27e7Smillert      the current value of i and 0.  Otherwise, write from the start of
3371acd27e7Smillert      this line until the end of the buffer. */
338af70c2dfSkettenis   for ( ; bp > buffer; bp--)
339af70c2dfSkettenis     if (*bp == '\n')
3401acd27e7Smillert       {
341af70c2dfSkettenis 	bp++;
3421acd27e7Smillert 	break;
3431acd27e7Smillert       }
3441acd27e7Smillert 
3451acd27e7Smillert   /* Write only if there are more lines in the file than we want to
3461acd27e7Smillert      truncate to. */
347af70c2dfSkettenis   if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
3481acd27e7Smillert     {
349af70c2dfSkettenis       write (file, bp, chars_read - (bp - buffer));
3501acd27e7Smillert 
3511acd27e7Smillert #if defined (__BEOS__)
3521acd27e7Smillert       /* BeOS ignores O_TRUNC. */
353af70c2dfSkettenis       ftruncate (file, chars_read - (bp - buffer));
3541acd27e7Smillert #endif
3551acd27e7Smillert 
3561acd27e7Smillert       close (file);
3571acd27e7Smillert     }
3581acd27e7Smillert 
3591acd27e7Smillert  truncate_exit:
3601acd27e7Smillert 
3611acd27e7Smillert   FREE (buffer);
3621acd27e7Smillert 
3631acd27e7Smillert   free (filename);
364af70c2dfSkettenis   return rv;
3651acd27e7Smillert }
3661acd27e7Smillert 
3671acd27e7Smillert /* Workhorse function for writing history.  Writes NELEMENT entries
3681acd27e7Smillert    from the history list to FILENAME.  OVERWRITE is non-zero if you
3691acd27e7Smillert    wish to replace FILENAME with the entries. */
3701acd27e7Smillert static int
history_do_write(filename,nelements,overwrite)3711acd27e7Smillert history_do_write (filename, nelements, overwrite)
372af70c2dfSkettenis      const char *filename;
3731acd27e7Smillert      int nelements, overwrite;
3741acd27e7Smillert {
3751acd27e7Smillert   register int i;
3761acd27e7Smillert   char *output;
377af70c2dfSkettenis   int file, mode, rv;
378*a7d4f26aSkrw #ifdef HAVE_MMAP
379af70c2dfSkettenis   size_t cursize;
380*a7d4f26aSkrw #endif
3811acd27e7Smillert 
382af70c2dfSkettenis #ifdef HAVE_MMAP
383af70c2dfSkettenis   mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
384af70c2dfSkettenis #else
3851acd27e7Smillert   mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
386af70c2dfSkettenis #endif
3871acd27e7Smillert   output = history_filename (filename);
388af70c2dfSkettenis   rv = 0;
3891acd27e7Smillert 
39083295b13Smillert   if (!output || (file = open (output, mode, 0600)) == -1)
3911acd27e7Smillert     {
3921acd27e7Smillert       FREE (output);
3931acd27e7Smillert       return (errno);
3941acd27e7Smillert     }
3951acd27e7Smillert 
396af70c2dfSkettenis #ifdef HAVE_MMAP
397af70c2dfSkettenis   cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
398af70c2dfSkettenis #endif
399af70c2dfSkettenis 
4001acd27e7Smillert   if (nelements > history_length)
4011acd27e7Smillert     nelements = history_length;
4021acd27e7Smillert 
4031acd27e7Smillert   /* Build a buffer of all the lines to write, and write them in one syscall.
4041acd27e7Smillert      Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
4051acd27e7Smillert   {
4061acd27e7Smillert     HIST_ENTRY **the_history;	/* local */
4071acd27e7Smillert     int buffer_size;
4081acd27e7Smillert     char *buffer;
4091acd27e7Smillert 
4101acd27e7Smillert     the_history = history_list ();
4111acd27e7Smillert     /* Calculate the total number of bytes to write. */
412d9924ce1Sotto     for (buffer_size = 1, i = history_length - nelements; i < history_length; i++)
4131acd27e7Smillert       buffer_size += 1 + strlen (the_history[i]->line);
4141acd27e7Smillert 
4151acd27e7Smillert     /* Allocate the buffer, and fill it. */
416af70c2dfSkettenis #ifdef HAVE_MMAP
417af70c2dfSkettenis     if (ftruncate (file, buffer_size+cursize) == -1)
418af70c2dfSkettenis       goto mmap_error;
419af70c2dfSkettenis     buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
420af70c2dfSkettenis     if ((void *)buffer == MAP_FAILED)
421af70c2dfSkettenis       {
422af70c2dfSkettenis mmap_error:
423af70c2dfSkettenis 	rv = errno;
424af70c2dfSkettenis 	FREE (output);
425af70c2dfSkettenis 	close (file);
426af70c2dfSkettenis 	return rv;
427af70c2dfSkettenis       }
428af70c2dfSkettenis #else
429af70c2dfSkettenis     buffer = (char *)malloc (buffer_size);
430af70c2dfSkettenis     if (buffer == 0)
431af70c2dfSkettenis       {
432af70c2dfSkettenis 	rv = errno;
433af70c2dfSkettenis 	FREE (output);
434af70c2dfSkettenis 	close (file);
435af70c2dfSkettenis 	return rv;
436af70c2dfSkettenis       }
437af70c2dfSkettenis #endif
43863bef317Sbeck     buffer[0] = '\0';
4391acd27e7Smillert 
44063bef317Sbeck     for (i = history_length - nelements; i < history_length; i++)
4411acd27e7Smillert       {
44263bef317Sbeck 	strlcat (buffer, the_history[i]->line, buffer_size);
44363bef317Sbeck 	strlcat (buffer, "\n", buffer_size);
4441acd27e7Smillert       }
4451acd27e7Smillert 
446af70c2dfSkettenis #ifdef HAVE_MMAP
447af70c2dfSkettenis     if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0)
448af70c2dfSkettenis       rv = errno;
449af70c2dfSkettenis #else
450af70c2dfSkettenis     if (write (file, buffer, buffer_size - 1) < 0)
451af70c2dfSkettenis       rv = errno;
4521acd27e7Smillert     free (buffer);
453af70c2dfSkettenis #endif
4541acd27e7Smillert   }
4551acd27e7Smillert 
4561acd27e7Smillert   close (file);
4571acd27e7Smillert 
4581acd27e7Smillert   FREE (output);
4591acd27e7Smillert 
460af70c2dfSkettenis   return (rv);
4611acd27e7Smillert }
4621acd27e7Smillert 
4631acd27e7Smillert /* Append NELEMENT entries to FILENAME.  The entries appended are from
4641acd27e7Smillert    the end of the list minus NELEMENTs up to the end of the list. */
4651acd27e7Smillert int
append_history(nelements,filename)4661acd27e7Smillert append_history (nelements, filename)
4671acd27e7Smillert      int nelements;
468af70c2dfSkettenis      const char *filename;
4691acd27e7Smillert {
4701acd27e7Smillert   return (history_do_write (filename, nelements, HISTORY_APPEND));
4711acd27e7Smillert }
4721acd27e7Smillert 
4731acd27e7Smillert /* Overwrite FILENAME with the current history.  If FILENAME is NULL,
4741acd27e7Smillert    then write the history list to ~/.history.  Values returned
4751acd27e7Smillert    are as in read_history ().*/
4761acd27e7Smillert int
write_history(filename)4771acd27e7Smillert write_history (filename)
478af70c2dfSkettenis      const char *filename;
4791acd27e7Smillert {
4801acd27e7Smillert   return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
4811acd27e7Smillert }
482