histfile.c revision 30971
1/* histfile.c - functions to manipulate the history file. */ 2 3/* Copyright (C) 1989, 1992 Free Software Foundation, Inc. 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 10 the Free Software Foundation; either version 1, or (at your option) 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, 21 675 Mass Ave, Cambridge, MA 02139, USA. */ 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. */ 26#define READLINE_LIBRARY 27 28#if defined (HAVE_CONFIG_H) 29# include <config.h> 30#endif 31 32#include <stdio.h> 33 34#include <sys/types.h> 35#include <sys/file.h> 36#include <sys/stat.h> 37#include <fcntl.h> 38 39#if defined (HAVE_STDLIB_H) 40# include <stdlib.h> 41#else 42# include "ansi_stdlib.h" 43#endif /* HAVE_STDLIB_H */ 44 45#if defined (HAVE_UNISTD_H) 46# include <unistd.h> 47#endif 48 49#if defined (HAVE_STRING_H) 50# include <string.h> 51#else 52# include <strings.h> 53#endif /* !HAVE_STRING_H */ 54 55#if defined (__EMX__) 56# ifndef O_BINARY 57# define O_BINARY 0 58# endif 59#else /* !__EMX__ */ 60 /* If we're not compiling for __EMX__, we don't want this at all. Ever. */ 61# undef O_BINARY 62# define O_BINARY 0 63#endif /* !__EMX__ */ 64 65#include <errno.h> 66#if !defined (errno) 67extern int errno; 68#endif /* !errno */ 69 70#include "history.h" 71#include "histlib.h" 72 73/* Functions imported from shell.c */ 74extern char *get_env_value (); 75 76extern char *xmalloc (), *xrealloc (); 77 78/* Return the string that should be used in the place of this 79 filename. This only matters when you don't specify the 80 filename to read_history (), or write_history (). */ 81static char * 82history_filename (filename) 83 char *filename; 84{ 85 char *return_val, *home; 86 int home_len; 87 88 return_val = filename ? savestring (filename) : (char *)NULL; 89 90 if (return_val) 91 return (return_val); 92 93 home = get_env_value ("HOME"); 94 95 if (home == 0) 96 { 97 home = "."; 98 home_len = 1; 99 } 100 else 101 home_len = strlen (home); 102 103 return_val = xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */ 104 strcpy (return_val, home); 105 return_val[home_len] = '/'; 106 strcpy (return_val + home_len + 1, ".history"); 107 108 return (return_val); 109} 110 111/* Add the contents of FILENAME to the history list, a line at a time. 112 If FILENAME is NULL, then read from ~/.history. Returns 0 if 113 successful, or errno if not. */ 114int 115read_history (filename) 116 char *filename; 117{ 118 return (read_history_range (filename, 0, -1)); 119} 120 121/* Read a range of lines from FILENAME, adding them to the history list. 122 Start reading at the FROM'th line and end at the TO'th. If FROM 123 is zero, start at the beginning. If TO is less than FROM, read 124 until the end of the file. If FILENAME is NULL, then read from 125 ~/.history. Returns 0 if successful, or errno if not. */ 126int 127read_history_range (filename, from, to) 128 char *filename; 129 int from, to; 130{ 131 register int line_start, line_end; 132 char *input, *buffer = (char *)NULL; 133 int file, current_line; 134 struct stat finfo; 135 136 input = history_filename (filename); 137 file = open (input, O_RDONLY|O_BINARY, 0666); 138 139 if ((file < 0) || (fstat (file, &finfo) == -1)) 140 goto error_and_exit; 141 142 buffer = xmalloc ((int)finfo.st_size + 1); 143 144 if (read (file, buffer, finfo.st_size) != finfo.st_size) 145 { 146 error_and_exit: 147 if (file >= 0) 148 close (file); 149 150 FREE (input); 151 FREE (buffer); 152 153 return (errno); 154 } 155 156 close (file); 157 158 /* Set TO to larger than end of file if negative. */ 159 if (to < 0) 160 to = finfo.st_size; 161 162 /* Start at beginning of file, work to end. */ 163 line_start = line_end = current_line = 0; 164 165 /* Skip lines until we are at FROM. */ 166 while (line_start < finfo.st_size && current_line < from) 167 { 168 for (line_end = line_start; line_end < finfo.st_size; line_end++) 169 if (buffer[line_end] == '\n') 170 { 171 current_line++; 172 line_start = line_end + 1; 173 if (current_line == from) 174 break; 175 } 176 } 177 178 /* If there are lines left to gobble, then gobble them now. */ 179 for (line_end = line_start; line_end < finfo.st_size; line_end++) 180 if (buffer[line_end] == '\n') 181 { 182 buffer[line_end] = '\0'; 183 184 if (buffer[line_start]) 185 add_history (buffer + line_start); 186 187 current_line++; 188 189 if (current_line >= to) 190 break; 191 192 line_start = line_end + 1; 193 } 194 195 FREE (input); 196 FREE (buffer); 197 198 return (0); 199} 200 201/* Truncate the history file FNAME, leaving only LINES trailing lines. 202 If FNAME is NULL, then use ~/.history. */ 203int 204history_truncate_file (fname, lines) 205 char *fname; 206 register int lines; 207{ 208 register int i; 209 int file, chars_read; 210 char *buffer, *filename; 211 struct stat finfo; 212 213 buffer = (char *)NULL; 214 filename = history_filename (fname); 215 file = open (filename, O_RDONLY|O_BINARY, 0666); 216 217 if (file == -1 || fstat (file, &finfo) == -1) 218 goto truncate_exit; 219 220 buffer = xmalloc ((int)finfo.st_size + 1); 221 chars_read = read (file, buffer, finfo.st_size); 222 close (file); 223 224 if (chars_read <= 0) 225 goto truncate_exit; 226 227 /* Count backwards from the end of buffer until we have passed 228 LINES lines. */ 229 for (i = chars_read - 1; lines && i; i--) 230 { 231 if (buffer[i] == '\n') 232 lines--; 233 } 234 235 /* If this is the first line, then the file contains exactly the 236 number of lines we want to truncate to, so we don't need to do 237 anything. It's the first line if we don't find a newline between 238 the current value of i and 0. Otherwise, write from the start of 239 this line until the end of the buffer. */ 240 for ( ; i; i--) 241 if (buffer[i] == '\n') 242 { 243 i++; 244 break; 245 } 246 247 /* Write only if there are more lines in the file than we want to 248 truncate to. */ 249 if (i && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1)) 250 { 251 write (file, buffer + i, finfo.st_size - i); 252 close (file); 253 } 254 255 truncate_exit: 256 257 FREE (buffer); 258 259 free (filename); 260 return 0; 261} 262 263/* Workhorse function for writing history. Writes NELEMENT entries 264 from the history list to FILENAME. OVERWRITE is non-zero if you 265 wish to replace FILENAME with the entries. */ 266static int 267history_do_write (filename, nelements, overwrite) 268 char *filename; 269 int nelements, overwrite; 270{ 271 register int i; 272 char *output; 273 int file, mode; 274 275 mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY; 276 output = history_filename (filename); 277 278 if ((file = open (output, mode, 0600)) == -1) 279 { 280 FREE (output); 281 return (errno); 282 } 283 284 if (nelements > history_length) 285 nelements = history_length; 286 287 /* Build a buffer of all the lines to write, and write them in one syscall. 288 Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */ 289 { 290 HIST_ENTRY **the_history; /* local */ 291 register int j; 292 int buffer_size; 293 char *buffer; 294 295 the_history = history_list (); 296 /* Calculate the total number of bytes to write. */ 297 for (buffer_size = 0, i = history_length - nelements; i < history_length; i++) 298 buffer_size += 1 + strlen (the_history[i]->line); 299 300 /* Allocate the buffer, and fill it. */ 301 buffer = xmalloc (buffer_size); 302 303 for (j = 0, i = history_length - nelements; i < history_length; i++) 304 { 305 strcpy (buffer + j, the_history[i]->line); 306 j += strlen (the_history[i]->line); 307 buffer[j++] = '\n'; 308 } 309 310 write (file, buffer, buffer_size); 311 free (buffer); 312 } 313 314 close (file); 315 316 FREE (output); 317 318 return (0); 319} 320 321/* Append NELEMENT entries to FILENAME. The entries appended are from 322 the end of the list minus NELEMENTs up to the end of the list. */ 323int 324append_history (nelements, filename) 325 int nelements; 326 char *filename; 327{ 328 return (history_do_write (filename, nelements, HISTORY_APPEND)); 329} 330 331/* Overwrite FILENAME with the current history. If FILENAME is NULL, 332 then write the history list to ~/.history. Values returned 333 are as in read_history ().*/ 334int 335write_history (filename) 336 char *filename; 337{ 338 return (history_do_write (filename, history_length, HISTORY_OVERWRITE)); 339} 340