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