diff options
author | John Ankarström <john@ankarstrom.se> | 2021-06-07 14:12:11 +0200 |
---|---|---|
committer | John Ankarström <john@ankarstrom.se> | 2021-06-07 14:12:11 +0200 |
commit | e2294b61e15781ca784a611e8ca7dabe132ebc6d (patch) | |
tree | c2a8cc44582534f52a76b1b8e64e6f83ffa501da /emacs.c | |
download | ksh-e2294b61e15781ca784a611e8ca7dabe132ebc6d.tar.gz |
First commit (NetBSD 9.1)
Diffstat (limited to 'emacs.c')
-rw-r--r-- | emacs.c | 2174 |
1 files changed, 2174 insertions, 0 deletions
@@ -0,0 +1,2174 @@ +/* $NetBSD: emacs.c,v 1.38 2018/05/08 16:37:59 kamil Exp $ */ + +/* + * 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 <sys/cdefs.h> + +#ifndef lint +__RCSID("$NetBSD: emacs.c,v 1.38 2018/05/08 16:37:59 kamil Exp $"); +#endif + +#include "config.h" +#ifdef EMACS + +#include <sys/stat.h> +#include <ctype.h> +#include <locale.h> +#include <stdbool.h> + +#include "sh.h" +#include "ksh_dir.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 */ +#define META(x) ((x) & 0x7f) +#define ISMETA(x) (Flag(FEMACSUSEMETA) && ((x) & 0x80)) + + +/* 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((unsigned char)c) || c == '_' || c == '$')) /* Separator for motion */ + +# define CHARMASK 0xFF /* 8-bit character mask */ +# define X_NTABS 3 /* normal, meta1, meta2 */ +#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 20 +static char *killstack[KILLSIZE]; +static int killsp, killtp; +static int x_curprefix; +static char *macroptr; +static int prompt_trunc; +static int prompt_skip; + +static int x_ins ARGS((char *cp)); +static void x_delete ARGS((int nc, int 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, Area *area)); +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_comment ARGS((int c)); +static int x_fold_case ARGS((int c)); +static char *x_lastcp ARGS((void)); +static void do_complete ARGS((int flags, Comp_type type)); +static int x_emacs_putbuf ARGS((const char *s, size_t len)); + + +/* 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_capitalize, "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 }, + { 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, 0, CTRL('I') }, + { 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_capitalize, 1, 'C' }, + { XFUNC_fold_capitalize, 1, 'c' }, + /* 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_meta2, 1, 'O' }, + { XFUNC_prev_com, 2, 'A' }, + { XFUNC_next_com, 2, 'B' }, + { XFUNC_mv_forw, 2, 'C' }, + { XFUNC_mv_back, 2, 'D' }, +}; + +int +x_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; + prompt_trunc = x_col - (x_cols - 3 - MIN_EDIT_SPACE); + if (prompt_trunc > 0) + x_col -= prompt_trunc; + else + prompt_trunc = 0; + x_adj_ok = 1; + x_displen = xx_cols - 2 - x_col; + x_adj_done = 0; + + pprompt(prompt, prompt_trunc); + + if (x_nextcmd >= 0) { + int off = source->line - x_nextcmd; + if (histptr - histlist >= off) + x_load_hist(histptr - off); + x_nextcmd = -1; + } + + while (1) { + x_flush(); + if ((c = x_e_getc()) < 0) + return 0; + + if (ISMETA(c)) { + c = META(c); + x_curprefix = 1; + } + + 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 int +x_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 int +x_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 int x_do_ins(const char *cp, int len); + +static int +x_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 int +x_ins(s) + char *s; +{ + char *cp = xcp; + 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; +} + +/* + * this is used for x_escape() in do_complete() + */ +static int +x_emacs_putbuf(s, len) + const char *s; + size_t len; +{ + int rval; + + if ((rval = x_do_ins(s, len)) != 0) + return (rval); + return (rval); +} + +static int +x_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 int +x_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 void +x_delete(nc, push) + int nc; + int 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 (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 = x_displen) > 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 int +x_del_bword(c) + int c; +{ + x_delete(x_bword(), true); + return KSTD; +} + +static int +x_mv_bword(c) + int c; +{ + (void)x_bword(); + return KSTD; +} + +static int +x_mv_fword(c) + int c; +{ + x_goto(xcp + x_fword()); + return KSTD; +} + +static int +x_del_fword(c) + int c; +{ + x_delete(x_fword(), true); + return KSTD; +} + +static int +x_bword() +{ + int nc = 0; + 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 int +x_fword() +{ + int nc = 0; + 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 void +x_goto(cp) + 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 void +x_bs(c) + int c; +{ + int i; + i = x_size(c); + while (i--) + x_e_putc('\b'); +} + +static int +x_size_str(cp) + char *cp; +{ + int size = 0; + while (*cp) + size += x_size(*cp++); + return size; +} + +static int +x_size(c) + int c; +{ + if (c=='\t') + return 4; /* Kludge, tabs are always four spaces. */ + if (iscntrl((unsigned char)c)) /* control char */ + return 2; + return 1; +} + +static void +x_zots(str) + char *str; +{ + int adj = x_adj_done; + + x_lastcp(); + while (*str && str < xlp && adj == x_adj_done) + x_zotc(*str++); +} + +static void +x_zotc(c) + int c; +{ + if (c == '\t') { + /* Kludge, tabs are always four spaces. */ + x_e_puts(" "); + } else if (iscntrl((unsigned char)c)) { + x_e_putc('^'); + x_e_putc(UNCTRL(c)); + } else + x_e_putc(c); +} + +static int +x_mv_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); + return KSTD; +} + +static int +x_mv_forw(c) + int c; +{ + int nleft = xep - xcp; + + if (!nleft) { + x_e_putc(BEL); + return KSTD; + } + if (x_arg > nleft) + x_arg = nleft; + x_goto(xcp + x_arg); + return KSTD; +} + +static int +x_search_char_forw(c) + int c; +{ + char *cp = xcp; + + *xep = '\0'; + c = x_e_getc(); + while (x_arg--) { + if (c < 0 + || ((cp = (cp == xep) ? NULL : strchr(cp + 1, c)) == NULL + && (cp = strchr(xbuf, c)) == NULL)) + { + x_e_putc(BEL); + return KSTD; + } + } + x_goto(cp); + return KSTD; +} + +static int +x_search_char_back(c) + int c; +{ + char *cp = xcp, *p; + + c = x_e_getc(); + for (; x_arg--; cp = p) + for (p = cp; ; ) { + if (p-- == xbuf) + p = xep; + if (c < 0 || p == cp) { + x_e_putc(BEL); + return KSTD; + } + if (*p == c) + break; + } + x_goto(cp); + return KSTD; +} + +static int +x_newline(c) + int c; +{ + x_e_putc('\r'); + x_e_putc('\n'); + x_flush(); + *xep++ = '\n'; + return KEOL; +} + +static int +x_end_of_text(c) + int c; +{ + return KEOL; +} + +static int x_beg_hist(c) int c; { x_load_hist(histlist); return KSTD;} + +static int x_end_hist(c) int c; { x_load_hist(histptr); return KSTD;} + +static int x_prev_com(c) int c; { x_load_hist(x_histp - x_arg); return KSTD;} + +static int x_next_com(c) int c; { x_load_hist(x_histp + x_arg); return KSTD;} + +/* Goto a particular history number obtained from argument. + * If no argument is given history 1 is probably not what you + * want so we'll simply go to the oldest one. + */ +static int +x_goto_hist(c) + int c; +{ + if (x_arg_defaulted) + x_load_hist(histlist); + else + x_load_hist(histptr + x_arg - source->line); + return KSTD; +} + +static void +x_load_hist(hp) + char **hp; +{ + int oldsize; + + if (hp < histlist || hp > histptr) { + x_e_putc(BEL); + return; + } + x_histp = hp; + oldsize = x_size_str(xbuf); + strlcpy(xbuf, *hp, xend - xbuf); + xbp = xbuf; + xep = xcp = xbuf + strlen(xbuf); + xlp_valid = false; + if (xep > x_lastcp()) + x_goto(xep); + else + x_redraw(oldsize); +} + +static int +x_nl_next_com(c) + int c; +{ + x_nextcmd = source->line - (histptr - x_histp) + 1; + return (x_newline(c)); +} + +static int +x_eot_del(c) + int c; +{ + if (xep == xbuf && x_arg_defaulted) + return (x_end_of_text(c)); + else + return (x_del_char(c)); +} + +/* reverse incremental history search */ +static int +x_search_hist(c) + int c; +{ + int offset = -1; /* offset of match in xbuf, else -1 */ + char pat [256+1]; /* pattern buffer */ + char *p = pat; + Findex f; + + *p = '\0'; + while (1) { + if (offset < 0) { + x_e_puts("\nI-search: "); + x_e_puts(pat); + } + x_flush(); + if ((c = x_e_getc()) < 0) + return KSTD; + f = x_tab[0][c&CHARMASK]; + if (c == CTRL('[')) + break; + else if (f == XFUNC_search_hist) + offset = x_search(pat, 0, offset); + else if (f == XFUNC_del_back) { + if (p == pat) { + offset = -1; + break; + } + if (p > pat) + *--p = '\0'; + if (p == pat) + offset = -1; + else + offset = x_search(pat, 1, offset); + continue; + } else if (f == XFUNC_insert) { + /* add char to pattern */ + /* overflow check... */ + if (p >= &pat[sizeof(pat) - 1]) { + x_e_putc(BEL); + continue; + } + *p++ = c, *p = '\0'; + if (offset >= 0) { + /* already have partial match */ + offset = x_match(xbuf, pat); + if (offset >= 0) { + x_goto(xbuf + offset + (p - pat) - (*pat == '^')); + continue; + } + } + offset = x_search(pat, 0, offset); + } else { /* other command */ + x_e_ungetc(c); + break; + } + } + if (offset < 0) + x_redraw(-1); + return KSTD; +} + +/* search backward from current line */ +static int +x_search(pat, sameline, offset) + char *pat; + int sameline; + int offset; +{ + char **hp; + int i; + + for (hp = x_histp - (sameline ? 0 : 1) ; hp >= histlist; --hp) { + i = x_match(*hp, pat); + if (i >= 0) { + if (offset < 0) + x_e_putc('\n'); + x_load_hist(hp); + x_goto(xbuf + i + strlen(pat) - (*pat == '^')); + return i; + } + } + x_e_putc(BEL); + x_histp = histptr; + return -1; +} + +/* return position of first match of pattern in string, else -1 */ +static int +x_match(str, pat) + char *str, *pat; +{ + if (*pat == '^') { + return (strncmp(str, pat+1, strlen(pat+1)) == 0) ? 0 : -1; + } else { + char *q = strstr(str, pat); + return (q == NULL) ? -1 : q - str; + } +} + +static int +x_del_line(c) + int c; +{ + int i, j; + + *xep = 0; + i = xep - xbuf; + j = x_size_str(xbuf); + xcp = xbuf; + x_push(i); + xlp = xbp = xep = xbuf; + xlp_valid = true; + *xcp = 0; + xmp = NULL; + x_redraw(j); + return KSTD; +} + +static int +x_mv_end(c) + int c; +{ + x_goto(xep); + return KSTD; +} + +static int +x_mv_begin(c) + int c; +{ + x_goto(xbuf); + return KSTD; +} + +static int +x_draw_line(c) + int c; +{ + x_redraw(-1); + return KSTD; + +} + +/* Redraw (part of) the line. If limit is < 0, the everything is redrawn + * on a NEW line, otherwise limit is the screen column up to which needs + * redrawing. + */ +static void +x_redraw(limit) + int limit; +{ + int i, j; + char *cp; + + x_adj_ok = 0; + if (limit == -1) + x_e_putc('\n'); + else + x_e_putc('\r'); + x_flush(); + if (xbp == xbuf) + { + pprompt(prompt + prompt_skip, 0); + x_col = promptlen(prompt, (const char **) 0); + } + x_displen = xx_cols - 2 - x_col; + xlp_valid = false; + cp = x_lastcp(); + x_zots(xbp); + if (xbp != xbuf || xep > xlp) + limit = xx_cols; + if (limit >= 0) + { + if (xep > xlp) + i = 0; /* we fill the line */ + else + i = limit - (xlp - xbp); + + for (j = 0; j < i && x_col < (xx_cols - 2); j++) + x_e_putc(' '); + i = ' '; + if (xep > xlp) /* more off screen */ + { + if (xbp > xbuf) + i = '*'; + else + i = '>'; + } + else + if (xbp > xbuf) + i = '<'; + x_e_putc(i); + j++; + while (j--) + x_e_putc('\b'); + } + for (cp = xlp; cp > xcp; ) + x_bs(*--cp); + x_adj_ok = 1; + D__(x_flush();) + return; +} + +static int +x_transpose(c) + int c; +{ + char tmp; + + /* What transpose is meant to do seems to be up for debate. This + * is a general summary of the options; the text is abcd with the + * upper case character or underscore indicating the cursor position: + * Who Before After Before After + * at&t ksh in emacs mode: abCd abdC abcd_ (bell) + * at&t ksh in gmacs mode: abCd baCd abcd_ abdc_ + * gnu emacs: abCd acbD abcd_ abdc_ + * Pdksh currently goes with GNU behavior since I believe this is the + * most common version of emacs, unless in gmacs mode, in which case + * it does the at&t ksh gmacs mode. + * This should really be broken up into 3 functions so users can bind + * to the one they want. + */ + if (xcp == xbuf) { + x_e_putc(BEL); + return KSTD; + } else if (xcp == xep || Flag(FGMACS)) { + if (xcp - xbuf == 1) { + x_e_putc(BEL); + return KSTD; + } + /* Gosling/Unipress emacs style: Swap two characters before the + * cursor, do not change cursor position + */ + x_bs(xcp[-1]); + x_bs(xcp[-2]); + x_zotc(xcp[-1]); + x_zotc(xcp[-2]); + tmp = xcp[-1]; + xcp[-1] = xcp[-2]; + xcp[-2] = tmp; + } else { + /* GNU emacs style: Swap the characters before and under the + * cursor, move cursor position along one. + */ + x_bs(xcp[-1]); + x_zotc(xcp[0]); + x_zotc(xcp[-1]); + tmp = xcp[-1]; + xcp[-1] = xcp[0]; + xcp[0] = tmp; + x_bs(xcp[0]); + x_goto(xcp + 1); + } + return KSTD; +} + +static int +x_literal(c) + int c; +{ + x_curprefix = -1; + return KSTD; +} + +static int +x_meta1(c) + int c; +{ + x_curprefix = 1; + return KSTD; +} + +static int +x_meta2(c) + int c; +{ + x_curprefix = 2; + return KSTD; +} + +static int +x_kill(c) + int c; +{ + int col = xcp - xbuf; + int lastcol = xep - xbuf; + int ndel; + + if (x_arg_defaulted) + x_arg = lastcol; + else if (x_arg > lastcol) + x_arg = lastcol; + ndel = x_arg - col; + if (ndel < 0) { + x_goto(xbuf + x_arg); + ndel = -ndel; + } + x_delete(ndel, true); + return KSTD; +} + +static void +x_push(nchars) + int nchars; +{ + char *cp = str_nsave(xcp, nchars, AEDIT); + if (killstack[killsp]) + afree((void *)killstack[killsp], AEDIT); + killstack[killsp] = cp; + killsp = (killsp + 1) % KILLSIZE; +} + +static int +x_yank(c) + int c; +{ + if (killsp == 0) + killtp = KILLSIZE; + else + killtp = killsp; + killtp--; + if (killstack[killtp] == 0) { + x_e_puts("\nnothing to yank"); + x_redraw(-1); + return KSTD; + } + xmp = xcp; + x_ins(killstack[killtp]); + return KSTD; +} + +static int +x_meta_yank(c) + int c; +{ + int len; + if ((x_last_command != XFUNC_yank && x_last_command != XFUNC_meta_yank) + || killstack[killtp] == 0) { + killtp = killsp; + x_e_puts("\nyank something first"); + x_redraw(-1); + return KSTD; + } + len = strlen(killstack[killtp]); + x_goto(xcp - len); + x_delete(len, false); + do { + if (killtp == 0) + killtp = KILLSIZE - 1; + else + killtp--; + } while (killstack[killtp] == 0); + x_ins(killstack[killtp]); + return KSTD; +} + +static int +x_abort(c) + int c; +{ + /* x_zotc(c); */ + xlp = xep = xcp = xbp = xbuf; + xlp_valid = true; + *xcp = 0; + return KINTR; +} + +static int +x_error(c) + int c; +{ + x_e_putc(BEL); + return KSTD; +} + +static int +x_stuffreset(c) + int c; +{ +#ifdef TIOCSTI + (void)x_stuff(c); + return KINTR; +#else + x_zotc(c); + xlp = xcp = xep = xbp = xbuf; + xlp_valid = true; + *xcp = 0; + x_redraw(-1); + return KSTD; +#endif +} + +static int +x_stuff(c) + int c; +{ +#if 0 || defined TIOCSTI + char ch = c; + bool savmode = x_mode(false); + + (void)ioctl(TTY, TIOCSTI, &ch); + (void)x_mode(savmode); + x_redraw(-1); +#endif + return KSTD; +} + +static char * +x_mapin(cp, area) + const char *cp; + Area *area; +{ + char *new, *op; + + op = new = str_save(cp, area); + while (*cp) { + /* XXX -- should handle \^ escape? */ + if (*cp == '^') { + cp++; + if (*cp >= '?') /* includes '?'; ASCII */ + *op++ = CTRL(*cp); + else { + *op++ = '^'; + cp--; + } + } else + *op++ = *cp; + cp++; + } + *op = '\0'; + + return new; +} + +static char * +x_mapout(c) + int c; +{ + static char buf[8]; + char *p = buf; + + if (iscntrl((unsigned char)c)) { + *p++ = '^'; + *p++ = UNCTRL(c); + } else + *p++ = c; + *p = 0; + return buf; +} + +static void +x_print(prefix, key) + int prefix, key; +{ + if (prefix == 1) + shprintf("%s", x_mapout(x_prefix1)); + if (prefix == 2) + shprintf("%s", x_mapout(x_prefix2)); + + shprintf("%s = ", x_mapout(key)); + if (x_tab[prefix][key] != XFUNC_ins_string) + shprintf("%s\n", x_ftab[x_tab[prefix][key]].xf_name); + else + shprintf("'%s'\n", x_atab[prefix][key]); +} + +int +x_bind(a1, a2, macro, list) + const char *a1, *a2; + int macro; /* bind -m */ + int list; /* bind -l */ +{ + Findex f; + int prefix, key; + char *sp = NULL; + char *m1, *m2; + + if (x_tab == NULL) { + bi_errorf("cannot bind, not a tty"); + return 1; + } + + /* List function names */ + if (list) { + for (f = 0; f < NELEM(x_ftab); f++) + if (x_ftab[f].xf_name + && !(x_ftab[f].xf_flags & XF_NOBIND)) + shprintf("%s\n", x_ftab[f].xf_name); + return 0; + } + + if (a1 == NULL) { + for (prefix = 0; prefix < X_NTABS; prefix++) + for (key = 0; key < X_TABSZ; key++) { + f = x_tab[prefix][key]; + if (f == XFUNC_insert || f == XFUNC_error + || (macro && f != XFUNC_ins_string)) + continue; + x_print(prefix, key); + } + return 0; + } + + m2 = m1 = x_mapin(a1, ATEMP); + prefix = key = 0; + for (;; m1++) { + key = *m1 & CHARMASK; + if (x_tab[prefix][key] == XFUNC_meta1) + prefix = 1; + else if (x_tab[prefix][key] == XFUNC_meta2) + prefix = 2; + else + break; + } + afree(m2, ATEMP); + + if (a2 == NULL) { + x_print(prefix, key); + return 0; + } + + if (*a2 == 0) + f = XFUNC_insert; + else if (!macro) { + for (f = 0; f < NELEM(x_ftab); f++) + if (x_ftab[f].xf_name + && strcmp(x_ftab[f].xf_name, a2) == 0) + break; + if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) { + bi_errorf("%s: no such function", a2); + return 1; + } +#if 0 /* This breaks the bind commands that map arrow keys */ + if (f == XFUNC_meta1) + x_prefix1 = key; + if (f == XFUNC_meta2) + x_prefix2 = key; +#endif /* 0 */ + } else { + f = XFUNC_ins_string; + sp = x_mapin(a2, AEDIT); + } + + if (x_tab[prefix][key] == XFUNC_ins_string && x_atab[prefix][key]) + afree((void *)x_atab[prefix][key], AEDIT); + x_tab[prefix][key] = f; + x_atab[prefix][key] = sp; + + /* Track what the user has bound so x_emacs_keys() won't toast things */ + if (f == XFUNC_insert) + x_bound[(prefix * X_TABSZ + key) / 8] &= + ~(1 << ((prefix * X_TABSZ + key) % 8)); + else + x_bound[(prefix * X_TABSZ + key) / 8] |= + (1 << ((prefix * X_TABSZ + key) % 8)); + + return 0; +} + +void +x_init_emacs() +{ + size_t i; + int j; + char *locale; + + ainit(AEDIT); + x_nextcmd = -1; + + x_tab = (Findex (*)[X_TABSZ]) alloc(sizeofN(*x_tab, X_NTABS), AEDIT); + for (j = 0; j < X_TABSZ; j++) + x_tab[0][j] = XFUNC_insert; + for (i = 1; i < X_NTABS; i++) + for (j = 0; j < X_TABSZ; j++) + x_tab[i][j] = XFUNC_error; + for (i = 0; i < NELEM(x_defbindings); i++) + x_tab[(unsigned char)x_defbindings[i].xdb_tab][x_defbindings[i].xdb_char] + = x_defbindings[i].xdb_func; + + x_atab = (char *(*)[X_TABSZ]) alloc(sizeofN(*x_atab, X_NTABS), AEDIT); + for (i = 1; i < X_NTABS; i++) + for (j = 0; j < X_TABSZ; j++) + x_atab[i][j] = NULL; + + /* Determine if we can translate meta key or use 8-bit AscII + * XXX - It would be nice if there was a locale attribute to + * determine if the locale is 7-bit or not. + */ + locale = setlocale(LC_CTYPE, NULL); + if (locale == NULL || !strcmp(locale, "C") || !strcmp(locale, "POSIX")) + Flag(FEMACSUSEMETA) = 0; +} + +static void bind_if_not_bound(int p, int k, int func); + +static void +bind_if_not_bound(p, k, func) + int p, k; + int func; +{ + /* Has user already bound this key? If so, don't override it */ + if (x_bound[((p) * X_TABSZ + (k)) / 8] + & (1 << (((p) * X_TABSZ + (k)) % 8))) + return; + + x_tab[p][k] = func; +} + +void +x_emacs_keys(ec) + X_chars *ec; +{ + if (ec->erase >= 0) { + bind_if_not_bound(0, ec->erase, XFUNC_del_back); + bind_if_not_bound(1, ec->erase, XFUNC_del_bword); + } + if (ec->kill >= 0) + bind_if_not_bound(0, ec->kill, XFUNC_del_line); + if (ec->werase >= 0) + bind_if_not_bound(0, ec->werase, XFUNC_del_bword); + if (ec->intr >= 0) + bind_if_not_bound(0, ec->intr, XFUNC_abort); + if (ec->quit >= 0) + bind_if_not_bound(0, ec->quit, XFUNC_noop); +} + +static int +x_set_mark(c) + int c; +{ + xmp = xcp; + return KSTD; +} + +static int +x_kill_region(c) + int c; +{ + int rsize; + char *xr; + + if (xmp == NULL) { + x_e_putc(BEL); + return KSTD; + } + if (xmp > xcp) { + rsize = xmp - xcp; + xr = xcp; + } else { + rsize = xcp - xmp; + xr = xmp; + } + x_goto(xr); + x_delete(rsize, true); + xmp = xr; + return KSTD; +} + +static int +x_xchg_point_mark(c) + int c; +{ + char *tmp; + + if (xmp == NULL) { + x_e_putc(BEL); + return KSTD; + } + tmp = xmp; + xmp = xcp; + x_goto( tmp ); + return KSTD; +} + +static int +x_version(c) + int c; +{ + char *o_xbuf = xbuf, *o_xend = xend; + char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp; + int lim = x_lastcp() - xbp; + + xbuf = xbp = xcp = ksh_version + 4; + xend = xep = ksh_version + 4 + strlen(ksh_version + 4); + x_redraw(lim); + x_flush(); + + c = x_e_getc(); + xbuf = o_xbuf; + xend = o_xend; + xbp = o_xbp; + xep = o_xep; + xcp = o_xcp; + x_redraw(strlen(ksh_version)); + + if (c < 0) + return KSTD; + /* This is what at&t ksh seems to do... Very bizarre */ + if (c != ' ') + x_e_ungetc(c); + + return KSTD; +} + +static int +x_noop(c) + int c; +{ + return KSTD; +} + +#ifdef SILLY +static int +x_game_of_life(c) + int c; +{ + char newbuf [256+1]; + char *ip, *op; + int i, len; + + i = xep - xbuf; + *xep = 0; + len = x_size_str(xbuf); + xcp = xbp = xbuf; + memmove(newbuf+1, xbuf, i); + newbuf[0] = 'A'; + newbuf[i] = 'A'; + for (ip = newbuf+1, op = xbuf; --i >= 0; ip++, op++) { + /* Empty space */ + if (*ip < '@' || *ip == '_' || *ip == 0x7F) { + /* Two adults, make whoopee */ + if (ip[-1] < '_' && ip[1] < '_') { + /* Make kid look like parents. */ + *op = '`' + ((ip[-1] + ip[1])/2)%32; + if (*op == 0x7F) /* Birth defect */ + *op = '`'; + } + else + *op = ' '; /* nothing happens */ + continue; + } + /* Child */ + if (*ip > '`') { + /* All alone, dies */ + if (ip[-1] == ' ' && ip[1] == ' ') + *op = ' '; + else /* Gets older */ + *op = *ip-'`'+'@'; + continue; + } + /* Adult */ + /* Overcrowded, dies */ + if (ip[-1] >= '@' && ip[1] >= '@') { + *op = ' '; + continue; + } + *op = *ip; + } + *op = 0; + x_redraw(len); + return KSTD; +} +#endif + +/* + * File/command name completion routines + */ + + +static int +x_comp_comm(c) + int c; +{ + do_complete(XCF_COMMAND, CT_COMPLETE); + return KSTD; +} +static int +x_list_comm(c) + int c; +{ + do_complete(XCF_COMMAND, CT_LIST); + return KSTD; +} +static int +x_complete(c) + int c; +{ + do_complete(XCF_COMMAND_FILE, CT_COMPLETE); + return KSTD; +} +static int +x_enumerate(c) + int c; +{ + do_complete(XCF_COMMAND_FILE, CT_LIST); + return KSTD; +} +static int +x_comp_file(c) + int c; +{ + do_complete(XCF_FILE, CT_COMPLETE); + return KSTD; +} +static int +x_list_file(c) + int c; +{ + do_complete(XCF_FILE, CT_LIST); + return KSTD; +} +static int +x_comp_list(c) + int c; +{ + do_complete(XCF_COMMAND_FILE, CT_COMPLIST); + return KSTD; +} +static int +x_expand(c) + int c; +{ + char **words; + int nwords = 0; + int start, end; + int is_command; + int i; + + nwords = x_cf_glob(XCF_FILE, + xbuf, xep - xbuf, xcp - xbuf, + &start, &end, &words, &is_command); + + if (nwords == 0) { + x_e_putc(BEL); + return KSTD; + } + + x_goto(xbuf + start); + x_delete(end - start, false); + for (i = 0; i < nwords;) { + if (x_escape(words[i], strlen(words[i]), x_emacs_putbuf) < 0 || + (++i < nwords && x_ins(space) < 0)) + { + x_e_putc(BEL); + return KSTD; + } + } + x_adjust(); + + return KSTD; +} + +/* type == 0 for list, 1 for complete and 2 for complete-list */ +static void +do_complete(flags, type) + int flags; /* XCF_{COMMAND,FILE,COMMAND_FILE} */ + Comp_type type; +{ + char **words; + int nwords; + int start, end, nlen, olen; + int is_command; + int completed = 0; + + nwords = x_cf_glob(flags, xbuf, xep - xbuf, xcp - xbuf, + &start, &end, &words, &is_command); + /* no match */ + if (nwords == 0) { + x_e_putc(BEL); + return; + } + + if (type == CT_LIST) { + x_print_expansions(nwords, words, is_command); + x_redraw(0); + x_free_words(nwords, words); + return; + } + + olen = end - start; + nlen = x_longest_prefix(nwords, words); + /* complete */ + if (nwords == 1 || nlen > olen) { + x_goto(xbuf + start); + x_delete(olen, false); + x_escape(words[0], nlen, x_emacs_putbuf); + x_adjust(); + completed = 1; + } + /* add space if single non-dir match */ + if ((nwords == 1) && (!ISDIRSEP(words[0][nlen - 1]))) { + x_ins(space); + completed = 1; + } + + if (type == CT_COMPLIST && !completed) { + x_print_expansions(nwords, words, is_command); + completed = 1; + } + + if (completed) + x_redraw(0); + + x_free_words(nwords, words); +} + +/* NAME: + * x_adjust - redraw the line adjusting starting point etc. + * + * DESCRIPTION: + * This function is called when we have exceeded the bounds + * of the edit window. It increments x_adj_done so that + * functions like x_ins and x_delete know that we have been + * called and can skip the x_bs() stuff which has already + * been done by x_redraw. + * + * RETURN VALUE: + * None + */ + +static void +x_adjust() +{ + x_adj_done++; /* flag the fact that we were called. */ + /* + * we had a problem if the prompt length > xx_cols / 2 + */ + if ((xbp = xcp - (x_displen / 2)) < xbuf) + xbp = xbuf; + xlp_valid = false; + x_redraw(xx_cols); + x_flush(); +} + +static int unget_char = -1; + +static void +x_e_ungetc(c) + int c; +{ + unget_char = c; +} + +static int +x_e_getc() +{ + int c; + + if (unget_char >= 0) { + c = unget_char; + unget_char = -1; + } else { + if (macroptr) { + c = (unsigned char) *macroptr++; + if (!*macroptr) + macroptr = (char *) 0; + } else + c = x_getc(); + } + + return c <= CHARMASK ? c : (c & CHARMASK); +} + +static void +x_e_putc(c) + int c; +{ + if (c == '\r' || c == '\n') + x_col = 0; + if (x_col < xx_cols) + { + x_putc(c); + switch(c) + { + case BEL: + break; + case '\r': + case '\n': + break; + case '\b': + x_col--; + break; + default: + x_col++; + break; + } + } + if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2))) + { + x_adjust(); + } +} + +#ifdef DEBUG +static int +x_debug_info(c) + int c; +{ + x_flush(); + shellf("\nksh debug:\n"); + shellf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n", + x_col, xx_cols, x_displen); + shellf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep); + shellf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf); + shellf("\txlp == 0x%lx\n", (long) xlp); + shellf("\txlp == 0x%lx\n", (long) x_lastcp()); + shellf(newline); + x_redraw(-1); + return 0; +} +#endif + +static void +x_e_puts(s) + const char *s; +{ + int adj = x_adj_done; + + while (*s && adj == x_adj_done) + x_e_putc(*s++); +} + +/* NAME: + * x_set_arg - set an arg value for next function + * + * DESCRIPTION: + * This is a simple implementation of M-[0-9]. + * + * RETURN VALUE: + * KSTD + */ + +static int +x_set_arg(c) + int c; +{ + int n = 0; + int first = 1; + + c &= CHARMASK; /* strip command prefix */ + for (; c >= 0 && isdigit(c); c = x_e_getc(), first = 0) + n = n * 10 + (c - '0'); + if (c < 0 || first) { + x_e_putc(BEL); + x_arg = 1; + x_arg_defaulted = 1; + } else { + x_e_ungetc(c); + x_arg = n; + x_arg_defaulted = 0; + } + return KSTD; +} + + +/* Comment or uncomment the current line. */ +static int +x_comment(c) + int c; +{ + int oldsize = x_size_str(xbuf); + int len = xep - xbuf; + int ret = x_do_comment(xbuf, xend - xbuf, &len); + + if (ret < 0) + x_e_putc(BEL); + else { + xep = xbuf + len; + *xep = '\0'; + xcp = xbp = xbuf; + x_redraw(oldsize); + if (ret > 0) + return x_newline('\n'); + } + return KSTD; +} + + +/* NAME: + * x_prev_histword - recover word from prev command + * + * DESCRIPTION: + * This function recovers the last word from the previous + * command and inserts it into the current edit line. If a + * numeric arg is supplied then the n'th word from the + * start of the previous command is used. + * + * Bound to M-. + * + * RETURN VALUE: + * KSTD + */ + +static int +x_prev_histword(c) + int c; +{ + char *rcp; + char *cp; + + cp = *histptr; + if (!cp) + x_e_putc(BEL); + else if (x_arg_defaulted) { + rcp = &cp[strlen(cp) - 1]; + /* + * ignore white-space after the last word + */ + while (rcp > cp && is_cfs(*rcp)) + rcp--; + while (rcp > cp && !is_cfs(*rcp)) + rcp--; + if (is_cfs(*rcp)) + rcp++; + x_ins(rcp); + } else { + int i; + + rcp = cp; + /* + * ignore white-space at start of line + */ + while (*rcp && is_cfs(*rcp)) + rcp++; + while (x_arg-- > 1) + { + while (*rcp && !is_cfs(*rcp)) + rcp++; + while (*rcp && is_cfs(*rcp)) + rcp++; + } + cp = rcp; + while (*rcp && !is_cfs(*rcp)) + rcp++; + i = *rcp; + *rcp = '\0'; + x_ins(cp); + *rcp = i; + } + return KSTD; +} + +/* Uppercase N(1) words */ +static int +x_fold_upper(c) + int c; +{ + return x_fold_case('U'); +} + +/* Lowercase N(1) words */ +static int +x_fold_lower(c) + int c; +{ + return x_fold_case('L'); +} + +/* Lowercase N(1) words */ +static int +x_fold_capitalize(c) + int c; +{ + return x_fold_case('C'); +} + +/* NAME: + * x_fold_case - convert word to UPPER/lower/Capital case + * + * DESCRIPTION: + * This function is used to implement M-U,M-u,M-L,M-l,M-C and M-c + * to UPPER case, lower case or Capitalize words. + * + * RETURN VALUE: + * None + */ + +static int +x_fold_case(c) + int c; +{ + char *cp = xcp; + + if (cp == xep) { + x_e_putc(BEL); + return KSTD; + } + while (x_arg--) { + /* + * first skip over any white-space + */ + while (cp != xep && is_mfs(*cp)) + cp++; + /* + * do the first char on its own since it may be + * a different action than for the rest. + */ + if (cp != xep) { + if (c == 'L') { /* lowercase */ + if (isupper((unsigned char)*cp)) + *cp = tolower((unsigned char)*cp); + } else { /* uppercase, capitialize */ + if (islower((unsigned char)*cp)) + *cp = toupper((unsigned char)*cp); + } + cp++; + } + /* + * now for the rest of the word + */ + while (cp != xep && !is_mfs((unsigned char)*cp)) { + if (c == 'U') { /* uppercase */ + if (islower((unsigned char)*cp)) + *cp = toupper((unsigned char)*cp); + } else { /* lowercase, capitialize */ + if (isupper((unsigned char)*cp)) + *cp = tolower((unsigned char)*cp); + } + cp++; + } + } + x_goto(cp); + return KSTD; +} + +/* NAME: + * x_lastcp - last visible char + * + * SYNOPSIS: + * x_lastcp() + * + * DESCRIPTION: + * This function returns a pointer to that char in the + * edit buffer that will be the last displayed on the + * screen. The sequence: + * + * for (cp = x_lastcp(); cp > xcp; cp) + * x_bs(*--cp); + * + * Will position the cursor correctly on the screen. + * + * RETURN VALUE: + * cp or NULL + */ + +static char * +x_lastcp() +{ + char *rcp; + int i; + + if (!xlp_valid) + { + for (i = 0, rcp = xbp; rcp < xep && i < x_displen; rcp++) + i += x_size(*rcp); + xlp = rcp; + } + xlp_valid = true; + return (xlp); +} + +#endif /* EDIT */ |