📄 history.c
字号:
/* History.c -- standalone history library *//* Copyright (C) 1989, 1991 Free Software Foundation, Inc. This file contains the GNU History Library (the Library), a set of routines for managing the text of previously typed lines. The Library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* The goal is to make the implementation transparent, so that you don't have to know what data types are used, just what functions you can call. I think I have done that. *//* Remove these declarations when we have a complete libgnu.a. */#if !defined (STATIC_MALLOC)extern char *xmalloc (), *xrealloc ();#elsestatic char *xmalloc (), *xrealloc ();#endif#include "sysdep.h"#include <stdio.h>#include <errno.h>#include <sys/types.h>#ifndef NO_SYS_FILE#include <sys/file.h>#endif#include <sys/stat.h>#include <fcntl.h>#include "history.h"#ifndef savestring#define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x))#endif#ifndef whitespace#define whitespace(c) (((c) == ' ') || ((c) == '\t'))#endif#ifndef digit#define digit(c) ((c) >= '0' && (c) <= '9')#endif#ifndef member#define member(c, s) ((c) ? index ((s), (c)) : 0)#endif/* **************************************************************** *//* *//* History Functions *//* *//* **************************************************************** *//* An array of HIST_ENTRY. This is where we store the history. */static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL;/* Non-zero means that we have enforced a limit on the amount of history that we save. */int history_stifled = 0;/* If HISTORY_STIFLED is non-zero, then this is the maximum number of entries to remember. */int max_input_history;/* The current location of the interactive history pointer. Just makes life easier for outside callers. */static int history_offset = 0;/* The number of strings currently stored in the input_history list. */int history_length = 0;/* The current number of slots allocated to the input_history. */static int history_size = 0;/* The number of slots to increase the_history by. */#define DEFAULT_HISTORY_GROW_SIZE 50/* The character that represents the start of a history expansion request. This is usually `!'. */char history_expansion_char = '!';/* The character that invokes word substitution if found at the start of a line. This is usually `^'. */char history_subst_char = '^';/* During tokenization, if this character is seen as the first character of a word, then it, and all subsequent characters upto a newline are ignored. For a Bourne shell, this should be '#'. Bash special cases the interactive comment character to not be a comment delimiter. */char history_comment_char = '\0';/* The list of characters which inhibit the expansion of text if found immediately following history_expansion_char. */char *history_no_expand_chars = " \t\n\r=";/* The logical `base' of the history array. It defaults to 1. */int history_base = 1;/* Begin a session in which the history functions might be used. This initializes interactive variables. */voidusing_history (){ history_offset = history_length;}/* Return the number of bytes that the primary history entries are using. This just adds up the lengths of the_history->lines. */inthistory_total_bytes (){ register int i, result; result = 0; for (i = 0; the_history && the_history[i]; i++) result += strlen (the_history[i]->line); return (result);}/* Place STRING at the end of the history list. The data field is set to NULL. */voidadd_history (string) char *string;{ HIST_ENTRY *temp; if (history_stifled && (history_length == max_input_history)) { register int i; /* If the history is stifled, and history_length is zero, and it equals max_input_history, we don't save items. */ if (!history_length) return; /* If there is something in the slot, then remove it. */ if (the_history[0]) { free (the_history[0]->line); free (the_history[0]); } for (i = 0; i < history_length; i++) the_history[i] = the_history[i + 1]; history_base++; } else { if (!history_size) { the_history = (HIST_ENTRY **) xmalloc ((history_size = DEFAULT_HISTORY_GROW_SIZE) * sizeof (HIST_ENTRY *)); history_length = 1; } else { if (history_length == (history_size - 1)) { the_history = (HIST_ENTRY **) xrealloc (the_history, ((history_size += DEFAULT_HISTORY_GROW_SIZE) * sizeof (HIST_ENTRY *))); } history_length++; } } temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); temp->line = savestring (string); temp->data = (char *)NULL; the_history[history_length] = (HIST_ENTRY *)NULL; the_history[history_length - 1] = temp;}/* Make the history entry at WHICH have LINE and DATA. This returns the old entry so you can dispose of the data. In the case of an invalid WHICH, a NULL pointer is returned. */HIST_ENTRY *replace_history_entry (which, line, data) int which; char *line; char *data;{ HIST_ENTRY *temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); HIST_ENTRY *old_value; if (which >= history_length) return ((HIST_ENTRY *)NULL); old_value = the_history[which]; temp->line = savestring (line); temp->data = data; the_history[which] = temp; return (old_value);}/* Returns the magic number which says what history element we are looking at now. In this implementation, it returns history_offset. */intwhere_history (){ return (history_offset);}/* Search the history for STRING, starting at history_offset. If DIRECTION < 0, then the search is through previous entries, else through subsequent. If ANCHORED is non-zero, the string must appear at the beginning of a history line, otherwise, the string may appear anywhere in the line. If the string is found, then current_history () is the history entry, and the value of this function is the offset in the line of that history entry that the string was found in. Otherwise, nothing is changed, and a -1 is returned. */#define ANCHORED_SEARCH 1#define NON_ANCHORED_SEARCH 0static inthistory_search_internal (string, direction, anchored) char *string; int direction, anchored;{ register int i = history_offset; register int reverse = (direction < 0); register char *line; register int index; int string_len = strlen (string); /* Take care of trivial cases first. */ if (!history_length || ((i == history_length) && !reverse)) return (-1); if (reverse && (i == history_length)) i--; while (1) { /* Search each line in the history list for STRING. */ /* At limit for direction? */ if ((reverse && i < 0) || (!reverse && i == history_length)) return (-1); line = the_history[i]->line; index = strlen (line); /* If STRING is longer than line, no match. */ if (string_len > index) goto next_line; /* Handle anchored searches first. */ if (anchored == ANCHORED_SEARCH) { if (strncmp (string, line, string_len) == 0) { history_offset = i; return (0); } goto next_line; } /* Do substring search. */ if (reverse) { index -= string_len; while (index >= 0) { if (strncmp (string, line + index, string_len) == 0) { history_offset = i; return (index); } index--; } } else { register int limit = index - string_len + 1; index = 0; while (index < limit) { if (strncmp (string, line + index, string_len) == 0) { history_offset = i; return (index); } index++; } } next_line: if (reverse) i--; else i++; }}/* Do a non-anchored search for STRING through the history in DIRECTION. */inthistory_search (string, direction) char *string; int direction;{ return (history_search_internal (string, direction, NON_ANCHORED_SEARCH));}/* Do an anchored search for string through the history in DIRECTION. */inthistory_search_prefix (string, direction) char *string; int direction;{ return (history_search_internal (string, direction, ANCHORED_SEARCH));}/* Remove history element WHICH from the history. The removed element is returned to you so you can free the line, data, and containing structure. */HIST_ENTRY *remove_history (which) int which;{ HIST_ENTRY *return_value; if (which >= history_length || !history_length) return_value = (HIST_ENTRY *)NULL; else { register int i; return_value = the_history[which]; for (i = which; i < history_length; i++) the_history[i] = the_history[i + 1]; history_length--; } return (return_value);}/* Stifle the history list, remembering only MAX number of lines. */voidstifle_history (max) int max;{ if (max < 0) max = 0; if (history_length > max) { register int i, j; /* This loses because we cannot free the data. */ for (i = 0; i < (history_length - max); i++) { free (the_history[i]->line); free (the_history[i]); } history_base = i; for (j = 0, i = history_length - max; j < max; i++, j++) the_history[j] = the_history[i]; the_history[j] = (HIST_ENTRY *)NULL; history_length = j; } history_stifled = 1; max_input_history = max;}/* Stop stifling the history. This returns the previous amount the history was stifled by. The value is positive if the history was stifled, negative if it wasn't. */intunstifle_history (){ int result = max_input_history; if (history_stifled) { result = - result; history_stifled = 0; } return (result);}/* Return the string that should be used in the place of this filename. This only matters when you don't specify the filename to read_history (), or write_history (). */static char *history_filename (filename) char *filename;{ char *return_val = filename ? savestring (filename) : (char *)NULL; if (!return_val) { char *home = (char *)getenv ("HOME"); if (!home) home = "."; return_val = (char *)xmalloc (2 + strlen (home) + strlen (".history")); sprintf (return_val, "%s/.history", home); } return (return_val);}/* Add the contents of FILENAME to the history list, a line at a time. If FILENAME is NULL, then read from ~/.history. Returns 0 if successful, or errno if not. */intread_history (filename) char *filename;{ return (read_history_range (filename, 0, -1));}/* Read a range of lines from FILENAME, adding them to the history list. Start reading at the FROM'th line and end at the TO'th. If FROM is zero, start at the beginning. If TO is less than FROM, read until the end of the file. If FILENAME is NULL, then read from ~/.history. Returns 0 if successful, or errno if not. */intread_history_range (filename, from, to) char *filename; int from, to;{ register int line_start, line_end; char *input, *buffer = (char *)NULL; int file, current_line; struct stat finfo; extern int errno; input = history_filename (filename); file = open (input, O_RDONLY, 0666); if ((file < 0) || (stat (input, &finfo) == -1)) goto error_and_exit; buffer = (char *)xmalloc (finfo.st_size + 1); if (read (file, buffer, finfo.st_size) != finfo.st_size) error_and_exit: { if (file >= 0) close (file); if (buffer) free (buffer); return (errno); } close (file); /* Set TO to larger than end of file if negative. */ if (to < 0) to = finfo.st_size; /* Start at beginning of file, work to end. */ line_start = line_end = current_line = 0; /* Skip lines until we are at FROM. */ while (line_start < finfo.st_size && current_line < from) { for (line_end = line_start; line_end < finfo.st_size; line_end++) if (buffer[line_end] == '\n') { current_line++; line_start = line_end + 1; if (current_line == from) break; } } /* If there are lines left to gobble, then gobble them now. */ for (line_end = line_start; line_end < finfo.st_size; line_end++) if (buffer[line_end] == '\n') { buffer[line_end] = '\0'; if (buffer[line_start]) add_history (buffer + line_start); current_line++; if (current_line >= to) break; line_start = line_end + 1; } return (0);}/* Truncate the history file FNAME, leaving only LINES trailing lines. If FNAME is NULL, then use ~/.history. */history_truncate_file (fname, lines) char *fname; register int lines;{ register int i; int file; char *buffer = (char *)NULL, *filename; struct stat finfo; filename = history_filename (fname); if (stat (filename, &finfo) == -1) goto truncate_exit; file = open (filename, O_RDONLY, 0666); if (file == -1) goto truncate_exit; buffer = (char *)xmalloc (finfo.st_size + 1); read (file, buffer, finfo.st_size); close (file); /* Count backwards from the end of buffer until we have passed LINES lines. */ for (i = finfo.st_size; lines && i; i--) { if (buffer[i] == '\n') lines--; } /* If there are fewer lines in the file than we want to truncate to, then we are all done. */ if (!i) goto truncate_exit; /* Otherwise, write from the start of this line until the end of the buffer. */ for (--i; i; i--) if (buffer[i] == '\n') { i++; break; } file = open (filename, O_WRONLY | O_TRUNC | O_CREAT, 0666); if (file == -1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -