Add "make pdf" and "make install-pdf", from Brooks Moses
[deliverable/binutils-gdb.git] / readline / histfile.c
CommitLineData
d60d9f65
SS
1/* histfile.c - functions to manipulate the history file. */
2
5bdf8622 3/* Copyright (C) 1989-2003 Free Software Foundation, Inc.
d60d9f65
SS
4
5 This file contains the GNU History Library (the Library), a set of
6 routines for managing the text of previously typed lines.
7
8 The Library is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
1b17e766 10 the Free Software Foundation; either version 2, or (at your option)
d60d9f65
SS
11 any later version.
12
13 The Library is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 The GNU General Public License is often shipped with GNU software, and
19 is generally kept in a file called COPYING or LICENSE. If you do not
20 have a copy of the license, write to the Free Software Foundation,
1b17e766 21 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
d60d9f65
SS
22
23/* The goal is to make the implementation transparent, so that you
24 don't have to know what data types are used, just what functions
25 you can call. I think I have done that. */
5bdf8622 26
d60d9f65
SS
27#define READLINE_LIBRARY
28
5bdf8622
DJ
29#if defined (__TANDEM)
30# include <floss.h>
31#endif
32
d60d9f65
SS
33#if defined (HAVE_CONFIG_H)
34# include <config.h>
35#endif
36
37#include <stdio.h>
38
39#include <sys/types.h>
5bdf8622 40#if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H)
d60d9f65
SS
41# include <sys/file.h>
42#endif
1b17e766 43#include "posixstat.h"
d60d9f65
SS
44#include <fcntl.h>
45
46#if defined (HAVE_STDLIB_H)
47# include <stdlib.h>
48#else
49# include "ansi_stdlib.h"
50#endif /* HAVE_STDLIB_H */
51
52#if defined (HAVE_UNISTD_H)
53# include <unistd.h>
54#endif
55
9255ee31
EZ
56#if defined (__EMX__) || defined (__CYGWIN__)
57# undef HAVE_MMAP
58#endif
59
5bdf8622 60#ifdef HISTORY_USE_MMAP
9255ee31
EZ
61# include <sys/mman.h>
62
63# ifdef MAP_FILE
64# define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE)
65# define MAP_WFLAGS (MAP_FILE|MAP_SHARED)
66# else
67# define MAP_RFLAGS MAP_PRIVATE
68# define MAP_WFLAGS MAP_SHARED
69# endif
70
71# ifndef MAP_FAILED
72# define MAP_FAILED ((void *)-1)
73# endif
d60d9f65 74
5bdf8622 75#endif /* HISTORY_USE_MMAP */
1b17e766
EZ
76
77/* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
78 on win 95/98/nt), we want to open files with O_BINARY mode so that there
79 is no \n -> \r\n conversion performed. On other systems, we don't want to
80 mess around with O_BINARY at all, so we ensure that it's defined to 0. */
81#if defined (__EMX__) || defined (__CYGWIN__)
d60d9f65
SS
82# ifndef O_BINARY
83# define O_BINARY 0
84# endif
1b17e766 85#else /* !__EMX__ && !__CYGWIN__ */
d60d9f65
SS
86# undef O_BINARY
87# define O_BINARY 0
1b17e766 88#endif /* !__EMX__ && !__CYGWIN__ */
d60d9f65
SS
89
90#include <errno.h>
91#if !defined (errno)
92extern int errno;
93#endif /* !errno */
94
95#include "history.h"
96#include "histlib.h"
97
1b17e766
EZ
98#include "rlshell.h"
99#include "xmalloc.h"
d60d9f65 100
5bdf8622
DJ
101/* If non-zero, we write timestamps to the history file in history_do_write() */
102int history_write_timestamps = 0;
103
104/* Does S look like the beginning of a history timestamp entry? Placeholder
105 for more extensive tests. */
106#define HIST_TIMESTAMP_START(s) (*(s) == history_comment_char)
107
d60d9f65
SS
108/* Return the string that should be used in the place of this
109 filename. This only matters when you don't specify the
110 filename to read_history (), or write_history (). */
111static char *
112history_filename (filename)
9255ee31 113 const char *filename;
d60d9f65 114{
9255ee31
EZ
115 char *return_val;
116 const char *home;
d60d9f65
SS
117 int home_len;
118
119 return_val = filename ? savestring (filename) : (char *)NULL;
120
121 if (return_val)
122 return (return_val);
123
9255ee31 124 home = sh_get_env_value ("HOME");
d60d9f65
SS
125
126 if (home == 0)
127 {
128 home = ".";
129 home_len = 1;
130 }
131 else
132 home_len = strlen (home);
133
9255ee31 134 return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
d60d9f65
SS
135 strcpy (return_val, home);
136 return_val[home_len] = '/';
1b17e766
EZ
137#if defined (__MSDOS__)
138 strcpy (return_val + home_len + 1, "_history");
139#else
d60d9f65 140 strcpy (return_val + home_len + 1, ".history");
1b17e766 141#endif
d60d9f65
SS
142
143 return (return_val);
144}
145
146/* Add the contents of FILENAME to the history list, a line at a time.
147 If FILENAME is NULL, then read from ~/.history. Returns 0 if
148 successful, or errno if not. */
149int
150read_history (filename)
9255ee31 151 const char *filename;
d60d9f65
SS
152{
153 return (read_history_range (filename, 0, -1));
154}
155
156/* Read a range of lines from FILENAME, adding them to the history list.
157 Start reading at the FROM'th line and end at the TO'th. If FROM
158 is zero, start at the beginning. If TO is less than FROM, read
159 until the end of the file. If FILENAME is NULL, then read from
160 ~/.history. Returns 0 if successful, or errno if not. */
161int
162read_history_range (filename, from, to)
9255ee31 163 const char *filename;
d60d9f65
SS
164 int from, to;
165{
5bdf8622
DJ
166 register char *line_start, *line_end, *p;
167 char *input, *buffer, *bufend, *last_ts;
1b17e766 168 int file, current_line, chars_read;
d60d9f65
SS
169 struct stat finfo;
170 size_t file_size;
5bdf8622
DJ
171#if defined (EFBIG)
172 int overflow_errno = EFBIG;
173#elif defined (EOVERFLOW)
174 int overflow_errno = EOVERFLOW;
175#else
176 int overflow_errno = EIO;
177#endif
d60d9f65 178
5bdf8622 179 buffer = last_ts = (char *)NULL;
d60d9f65
SS
180 input = history_filename (filename);
181 file = open (input, O_RDONLY|O_BINARY, 0666);
182
183 if ((file < 0) || (fstat (file, &finfo) == -1))
184 goto error_and_exit;
185
186 file_size = (size_t)finfo.st_size;
187
188 /* check for overflow on very large files */
189 if (file_size != finfo.st_size || file_size + 1 < file_size)
190 {
5bdf8622 191 errno = overflow_errno;
d60d9f65
SS
192 goto error_and_exit;
193 }
194
5bdf8622 195#ifdef HISTORY_USE_MMAP
9255ee31
EZ
196 /* We map read/write and private so we can change newlines to NULs without
197 affecting the underlying object. */
198 buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
199 if ((void *)buffer == MAP_FAILED)
5bdf8622
DJ
200 {
201 errno = overflow_errno;
202 goto error_and_exit;
203 }
9255ee31
EZ
204 chars_read = file_size;
205#else
206 buffer = (char *)malloc (file_size + 1);
207 if (buffer == 0)
5bdf8622
DJ
208 {
209 errno = overflow_errno;
210 goto error_and_exit;
211 }
1b17e766
EZ
212
213 chars_read = read (file, buffer, file_size);
9255ee31 214#endif
1b17e766 215 if (chars_read < 0)
d60d9f65
SS
216 {
217 error_and_exit:
5bdf8622
DJ
218 if (errno != 0)
219 chars_read = errno;
220 else
221 chars_read = EIO;
d60d9f65
SS
222 if (file >= 0)
223 close (file);
224
225 FREE (input);
5bdf8622 226#ifndef HISTORY_USE_MMAP
d60d9f65 227 FREE (buffer);
9255ee31 228#endif
d60d9f65 229
9255ee31 230 return (chars_read);
d60d9f65
SS
231 }
232
233 close (file);
234
235 /* Set TO to larger than end of file if negative. */
236 if (to < 0)
1b17e766 237 to = chars_read;
d60d9f65
SS
238
239 /* Start at beginning of file, work to end. */
9255ee31
EZ
240 bufend = buffer + chars_read;
241 current_line = 0;
d60d9f65
SS
242
243 /* Skip lines until we are at FROM. */
9255ee31
EZ
244 for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
245 if (*line_end == '\n')
246 {
5bdf8622
DJ
247 p = line_end + 1;
248 /* If we see something we think is a timestamp, continue with this
249 line. We should check more extensively here... */
250 if (HIST_TIMESTAMP_START(p) == 0)
251 current_line++;
252 line_start = p;
9255ee31 253 }
d60d9f65
SS
254
255 /* If there are lines left to gobble, then gobble them now. */
9255ee31
EZ
256 for (line_end = line_start; line_end < bufend; line_end++)
257 if (*line_end == '\n')
d60d9f65 258 {
a75b402a
DJ
259 if (line_end - 1 >= line_start && *(line_end - 1) == '\r')
260 *(line_end - 1) = '\0';
261 else
262 *line_end = '\0';
d60d9f65 263
9255ee31 264 if (*line_start)
5bdf8622
DJ
265 {
266 if (HIST_TIMESTAMP_START(line_start) == 0)
267 {
268 add_history (line_start);
269 if (last_ts)
270 {
271 add_history_time (last_ts);
272 last_ts = NULL;
273 }
274 }
275 else
276 {
277 last_ts = line_start;
278 current_line--;
279 }
280 }
d60d9f65
SS
281
282 current_line++;
283
284 if (current_line >= to)
285 break;
286
287 line_start = line_end + 1;
288 }
289
290 FREE (input);
5bdf8622 291#ifndef HISTORY_USE_MMAP
d60d9f65 292 FREE (buffer);
9255ee31
EZ
293#else
294 munmap (buffer, file_size);
295#endif
d60d9f65
SS
296
297 return (0);
298}
299
300/* Truncate the history file FNAME, leaving only LINES trailing lines.
9255ee31
EZ
301 If FNAME is NULL, then use ~/.history. Returns 0 on success, errno
302 on failure. */
d60d9f65
SS
303int
304history_truncate_file (fname, lines)
9255ee31 305 const char *fname;
c862e87b 306 int lines;
d60d9f65 307{
5bdf8622 308 char *buffer, *filename, *bp, *bp1; /* bp1 == bp+1 */
9255ee31 309 int file, chars_read, rv;
d60d9f65
SS
310 struct stat finfo;
311 size_t file_size;
312
313 buffer = (char *)NULL;
314 filename = history_filename (fname);
315 file = open (filename, O_RDONLY|O_BINARY, 0666);
9255ee31 316 rv = 0;
d60d9f65 317
9255ee31 318 /* Don't try to truncate non-regular files. */
d60d9f65 319 if (file == -1 || fstat (file, &finfo) == -1)
9255ee31
EZ
320 {
321 rv = errno;
322 if (file != -1)
323 close (file);
324 goto truncate_exit;
325 }
d60d9f65 326
9255ee31
EZ
327 if (S_ISREG (finfo.st_mode) == 0)
328 {
329 close (file);
330#ifdef EFTYPE
331 rv = EFTYPE;
332#else
333 rv = EINVAL;
334#endif
335 goto truncate_exit;
336 }
1b17e766 337
d60d9f65
SS
338 file_size = (size_t)finfo.st_size;
339
340 /* check for overflow on very large files */
341 if (file_size != finfo.st_size || file_size + 1 < file_size)
342 {
343 close (file);
344#if defined (EFBIG)
9255ee31
EZ
345 rv = errno = EFBIG;
346#elif defined (EOVERFLOW)
347 rv = errno = EOVERFLOW;
348#else
349 rv = errno = EINVAL;
d60d9f65
SS
350#endif
351 goto truncate_exit;
352 }
353
9255ee31
EZ
354 buffer = (char *)malloc (file_size + 1);
355 if (buffer == 0)
356 {
357 close (file);
358 goto truncate_exit;
359 }
360
d60d9f65
SS
361 chars_read = read (file, buffer, file_size);
362 close (file);
363
364 if (chars_read <= 0)
9255ee31
EZ
365 {
366 rv = (chars_read < 0) ? errno : 0;
367 goto truncate_exit;
368 }
d60d9f65
SS
369
370 /* Count backwards from the end of buffer until we have passed
5bdf8622
DJ
371 LINES lines. bp1 is set funny initially. But since bp[1] can't
372 be a comment character (since it's off the end) and *bp can't be
373 both a newline and the history comment character, it should be OK. */
374 for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
d60d9f65 375 {
5bdf8622 376 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
d60d9f65 377 lines--;
5bdf8622 378 bp1 = bp;
d60d9f65
SS
379 }
380
381 /* If this is the first line, then the file contains exactly the
382 number of lines we want to truncate to, so we don't need to do
383 anything. It's the first line if we don't find a newline between
384 the current value of i and 0. Otherwise, write from the start of
385 this line until the end of the buffer. */
9255ee31 386 for ( ; bp > buffer; bp--)
5bdf8622
DJ
387 {
388 if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
389 {
390 bp++;
391 break;
392 }
393 bp1 = bp;
394 }
d60d9f65
SS
395
396 /* Write only if there are more lines in the file than we want to
397 truncate to. */
9255ee31 398 if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
d60d9f65 399 {
9255ee31 400 write (file, bp, chars_read - (bp - buffer));
c862e87b
JM
401
402#if defined (__BEOS__)
403 /* BeOS ignores O_TRUNC. */
9255ee31 404 ftruncate (file, chars_read - (bp - buffer));
c862e87b
JM
405#endif
406
d60d9f65
SS
407 close (file);
408 }
409
410 truncate_exit:
411
412 FREE (buffer);
413
414 free (filename);
9255ee31 415 return rv;
d60d9f65
SS
416}
417
418/* Workhorse function for writing history. Writes NELEMENT entries
419 from the history list to FILENAME. OVERWRITE is non-zero if you
420 wish to replace FILENAME with the entries. */
421static int
422history_do_write (filename, nelements, overwrite)
9255ee31 423 const char *filename;
d60d9f65
SS
424 int nelements, overwrite;
425{
426 register int i;
427 char *output;
9255ee31 428 int file, mode, rv;
5bdf8622 429#ifdef HISTORY_USE_MMAP
9255ee31 430 size_t cursize;
d60d9f65 431
9255ee31
EZ
432 mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
433#else
d60d9f65 434 mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
9255ee31 435#endif
d60d9f65 436 output = history_filename (filename);
9255ee31 437 rv = 0;
d60d9f65
SS
438
439 if ((file = open (output, mode, 0600)) == -1)
440 {
441 FREE (output);
442 return (errno);
443 }
444
5bdf8622 445#ifdef HISTORY_USE_MMAP
9255ee31
EZ
446 cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
447#endif
448
d60d9f65
SS
449 if (nelements > history_length)
450 nelements = history_length;
451
452 /* Build a buffer of all the lines to write, and write them in one syscall.
453 Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
454 {
455 HIST_ENTRY **the_history; /* local */
456 register int j;
457 int buffer_size;
458 char *buffer;
459
460 the_history = history_list ();
461 /* Calculate the total number of bytes to write. */
462 for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
5bdf8622
DJ
463#if 0
464 buffer_size += 2 + HISTENT_BYTES (the_history[i]);
465#else
466 {
467 if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
468 buffer_size += strlen (the_history[i]->timestamp) + 1;
469 buffer_size += strlen (the_history[i]->line) + 1;
470 }
471#endif
d60d9f65
SS
472
473 /* Allocate the buffer, and fill it. */
5bdf8622 474#ifdef HISTORY_USE_MMAP
9255ee31
EZ
475 if (ftruncate (file, buffer_size+cursize) == -1)
476 goto mmap_error;
477 buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
478 if ((void *)buffer == MAP_FAILED)
479 {
480mmap_error:
481 rv = errno;
482 FREE (output);
483 close (file);
484 return rv;
485 }
486#else
487 buffer = (char *)malloc (buffer_size);
488 if (buffer == 0)
489 {
490 rv = errno;
491 FREE (output);
492 close (file);
493 return rv;
494 }
495#endif
d60d9f65
SS
496
497 for (j = 0, i = history_length - nelements; i < history_length; i++)
498 {
5bdf8622
DJ
499 if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
500 {
501 strcpy (buffer + j, the_history[i]->timestamp);
502 j += strlen (the_history[i]->timestamp);
503 buffer[j++] = '\n';
504 }
d60d9f65
SS
505 strcpy (buffer + j, the_history[i]->line);
506 j += strlen (the_history[i]->line);
507 buffer[j++] = '\n';
508 }
509
5bdf8622 510#ifdef HISTORY_USE_MMAP
9255ee31
EZ
511 if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0)
512 rv = errno;
513#else
514 if (write (file, buffer, buffer_size) < 0)
515 rv = errno;
d60d9f65 516 free (buffer);
9255ee31 517#endif
d60d9f65
SS
518 }
519
520 close (file);
521
522 FREE (output);
523
9255ee31 524 return (rv);
d60d9f65
SS
525}
526
527/* Append NELEMENT entries to FILENAME. The entries appended are from
528 the end of the list minus NELEMENTs up to the end of the list. */
529int
530append_history (nelements, filename)
531 int nelements;
9255ee31 532 const char *filename;
d60d9f65
SS
533{
534 return (history_do_write (filename, nelements, HISTORY_APPEND));
535}
536
537/* Overwrite FILENAME with the current history. If FILENAME is NULL,
538 then write the history list to ~/.history. Values returned
539 are as in read_history ().*/
540int
541write_history (filename)
9255ee31 542 const char *filename;
d60d9f65
SS
543{
544 return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
545}
This page took 0.359913 seconds and 4 git commands to generate.