📄 hist.c
字号:
/* * Copyright (c) 1994 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * Adapted from code written by Stephen Rothwell. * * Interactive readline module. This is called to read lines of input, * while using emacs-like editing commands within a command stack. * The key bindings for the editing commands are (slightly) configurable. */#include <stdio.h>#include <ctype.h>#include <pwd.h>#include "hist.h"#include "terminal.h"#include "have_string.h"#if defined(USE_TERMIOS)# include <termios.h># define TTYSTRUCT struct termios#else /* USE_SGTTY */# if defined(USE_TERMIO)# include <termio.h># define TTYSTRUCT struct termio# else /* USE_TERMIO */ /* assume USE_SGTTY */# include <sys/ioctl.h># define TTYSTRUCT struct sgttyb# endif /* USE_TERMIO */#endif /* USE_SGTTY */#ifdef HAVE_STRING_H# include <string.h>#endif#define STDIN 0#define SAVE_SIZE 256 /* size of save buffer */#define MAX_KEYS 60 /* number of key bindings */#define CONTROL(x) ((char)(((int)(x)) & 0x1f))static struct { char *prompt; char *buf; char *pos; char *end; char *mark; int bufsize; int linelen; int histcount; int curhist;} HS;typedef void (*FUNCPTR)();typedef struct { char *name; FUNCPTR func;} FUNC;static void flush_input(), start_of_line(), end_of_line();static void forward_char(), backward_char(), forward_word();static void backward_word(), delete_char(), forward_kill_char();static void backward_kill_char(), forward_kill_word(), kill_line();static void new_line(), save_line(), forward_history();static void backward_history(), insert_char();static void goto_line(), list_history(), refresh_line(), swap_chars();static void set_mark(), yank(), save_region(), kill_region();static void reverse_search(), quote_char(), uppercase_word();static void lowercase_word(), ignore_char(), arrow_key(), quit_calc();static FUNC funcs[] ={ {"ignore-char", ignore_char}, {"flush-input", flush_input}, {"start-of-line", start_of_line}, {"end-of-line", end_of_line}, {"forward-char", forward_char}, {"backward-char", backward_char}, {"forward-word", forward_word}, {"backward-word", backward_word}, {"delete-char", delete_char}, {"forward-kill-char", forward_kill_char}, {"backward-kill-char", backward_kill_char}, {"forward-kill-word", forward_kill_word}, {"uppercase-word", uppercase_word}, {"lowercase-word", lowercase_word}, {"kill-line", kill_line}, {"goto-line", goto_line}, {"new-line", new_line}, {"save-line", save_line}, {"forward-history", forward_history}, {"backward-history", backward_history}, {"insert-char", insert_char}, {"list-history", list_history}, {"refresh-line", refresh_line}, {"swap-chars", swap_chars}, {"set-mark", set_mark}, {"yank", yank}, {"save-region", save_region}, {"kill-region", kill_region}, {"reverse-search", reverse_search}, {"quote-char", quote_char}, {"arrow-key", arrow_key}, {"quit", quit_calc}, {NULL, NULL}};typedef struct key_ent KEY_ENT;typedef struct key_map KEY_MAP;struct key_ent { FUNCPTR func; KEY_MAP *next;};struct key_map { char *name; KEY_ENT default_ent; KEY_ENT *map[256];};static char base_map_name[] = "base-map";static char esc_map_name[] = "esc-map";static KEY_MAP maps[] = { {base_map_name}, {esc_map_name}};#define INTROUND (sizeof(int) - 1)#define HISTLEN(hp) ((((hp)->len + INTROUND) & ~INTROUND) + sizeof(int))#define HISTOFFSET(hp) (((char *) (hp)) - histbuf)#define FIRSTHIST ((HIST *) histbuf)#define NEXTHIST(hp) ((HIST *) (((char *) (hp)) + HISTLEN(hp)))typedef struct { int len; /* length of data */ char data[1]; /* varying length data */} HIST;static int inited;static int canedit;static int histused;static int key_count;static int save_len;static TTYSTRUCT oldtty;static KEY_MAP *cur_map;static KEY_MAP *base_map;static KEY_ENT key_table[MAX_KEYS];static char histbuf[HIST_SIZE + 1];static char save_buffer[SAVE_SIZE];static FUNCPTR find_func();static HIST *get_event();static HIST *find_event();static void read_key();static void erasechar();static void newline();static void backspace();static void beep();static void echo_char();static void echo_string();static void savetext();static void memrcpy();static int read_bindings();static int in_word();/* * Read a line into the specified buffer. The line ends in a newline, * and is NULL terminated. Returns the number of characters read, or * zero on an end of file or error. The prompt is printed before reading * the line. */inthist_getline(prompt, buf, len) char *prompt; char *buf; int len;{ if (!inited) (void) hist_init((char *) NULL); HS.prompt = prompt; HS.bufsize = len - 2; HS.buf = buf; HS.pos = buf; HS.end = buf; HS.mark = NULL; HS.linelen = -1; fputs(prompt, stdout); fflush(stdout); if (!canedit) { if (fgets(buf, len, stdin) == NULL) return 0; return strlen(buf); } while (HS.linelen < 0) read_key(); return HS.linelen;}/* * Initialize the module by reading in the key bindings from the specified * filename, and then setting the terminal modes for noecho and cbreak mode. * If the supplied filename is NULL, then a default filename will be used. * Returns zero if successful, or a nonzero error code if unsuccessful. * If this routine fails, hist_getline, hist_saveline, and hist_term can * still be called but all fancy editing is disabled. */inthist_init(filename) char *filename;{ TTYSTRUCT newtty; if (inited) return HIST_INITED; inited = 1; canedit = 0; if (filename == NULL) filename = HIST_BINDING_FILE; if (read_bindings(filename)) return HIST_NOFILE;#ifdef USE_SGTTY if (ioctl(STDIN, TIOCGETP, &oldtty) < 0) return HIST_NOTTY; newtty = oldtty; newtty.sg_flags &= ~ECHO; newtty.sg_flags |= CBREAK; if (ioctl(STDIN, TIOCSETP, &newtty) < 0) return HIST_NOTTY;#endif#ifdef USE_TERMIO if (ioctl(STDIN, TCGETA, &oldtty) < 0) return HIST_NOTTY; newtty = oldtty; newtty.c_lflag &= ~(ECHO | ECHOE | ECHOK); newtty.c_iflag |= ISTRIP; newtty.c_lflag &= ~ICANON; newtty.c_cc[VMIN] = 1; newtty.c_cc[VTIME] = 0; if (ioctl(STDIN, TCSETAW, &newtty) < 0) return HIST_NOTTY;#endif#ifdef USE_TERMIOS if (tcgetattr(STDIN, &oldtty) < 0) return HIST_NOTTY; newtty = oldtty; newtty.c_lflag &= ~(ECHO | ECHOE | ECHOK); newtty.c_iflag |= ISTRIP; newtty.c_lflag &= ~ICANON; newtty.c_cc[VMIN] = 1; newtty.c_cc[VTIME] = 0; if (tcsetattr(STDIN, TCSANOW, &newtty) < 0) return HIST_NOTTY;#endif canedit = 1; return HIST_SUCCESS;}/* * Reset the terminal modes just before exiting. */voidhist_term(){ if (!inited || !canedit) { inited = 0; return; }#ifdef USE_SGTTY (void) ioctl(STDIN, TIOCSETP, &oldtty);#endif#ifdef USE_TERMIO (void) ioctl(STDIN, TCSETAW, &oldtty);#endif#ifdef USE_TERMIOS (void) tcsetattr(STDIN, TCSANOW, &oldtty);#endif}static KEY_MAP *find_map(map) char *map;{ int i; for (i = 0; i < sizeof(maps) / sizeof(maps[0]); i++) { if (strcmp(map, maps[i].name) == 0) return &maps[i]; } return NULL;}static voidunbind_key(map, key) int key; KEY_MAP *map;{ map->map[key] = NULL;}static voidraw_bind_key(map, key, func, next_map) int key; KEY_MAP *map; FUNCPTR func; KEY_MAP *next_map;{ if (map->map[key] == NULL) { if (key_count >= MAX_KEYS) return; map->map[key] = &key_table[key_count++]; } map->map[key]->func = func; map->map[key]->next = next_map;}static KEY_MAP *do_map_line(line) char line[];{ char *cp; char *map_name; cp = line; while (isspace(*cp)) cp++; if (*cp == '\0') return NULL; map_name = cp; while ((*cp != '\0') && !isspace(*cp)) cp++; *cp = '\0'; return find_map(map_name);}static voiddo_bind_line(map, line) KEY_MAP *map; char line[];{ char *cp; char key; char *func_name; char *next_name; KEY_MAP *next; FUNCPTR func; if (map == NULL) return; cp = line; key = *cp++; if (*cp == '\0') { unbind_key(map, key); return; } if (key == '^') { if (*cp == '?') { key = 0177; cp++; } else key = CONTROL(*cp++); } else if (key == '\\') key = *cp++; while (isspace(*cp)) cp++; if (*cp == '\0') { unbind_key(map, key); return; } func_name = cp; while ((*cp != '\0') && !isspace(*cp)) cp++; if (*cp) { *cp++ = '\0'; while (isspace(*cp)) cp++; } func = find_func(func_name); if (func == NULL) { fprintf(stderr, "Unknown function \"%s\"\n", func_name); return; } if (*cp == '\0') { next = map->default_ent.next; if (next == NULL) next = base_map; } else { next_name = cp; while ((*cp != '\0') && !isspace(*cp)) cp++; if (*cp) { *cp++ = '\0'; while (isspace(*cp)) cp++; } next = find_map(next_name); if (next == NULL) return; } raw_bind_key(map, key, func, next);}static voiddo_default_line(map, line) KEY_MAP *map; char *line;{ char *cp; char *func_name; char *next_name; KEY_MAP *next; FUNCPTR func; if (map == NULL) return; cp = line; while (isspace(*cp)) cp++; if (*cp == '\0') return; func_name = cp; while ((*cp != '\0') && !isspace(*cp)) cp++; if (*cp != '\0') { *cp++ = '\0'; while (isspace(*cp)) cp++; } func = find_func(func_name); if (func == NULL) return; if (*cp == '\0') next = map; else { next_name = cp; while ((*cp != '\0') && !isspace(*cp)) cp++; if (*cp != '\0') { *cp++ = '\0'; while (isspace(*cp)) cp++; } next = find_map(next_name); if (next == NULL) return; } map->default_ent.func = func; map->default_ent.next = next;}/* * Read bindings from specified file. * Returns nonzero on error. */static intread_bindings(bindfile) char *bindfile;{ char *cp; KEY_MAP *input_map; FILE *fp; char line[100]; base_map = find_map(base_map_name); cur_map = base_map; input_map = base_map; fp = fopen(bindfile, "r"); if (fp == NULL) return 1; while (fgets(line, sizeof(line) - 1, fp)) { cp = line; while (isspace(*cp)) cp++; if ((*cp == '\0') || (*cp == '#') || (*cp == '\n')) continue; if (cp[strlen(cp) - 1] == '\n') cp[strlen(cp) - 1] = '\0'; if (memcmp(cp, "map", 3) == 0) input_map = do_map_line(&cp[3]); else if (memcmp(cp, "default", 7) == 0) do_default_line(input_map, &cp[7]); else do_bind_line(input_map, cp); } fclose(fp); return 0;}static voidread_key(){ KEY_ENT *ent; int key; fflush(stdout); key = fgetc(stdin); if (key == EOF) { HS.linelen = 0; HS.buf[0] = '\0'; return; } ent = cur_map->map[key]; if (ent == NULL) ent = &cur_map->default_ent; if (ent->next) cur_map = ent->next; if (ent->func) (*ent->func)(key); else insert_char(key);}/* * Return the Nth history event, indexed from zero. * Earlier history events are lower in number. */static HIST *get_event(n) int n;{ register HIST * hp; if ((n < 0) || (n >= HS.histcount)) return NULL; hp = FIRSTHIST; while (n-- > 0) hp = NEXTHIST(hp); return hp;}/* * Search the history list for the specified pattern. * Returns the found history, or NULL. */static HIST *find_event(pat, len) int len; char * pat;{ register HIST * hp; for (hp = FIRSTHIST; hp->len; hp = NEXTHIST(hp)) { if ((hp->len == len) && (memcmp(hp->data, pat, len) == 0)) return hp; } return NULL;}/* * Insert a line into the end of the history table. * If the line already appears in the table, then it is moved to the end. * If the table is full, then the earliest commands are deleted as necessary. * Warning: the incoming line cannot point into the history table. */voidhist_saveline(line, len) int len; char * line;{ HIST * hp; HIST * hp2; int left; if ((len > 0) && (line[len - 1] == '\n')) len--; if (len <= 0) return; /* * See if the line is already present in the history table. * If so, and it is already at the end, then we are all done. * Otherwise delete it since we will reinsert it at the end. */ hp = find_event(line, len); if (hp) { hp2 = NEXTHIST(hp); left = histused - HISTOFFSET(hp2); if (left <= 0) return; histused -= HISTLEN(hp); memcpy(hp, hp2, left + 1); HS.histcount--; } /* * If there is not enough room left in the history buffer to add * the new command, then repeatedly delete the earliest command * as many times as necessary in order to make enough room. */ while ((histused + len) >= HIST_SIZE) { hp = (HIST *) histbuf; hp2 = NEXTHIST(hp); left = histused - HISTOFFSET(hp2); histused -= HISTLEN(hp); memcpy(hp, hp2, left + 1); HS.histcount--; } /* * Add the line to the end of the history table. */ hp = (HIST *) &histbuf[histused]; hp->len = len; memcpy(hp->data, line, len); histused += HISTLEN(hp); histbuf[histused] = 0; HS.curhist = ++HS.histcount;}/* * Find the function for a specified name. */static FUNCPTRfind_func(name) char *name;{ FUNC *fp; for (fp = funcs; fp->name; fp++) { if (strcmp(fp->name, name) == 0) return fp->func; } return NULL;}static voidarrow_key(){ switch (fgetc(stdin)) { case 'A': backward_history();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -