?? history.c
字號:
read_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; input = history_filename (filename); file = open (input, O_RDONLY, 0666); if ((file < 0) || (fstat (file, &finfo) == -1)) goto error_and_exit; buffer = xmalloc ((int)finfo.st_size + 1); if (read (file, buffer, finfo.st_size) != finfo.st_size) { error_and_exit: if (file >= 0) close (file); if (input) free (input); 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; } if (input) free (input); if (buffer) free (buffer); return (0);}/* Truncate the history file FNAME, leaving only LINES trailing lines. If FNAME is NULL, then use ~/.history. */inthistory_truncate_file (fname, lines) char *fname; register int lines;{ register int i; int file, chars_read; char *buffer = (char *)NULL, *filename; struct stat finfo; filename = history_filename (fname); file = open (filename, O_RDONLY, 0666); if (file == -1 || fstat (file, &finfo) == -1) goto truncate_exit; buffer = xmalloc ((int)finfo.st_size + 1); chars_read = read (file, buffer, finfo.st_size); close (file); if (chars_read <= 0) goto truncate_exit; /* Count backwards from the end of buffer until we have passed LINES lines. */ for (i = chars_read - 1; lines && i; i--) { if (buffer[i] == '\n') lines--; } /* If this is the first line, then the file contains exactly the number of lines we want to truncate to, so we don't need to do anything. It's the first line if we don't find a newline between the current value of i and 0. Otherwise, write from the start of this line until the end of the buffer. */ for ( ; i; i--) if (buffer[i] == '\n') { i++; break; } /* Write only if there are more lines in the file than we want to truncate to. */ if (i && ((file = open (filename, O_WRONLY|O_TRUNC, 0666)) != -1)) { write (file, buffer + i, finfo.st_size - i); close (file); } truncate_exit: if (buffer) free (buffer); free (filename); return 0;}#define HISTORY_APPEND 0#define HISTORY_OVERWRITE 1/* Workhorse function for writing history. Writes NELEMENT entries from the history list to FILENAME. OVERWRITE is non-zero if you wish to replace FILENAME with the entries. */static inthistory_do_write (filename, nelements, overwrite) char *filename; int nelements, overwrite;{ register int i; char *output = history_filename (filename); int file, mode; mode = overwrite ? O_WRONLY | O_CREAT | O_TRUNC : O_WRONLY | O_APPEND; if ((file = open (output, mode, 0666)) == -1) { if (output) free (output); return (errno); } if (nelements > history_length) nelements = history_length; /* Build a buffer of all the lines to write, and write them in one syscall. Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */ { register int j = 0; int buffer_size = 0; char *buffer; /* Calculate the total number of bytes to write. */ for (i = history_length - nelements; i < history_length; i++) buffer_size += 1 + strlen (the_history[i]->line); /* Allocate the buffer, and fill it. */ buffer = xmalloc (buffer_size); for (i = history_length - nelements; i < history_length; i++) { strcpy (buffer + j, the_history[i]->line); j += strlen (the_history[i]->line); buffer[j++] = '\n'; } write (file, buffer, buffer_size); free (buffer); } close (file); if (output) free (output); return (0);}/* Append NELEMENT entries to FILENAME. The entries appended are from the end of the list minus NELEMENTs up to the end of the list. */intappend_history (nelements, filename) int nelements; char *filename;{ return (history_do_write (filename, nelements, HISTORY_APPEND));}/* Overwrite FILENAME with the current history. If FILENAME is NULL, then write the history list to ~/.history. Values returned are as in read_history ().*/intwrite_history (filename) char *filename;{ return (history_do_write (filename, history_length, HISTORY_OVERWRITE));}/* Return the history entry at the current position, as determined by history_offset. If there is no entry there, return a NULL pointer. */HIST_ENTRY *current_history (){ if ((history_offset == history_length) || !the_history) return ((HIST_ENTRY *)NULL); else return (the_history[history_offset]);}/* Back up history_offset to the previous history entry, and return a pointer to that entry. If there is no previous entry then return a NULL pointer. */HIST_ENTRY *previous_history (){ if (!history_offset) return ((HIST_ENTRY *)NULL); else return (the_history[--history_offset]);}/* Move history_offset forward to the next history entry, and return a pointer to that entry. If there is no next entry then return a NULL pointer. */HIST_ENTRY *next_history (){ if (history_offset == history_length) return ((HIST_ENTRY *)NULL); else return (the_history[++history_offset]);}/* Return the current history array. The caller has to be carefull, since this is the actual array of data, and could be bashed or made corrupt easily. The array is terminated with a NULL pointer. */HIST_ENTRY **history_list (){ return (the_history);}/* Return the history entry which is logically at OFFSET in the history array. OFFSET is relative to history_base. */HIST_ENTRY *history_get (offset) int offset;{ int local_index = offset - history_base; if (local_index >= history_length || local_index < 0 || !the_history) return ((HIST_ENTRY *)NULL); return (the_history[local_index]);}/* Search for STRING in the history list. DIR is < 0 for searching backwards. POS is an absolute index into the history list at which point to begin searching. */inthistory_search_pos (string, dir, pos) char *string; int dir, pos;{ int ret, old = where_history (); history_set_pos (pos); if (history_search (string, dir) == -1) { history_set_pos (old); return (-1); } ret = where_history (); history_set_pos (old); return ret;}/* Make the current history item be the one at POS, an absolute index. Returns zero if POS is out of range, else non-zero. */inthistory_set_pos (pos) int pos;{ if (pos > history_length || pos < 0 || !the_history) return (0); history_offset = pos; return (1);} /* **************************************************************** *//* *//* History Expansion *//* *//* **************************************************************** *//* Hairy history expansion on text, not tokens. This is of general use, and thus belongs in this library. *//* The last string searched for in a !?string? search. */static char *search_string = (char *)NULL;/* Return the event specified at TEXT + OFFSET modifying OFFSET to point to after the event specifier. Just a pointer to the history line is returned; NULL is returned in the event of a bad specifier. You pass STRING with *INDEX equal to the history_expansion_char that begins this specification. DELIMITING_QUOTE is a character that is allowed to end the string specification for what to search for in addition to the normal characters `:', ` ', `\t', `\n', and sometimes `?'. So you might call this function like: line = get_history_event ("!echo:p", &index, 0); */char *get_history_event (string, caller_index, delimiting_quote) char *string; int *caller_index; int delimiting_quote;{ register int i = *caller_index; register char c; HIST_ENTRY *entry; int which, sign = 1; int local_index, search_mode, substring_okay = 0; char *temp; /* The event can be specified in a number of ways. !! the previous command !n command line N !-n current command-line minus N !str the most recent command starting with STR !?str[?] the most recent command containing STR All values N are determined via HISTORY_BASE. */ if (string[i] != history_expansion_char) return ((char *)NULL); /* Move on to the specification. */ i++;#define RETURN_ENTRY(e, w) \ return ((e = history_get (w)) ? e->line : (char *)NULL) /* Handle !! case. */ if (string[i] == history_expansion_char) { i++; which = history_base + (history_length - 1); *caller_index = i; RETURN_ENTRY (entry, which); } /* Hack case of numeric line specification. */ if (string[i] == '-') { sign = -1; i++; } if (digit_p (string[i])) { /* Get the extent of the digits and compute the value. */ for (which = 0; digit_p (string[i]); i++) which = (which * 10) + digit_value (string[i]); *caller_index = i; if (sign < 0) which = (history_length + history_base) - which; RETURN_ENTRY (entry, which); } /* This must be something to search for. If the spec begins with a '?', then the string may be anywhere on the line. Otherwise, the string must be found at the start of a line. */ if (string[i] == '?') { substring_okay++; i++; } /* Only a closing `?' or a newline delimit a substring search string. */ for (local_index = i; c = string[i]; i++) if ((!substring_okay && (whitespace (c) || c == ':' ||#if defined (SHELL) member (c, ";&()|<>") ||#endif /* SHELL */ string[i] == delimiting_quote)) || string[i] == '\n' || (substring_okay && string[i] == '?')) break; temp = xmalloc (1 + (i - local_index)); strncpy (temp, &string[local_index], (i - local_index)); temp[i - local_index] = '\0'; if (substring_okay && string[i] == '?') i++; *caller_index = i;#define FAIL_SEARCH() \ do { history_offset = history_length; free (temp) ; return (char *)NULL; } while (0) search_mode = substring_okay ? NON_ANCHORED_SEARCH : ANCHORED_SEARCH; while (1) { local_index = history_search_internal (temp, -1, search_mode); if (local_index < 0) FAIL_SEARCH (); if (local_index == 0 || substring_okay) { entry = current_history (); history_offset = history_length; /* If this was a substring search, then remember the string that we matched for word substitution. */ if (substring_okay) { if (search_string) free (search_string); search_string = temp; } else free (temp); return (entry->line); } if (history_offset) history_offset--; else FAIL_SEARCH (); }#undef FAIL_SEARCH#undef RETURN_ENTRY}#if defined (SHELL)/* Function for extracting single-quoted strings. Used for inhibiting history expansion within single quotes. *//* Extract the contents of STRING as if it is enclosed in single quotes. SINDEX, when passed in, is the offset of the character immediately following the opening single quote; on exit, SINDEX is left pointing to the closing single quote. */static voidrl_string_extract_single_quoted (string, sindex) char *string; int *sindex;{ register int i = *sindex; while (string[i] && string[i] != '\'') i++; *sindex = i;}static char *quote_breaks (s) char *s;{ register char *p, *r; char *ret; int len = 3; for (p = s; p && *p; p++, len++) { if (*p == '\'') len += 3; else if (whitespace (*p) || *p == '\n') len += 2; } r = ret = xmalloc (len); *r++ = '\''; for (p = s; p && *p; ) { if (*p == '\'') { *r++ = '\''; *r++ = '\\'; *r++ = '\''; *r++ = '\''; p++; } else if (whitespace (*p) || *p == '\n') { *r++ = '\''; *r++ = *p++; *r++ = '\''; } else *r++ = *p++; } *r++ = '\''; *r = '\0'; return ret;}#endif /* SHELL */static char *hist_error(s, start, current, errtype)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -