📄 emacs.c
字号:
/* * Emacs-like command line editing and history * * created by Ron Natalie at BRL * modified by Doug Kingston, Doug Gwyn, and Lou Salkind * adapted to PD ksh by Eric Gisin */#include "config.h"#ifdef EMACS#include "sh.h"#include "ksh_stat.h"#include "ksh_dir.h"#include <ctype.h>#include "edit.h"static Area aedit;#define AEDIT &aedit /* area for kill ring and macro defns */#undef CTRL /* _BSD brain damage */#define CTRL(x) ((x) == '?' ? 0x7F : (x) & 0x1F) /* ASCII */#define UNCTRL(x) ((x) == 0x7F ? '?' : (x) | 0x40) /* ASCII *//* values returned by keyboard functions */#define KSTD 0#define KEOL 1 /* ^M, ^J */#define KINTR 2 /* ^G, ^C */struct x_ftab { int (*xf_func) ARGS((int c)); const char *xf_name; short xf_flags;};/* index into struct x_ftab x_ftab[] - small is good */typedef unsigned char Findex;struct x_defbindings { Findex xdb_func; /* XFUNC_* */ unsigned char xdb_tab; unsigned char xdb_char;};#define XF_ARG 1 /* command takes number prefix */#define XF_NOBIND 2 /* not allowed to bind to function */#define XF_PREFIX 4 /* function sets prefix *//* Separator for completion */#define is_cfs(c) (c == ' ' || c == '\t' || c == '"' || c == '\'')#define is_mfs(c) (!(isalnum(c) || c == '_' || c == '$')) /* Separator for motion */#ifdef OS2 /* Deal with 8 bit chars & an extra prefix for function key (these two * changes increase memory usage from 9,216 bytes to 24,416 bytes...) */# define CHARMASK 0xFF /* 8-bit ASCII character mask */# define X_NTABS 4 /* normal, meta1, meta2, meta3 */static int x_prefix3 = 0xE0;#else /* OS2 */# define CHARMASK 0xFF /* 8-bit character mask */# define X_NTABS 3 /* normal, meta1, meta2 */#endif /* OS2 */#define X_TABSZ (CHARMASK+1) /* size of keydef tables etc *//* Arguments for do_complete() * 0 = enumerate M-= complete as much as possible and then list * 1 = complete M-Esc * 2 = list M-? */typedef enum { CT_LIST, /* list the possible completions */ CT_COMPLETE, /* complete to longest prefix */ CT_COMPLIST /* complete and then list (if non-exact) */ } Comp_type;/* { from 4.9 edit.h *//* * The following are used for my horizontal scrolling stuff */static char *xbuf; /* beg input buffer */static char *xend; /* end input buffer */static char *xcp; /* current position */static char *xep; /* current end */static char *xbp; /* start of visible portion of input buffer */static char *xlp; /* last char visible on screen */static int x_adj_ok;/* * we use x_adj_done so that functions can tell * whether x_adjust() has been called while they are active. */static int x_adj_done;static int xx_cols;static int x_col;static int x_displen;static int x_arg; /* general purpose arg */static int x_arg_defaulted;/* x_arg not explicitly set; defaulted to 1 */static int xlp_valid;/* end from 4.9 edit.h } */static int x_prefix1 = CTRL('['), x_prefix2 = CTRL('X');static char **x_histp; /* history position */static int x_nextcmd; /* for newline-and-next */static char *xmp; /* mark pointer */static Findex x_last_command;static Findex (*x_tab)[X_TABSZ]; /* key definition */static char *(*x_atab)[X_TABSZ]; /* macro definitions */static unsigned char x_bound[(X_TABSZ * X_NTABS + 7) / 8];#define KILLSIZE 20static char *killstack[KILLSIZE];static int killsp, killtp;static int x_curprefix;static char *macroptr;static int prompt_skip;static int x_ins ARGS((char *cp));static void x_delete ARGS((int nc, int force_push));static int x_bword ARGS((void));static int x_fword ARGS((void));static void x_goto ARGS((char *cp));static void x_bs ARGS((int c));static int x_size_str ARGS((char *cp));static int x_size ARGS((int c));static void x_zots ARGS((char *str));static void x_zotc ARGS((int c));static void x_load_hist ARGS((char **hp));static int x_search ARGS((char *pat, int sameline, int offset));static int x_match ARGS((char *str, char *pat));static void x_redraw ARGS((int limit));static void x_push ARGS((int nchars));static char * x_mapin ARGS((const char *cp));static char * x_mapout ARGS((int c));static void x_print ARGS((int prefix, int key));static void x_adjust ARGS((void));static void x_e_ungetc ARGS((int c));static int x_e_getc ARGS((void));static void x_e_putc ARGS((int c));static void x_e_puts ARGS((const char *s));static int x_fold_case ARGS((int c));static char *x_lastcp ARGS((void));static void do_complete ARGS((int flags, Comp_type type));/* The lines between START-FUNC-TAB .. END-FUNC-TAB are run through a * script (emacs-gen.sh) that generates emacs.out which contains: * - function declarations for x_* functions * - defines of the form XFUNC_<name> where <name> is function * name, sans leading x_. * Note that the script treats #ifdef and { 0, 0, 0} specially - use with * caution. */#include "emacs.out"static const struct x_ftab x_ftab[] = {/* @START-FUNC-TAB@ */ { x_abort, "abort", 0 }, { x_beg_hist, "beginning-of-history", 0 }, { x_comp_comm, "complete-command", 0 }, { x_comp_file, "complete-file", 0 }, { x_complete, "complete", 0 }, { x_del_back, "delete-char-backward", XF_ARG }, { x_del_bword, "delete-word-backward", XF_ARG }, { x_del_char, "delete-char-forward", XF_ARG }, { x_del_fword, "delete-word-forward", XF_ARG }, { x_del_line, "kill-line", 0 }, { x_draw_line, "redraw", 0 }, { x_end_hist, "end-of-history", 0 }, { x_end_of_text, "eot", 0 }, { x_enumerate, "list", 0 }, { x_eot_del, "eot-or-delete", XF_ARG }, { x_error, "error", 0 }, { x_goto_hist, "goto-history", XF_ARG }, { x_ins_string, "macro-string", XF_NOBIND }, { x_insert, "auto-insert", XF_ARG }, { x_kill, "kill-to-eol", XF_ARG }, { x_kill_region, "kill-region", 0 }, { x_list_comm, "list-command", 0 }, { x_list_file, "list-file", 0 }, { x_literal, "quote", 0 }, { x_meta1, "prefix-1", XF_PREFIX }, { x_meta2, "prefix-2", XF_PREFIX }, { x_meta_yank, "yank-pop", 0 }, { x_mv_back, "backward-char", XF_ARG }, { x_mv_begin, "beginning-of-line", 0 }, { x_mv_bword, "backward-word", XF_ARG }, { x_mv_end, "end-of-line", 0 }, { x_mv_forw, "forward-char", XF_ARG }, { x_mv_fword, "forward-word", XF_ARG }, { x_newline, "newline", 0 }, { x_next_com, "down-history", XF_ARG }, { x_nl_next_com, "newline-and-next", 0 }, { x_noop, "no-op", 0 }, { x_prev_com, "up-history", XF_ARG }, { x_prev_histword, "prev-hist-word", XF_ARG }, { x_search_char_forw, "search-character-forward", XF_ARG }, { x_search_char_back, "search-character-backward", XF_ARG }, { x_search_hist, "search-history", 0 }, { x_set_mark, "set-mark-command", 0 }, { x_stuff, "stuff", 0 }, { x_stuffreset, "stuff-reset", 0 }, { x_transpose, "transpose-chars", 0 }, { x_version, "version", 0 }, { x_xchg_point_mark, "exchange-point-and-mark", 0 }, { x_yank, "yank", 0 }, { x_comp_list, "complete-list", 0 }, { x_expand, "expand-file", 0 }, { x_fold_capitialize, "capitalize-word", XF_ARG }, { x_fold_lower, "downcase-word", XF_ARG }, { x_fold_upper, "upcase-word", XF_ARG }, { x_set_arg, "set-arg", XF_NOBIND }, { x_comment, "comment", 0 },#ifdef SILLY { x_game_of_life, "play-game-of-life", 0 },#else { 0, 0, 0 },#endif#ifdef DEBUG { x_debug_info, "debug-info", 0 },#else { 0, 0, 0 },#endif#ifdef OS2 { x_meta3, "prefix-3", XF_PREFIX },#else { 0, 0, 0 },#endif/* @END-FUNC-TAB@ */ };static struct x_defbindings const x_defbindings[] = { { XFUNC_del_back, 0, CTRL('?') }, { XFUNC_del_bword, 1, CTRL('?') }, { XFUNC_eot_del, 0, CTRL('D') }, { XFUNC_del_back, 0, CTRL('H') }, { XFUNC_del_bword, 1, CTRL('H') }, { XFUNC_del_bword, 1, 'h' }, { XFUNC_mv_bword, 1, 'b' }, { XFUNC_mv_fword, 1, 'f' }, { XFUNC_del_fword, 1, 'd' }, { XFUNC_mv_back, 0, CTRL('B') }, { XFUNC_mv_forw, 0, CTRL('F') }, { XFUNC_search_char_forw, 0, CTRL(']') }, { XFUNC_search_char_back, 1, CTRL(']') }, { XFUNC_newline, 0, CTRL('M') }, { XFUNC_newline, 0, CTRL('J') }, { XFUNC_end_of_text, 0, CTRL('_') }, { XFUNC_abort, 0, CTRL('G') }, { XFUNC_prev_com, 0, CTRL('P') }, { XFUNC_next_com, 0, CTRL('N') }, { XFUNC_nl_next_com, 0, CTRL('O') }, { XFUNC_search_hist, 0, CTRL('R') }, { XFUNC_beg_hist, 1, '<' }, { XFUNC_end_hist, 1, '>' }, { XFUNC_goto_hist, 1, 'g' }, { XFUNC_mv_end, 0, CTRL('E') }, { XFUNC_mv_begin, 0, CTRL('A') }, { XFUNC_draw_line, 0, CTRL('L') }, { XFUNC_meta1, 0, CTRL('[') }, { XFUNC_meta2, 0, CTRL('X') }, { XFUNC_kill, 0, CTRL('K') }, { XFUNC_yank, 0, CTRL('Y') }, { XFUNC_meta_yank, 1, 'y' }, { XFUNC_literal, 0, CTRL('^') }, { XFUNC_comment, 1, '#' },#if defined(BRL) && defined(TIOCSTI) { XFUNC_stuff, 0, CTRL('T') },#else { XFUNC_transpose, 0, CTRL('T') },#endif { XFUNC_complete, 1, CTRL('[') }, { XFUNC_comp_list, 1, '=' }, { XFUNC_enumerate, 1, '?' }, { XFUNC_expand, 1, '*' }, { XFUNC_comp_file, 1, CTRL('X') }, { XFUNC_comp_comm, 2, CTRL('[') }, { XFUNC_list_comm, 2, '?' }, { XFUNC_list_file, 2, CTRL('Y') }, { XFUNC_set_mark, 1, ' ' }, { XFUNC_kill_region, 0, CTRL('W') }, { XFUNC_xchg_point_mark, 2, CTRL('X') }, { XFUNC_version, 0, CTRL('V') },#ifdef DEBUG { XFUNC_debug_info, 1, CTRL('H') },#endif { XFUNC_prev_histword, 1, '.' }, { XFUNC_prev_histword, 1, '_' }, { XFUNC_set_arg, 1, '0' }, { XFUNC_set_arg, 1, '1' }, { XFUNC_set_arg, 1, '2' }, { XFUNC_set_arg, 1, '3' }, { XFUNC_set_arg, 1, '4' }, { XFUNC_set_arg, 1, '5' }, { XFUNC_set_arg, 1, '6' }, { XFUNC_set_arg, 1, '7' }, { XFUNC_set_arg, 1, '8' }, { XFUNC_set_arg, 1, '9' }, { XFUNC_fold_upper, 1, 'U' }, { XFUNC_fold_upper, 1, 'u' }, { XFUNC_fold_lower, 1, 'L' }, { XFUNC_fold_lower, 1, 'l' }, { XFUNC_fold_capitialize, 1, 'C' }, { XFUNC_fold_capitialize, 1, 'c' },#ifdef OS2 { XFUNC_meta3, 0, 0xE0 }, { XFUNC_mv_back, 3, 'K' }, { XFUNC_mv_forw, 3, 'M' }, { XFUNC_next_com, 3, 'P' }, { XFUNC_prev_com, 3, 'H' },#endif /* OS2 */ /* These for ansi arrow keys: arguablely shouldn't be here by * default, but its simpler/faster/smaller than using termcap * entries. */ { XFUNC_meta2, 1, '[' }, { XFUNC_prev_com, 2, 'A' }, { XFUNC_next_com, 2, 'B' }, { XFUNC_mv_forw, 2, 'C' }, { XFUNC_mv_back, 2, 'D' },};intx_emacs(buf, len) char *buf; size_t len;{ int c; const char *p; int i; Findex f; xbp = xbuf = buf; xend = buf + len; xlp = xcp = xep = buf; *xcp = 0; xlp_valid = TRUE; xmp = NULL; x_curprefix = 0; macroptr = (char *) 0; x_histp = histptr + 1; x_last_command = XFUNC_error; xx_cols = x_cols; x_col = promptlen(prompt, &p); prompt_skip = p - prompt; x_adj_ok = 1; x_displen = xx_cols - 2 - x_col; x_adj_done = 0; pprompt(prompt, 0); if (x_nextcmd >= 0) { int off = source->line - x_nextcmd; if (histptr - history >= off) x_load_hist(histptr - off); x_nextcmd = -1; } while (1) { x_flush(); if ((c = x_e_getc()) < 0) return 0; f = x_curprefix == -1 ? XFUNC_insert : x_tab[x_curprefix][c&CHARMASK]; if (!(x_ftab[f].xf_flags & XF_PREFIX) && x_last_command != XFUNC_set_arg) { x_arg = 1; x_arg_defaulted = 1; } i = c | (x_curprefix << 8); x_curprefix = 0; switch (i = (*x_ftab[f].xf_func)(i)) { case KSTD: if (!(x_ftab[f].xf_flags & XF_PREFIX)) x_last_command = f; break; case KEOL: i = xep - xbuf; return i; case KINTR: /* special case for interrupt */ trapsig(SIGINT); x_mode(FALSE); unwind(LSHELL); } }}static intx_insert(c) int c;{ char str[2]; /* * Should allow tab and control chars. */ if (c == 0) { x_e_putc(BEL); return KSTD; } str[0] = c; str[1] = '\0'; while (x_arg--) x_ins(str); return KSTD;}static intx_ins_string(c) int c;{ if (macroptr) { x_e_putc(BEL); return KSTD; } macroptr = x_atab[c>>8][c & CHARMASK]; if (macroptr && !*macroptr) { /* XXX bell? */ macroptr = (char *) 0; } return KSTD;}static intx_do_ins(cp, len) const char *cp; int len;{ if (xep+len >= xend) { x_e_putc(BEL); return -1; } memmove(xcp+len, xcp, xep - xcp + 1); memmove(xcp, cp, len); xcp += len; xep += len; return 0;}static intx_ins(s) char *s;{ char *cp = xcp; register int adj = x_adj_done; if (x_do_ins(s, strlen(s)) < 0) return -1; /* * x_zots() may result in a call to x_adjust() * we want xcp to reflect the new position. */ xlp_valid = FALSE; x_lastcp(); x_adj_ok = (xcp >= xlp); x_zots(cp); if (adj == x_adj_done) /* has x_adjust() been called? */ { /* no */ for (cp = xlp; cp > xcp; ) x_bs(*--cp); } x_adj_ok = 1; return 0;}static intx_del_back(c) int c;{ int col = xcp - xbuf; if (col == 0) { x_e_putc(BEL); return KSTD; } if (x_arg > col) x_arg = col; x_goto(xcp - x_arg); x_delete(x_arg, FALSE); return KSTD;}static intx_del_char(c) int c;{ int nleft = xep - xcp; if (!nleft) { x_e_putc(BEL); return KSTD; } if (x_arg > nleft) x_arg = nleft; x_delete(x_arg, FALSE); return KSTD;}/* Delete nc chars to the right of the cursor (including cursor position) */static voidx_delete(nc, force_push) int nc; int force_push;{ int i,j; char *cp; if (nc == 0) return; if (xmp != NULL && xmp > xcp) { if (xcp + nc > xmp) xmp = xcp; else xmp -= nc; } /* * This lets us yank a word we have deleted. */ if (nc > 1 || force_push) x_push(nc); xep -= nc; cp = xcp; j = 0; i = nc; while (i--) { j += x_size(*cp++); } memmove(xcp, xcp+nc, xep - xcp + 1); /* Copies the null */ x_adj_ok = 0; /* don't redraw */ x_zots(xcp); /* * if we are already filling the line, * there is no need to ' ','\b'. * But if we must, make sure we do the minimum. */ if ((i = xx_cols - 2 - x_col) > 0) { j = (j < i) ? j : i; i = j; while (i--) x_e_putc(' '); i = j; while (i--) x_e_putc('\b'); } /*x_goto(xcp);*/ x_adj_ok = 1; xlp_valid = FALSE; for (cp = x_lastcp(); cp > xcp; ) x_bs(*--cp); return; }static intx_del_bword(c) int c;{ x_delete(x_bword(), FALSE); return KSTD;}static intx_mv_bword(c) int c;{ (void)x_bword(); return KSTD;}static intx_mv_fword(c) int c;{ x_goto(xcp + x_fword()); return KSTD;}static intx_del_fword(c) int c;{ x_delete(x_fword(), FALSE); return KSTD;}static intx_bword(){ int nc = 0; register char *cp = xcp; if (cp == xbuf) { x_e_putc(BEL); return 0; } while (x_arg--) { while (cp != xbuf && is_mfs(cp[-1])) { cp--; nc++; } while (cp != xbuf && !is_mfs(cp[-1])) { cp--; nc++; } } x_goto(cp); return nc;}static intx_fword(){ int nc = 0; register char *cp = xcp; if (cp == xep) { x_e_putc(BEL); return 0; } while (x_arg--) { while (cp != xep && is_mfs(*cp)) { cp++; nc++; } while (cp != xep && !is_mfs(*cp)) { cp++; nc++; } } return nc;}static voidx_goto(cp) register char *cp;{ if (cp < xbp || cp >= (xbp + x_displen)) { /* we are heading off screen */ xcp = cp; x_adjust(); } else { if (cp < xcp) /* move back */ { while (cp < xcp) x_bs(*--xcp); } else { if (cp > xcp) /* move forward */ { while (cp > xcp) x_zotc(*xcp++); } } }}static voidx_bs(c) int c;{ register int i; i = x_size(c); while (i--) x_e_putc('\b');}static intx_size_str(cp) register char *cp;{ register int size = 0; while (*cp) size += x_size(*cp++); return size;}static intx_size(c) int c;{ if (c=='\t') return 4; /* Kludge, tabs are always four spaces. */ if (iscntrl(c)) /* control char */ return 2; return 1;}static voidx_zots(str) register char *str;{ register int adj = x_adj_done; x_lastcp(); while (*str && str < xlp && adj == x_adj_done) x_zotc(*str++);}static voidx_zotc(c) int c;{ if (c == '\t') { /* Kludge, tabs are always four spaces. */ x_e_puts(" "); } else if (iscntrl(c)) { x_e_putc('^'); x_e_putc(UNCTRL(c)); } else x_e_putc(c);}static intx_mv_back(c) int c;{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -