diff options
author | Jason Downs <downsj@cvs.openbsd.org> | 1996-08-14 06:19:13 +0000 |
---|---|---|
committer | Jason Downs <downsj@cvs.openbsd.org> | 1996-08-14 06:19:13 +0000 |
commit | 3039b959c7de1e35fe4ec27ae02f1c8fe8d4d4ac (patch) | |
tree | 09c6b5a4f13282e07afe153445fcfaa0d10651f4 /bin/ksh/vi.c | |
parent | 596c2317eee31ab9606c6b88085644407d4d2f0f (diff) |
Import pdksh 5.2.7.
Diffstat (limited to 'bin/ksh/vi.c')
-rw-r--r-- | bin/ksh/vi.c | 2208 |
1 files changed, 2208 insertions, 0 deletions
diff --git a/bin/ksh/vi.c b/bin/ksh/vi.c new file mode 100644 index 00000000000..c4b2aeeee33 --- /dev/null +++ b/bin/ksh/vi.c @@ -0,0 +1,2208 @@ +/* $OpenBSD: vi.c,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ + +/* + * vi command editing + * written by John Rochester (initially for nsh) + * bludgeoned to fit pdksh by Larry Bouzane, Jeff Sparkes & Eric Gisin + * + */ +#include "config.h" +#ifdef VI + +#include "sh.h" +#include <ctype.h> +#include "ksh_stat.h" /* completion */ +#include "edit.h" + +#define CMDLEN 1024 +#define Ctrl(c) (c&0x1f) +#define is_wordch(c) (letnum(c)) + +struct edstate { + int winleft; + char *cbuf; + int cbufsize; + int linelen; + int cursor; +}; + + +static int vi_hook ARGS((int ch)); +static void vi_reset ARGS((char *buf, size_t len)); +static int nextstate ARGS((int ch)); +static int vi_insert ARGS((int ch)); +static int vi_cmd ARGS((int argcnt, const char *cmd)); +static int domove ARGS((int argcnt, const char *cmd, int sub)); +static int redo_insert ARGS((int count)); +static void yank_range ARGS((int a, int b)); +static int bracktype ARGS((int ch)); +static void save_cbuf ARGS((void)); +static void restore_cbuf ARGS((void)); +static void edit_reset ARGS((char *buf, size_t len)); +static int putbuf ARGS((const char *buf, int len, int repl)); +static void del_range ARGS((int a, int b)); +static int findch ARGS((int ch, int cnt, int forw, int incl)); +static int forwword ARGS((int argcnt)); +static int backword ARGS((int argcnt)); +static int endword ARGS((int argcnt)); +static int Forwword ARGS((int argcnt)); +static int Backword ARGS((int argcnt)); +static int Endword ARGS((int argcnt)); +static int grabhist ARGS((int save, int n)); +static int grabsearch ARGS((int save, int start, int fwd, char *pat)); +static void redraw_line ARGS((int newline)); +static void refresh ARGS((int leftside)); +static int outofwin ARGS((void)); +static void rewindow ARGS((void)); +static int newcol ARGS((int ch, int col)); +static void display ARGS((char *wb1, char *wb2, int leftside)); +static void ed_mov_opt ARGS((int col, char *wb)); +static int do_comment ARGS((void)); +static int expand_word ARGS((int command)); +static int complete_word ARGS((int command, int count)); +static int print_expansions ARGS((struct edstate *e, int command)); +static int char_len ARGS((int c)); +static void x_vi_zotc ARGS((int c)); +static void vi_pprompt ARGS((int full)); +static void vi_error ARGS((void)); +static void vi_macro_reset ARGS((void)); + +#define C_ 0x1 /* a valid command that isn't a M_, E_, U_ */ +#define M_ 0x2 /* movement command (h, l, etc.) */ +#define E_ 0x4 /* extended command (c, d, y) */ +#define X_ 0x8 /* long command (@, f, F, t, T, etc.) */ +#define U_ 0x10 /* an UN-undoable command (that isn't a M_) */ +#define B_ 0x20 /* bad command (^@) */ +#define Z_ 0x40 /* repeat count defaults to 0 (not 1) */ +#define S_ 0x80 /* search (/, ?) */ + +#define is_bad(c) (classify[(c)&0x7f]&B_) +#define is_cmd(c) (classify[(c)&0x7f]&(M_|E_|C_|U_)) +#define is_move(c) (classify[(c)&0x7f]&M_) +#define is_extend(c) (classify[(c)&0x7f]&E_) +#define is_long(c) (classify[(c)&0x7f]&X_) +#define is_undoable(c) (!(classify[(c)&0x7f]&U_)) +#define is_srch(c) (classify[(c)&0x7f]&S_) +#define is_zerocount(c) (classify[(c)&0x7f]&Z_) + +const unsigned char classify[128] = { + /* 0 1 2 3 4 5 6 7 */ + /* 0 ^@ ^A ^B ^C ^D ^E ^F ^G */ + B_, 0, 0, 0, 0, C_|U_, C_|Z_, 0, + /* 01 ^H ^I ^J ^K ^L ^M ^N ^O */ + M_, C_|Z_, 0, 0, C_|U_, 0, C_, 0, + /* 02 ^P ^Q ^R ^S ^T ^U ^V ^W */ + C_, 0, C_|U_, 0, 0, 0, C_, 0, + /* 03 ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */ + C_, 0, 0, 0, 0, 0, 0, 0, + /* 04 <space> ! " # $ % & ' */ + M_, 0, 0, C_, M_, M_, 0, 0, + /* 05 ( ) * + , - . / */ + 0, 0, C_, C_, M_, C_, 0, C_|S_, + /* 06 0 1 2 3 4 5 6 7 */ + M_, 0, 0, 0, 0, 0, 0, 0, + /* 07 8 9 : ; < = > ? */ + 0, 0, 0, M_, 0, C_, 0, C_|S_, + /* 010 @ A B C D E F G */ + C_|X_, C_, M_, C_, C_, M_, M_|X_, C_|U_|Z_, + /* 011 H I J K L M N O */ + 0, C_, 0, 0, 0, 0, C_|U_, 0, + /* 012 P Q R S T U V W */ + C_, 0, C_, C_, M_|X_, C_, 0, M_, + /* 013 X Y Z [ \ ] ^ _ */ + C_, C_|U_, 0, 0, C_|Z_, 0, M_, C_|Z_, + /* 014 ` a b c d e f g */ + 0, C_, M_, E_, E_, M_, M_|X_, C_|Z_, + /* 015 h i j k l m n o */ + M_, C_, C_|U_, C_|U_, M_, 0, C_|U_, 0, + /* 016 p q r s t u v w */ + C_, 0, X_, C_, M_|X_, C_|U_, C_|U_|Z_,M_, + /* 017 x y z { | } ~ ^? */ + C_, E_|U_, 0, 0, M_|Z_, 0, C_, 0 +}; + +#define MAXVICMD 3 +#define SRCHLEN 40 + +#define INSERT 1 +#define REPLACE 2 + +#define VNORMAL 0 /* command, insert or replace mode */ +#define VARG1 1 /* digit prefix (first, eg, 5l) */ +#define VEXTCMD 2 /* cmd + movement (eg, cl) */ +#define VARG2 3 /* digit prefix (second, eg, 2c3l) */ +#define VXCH 4 /* f, F, t, T, @ */ +#define VFAIL 5 /* bad command */ +#define VCMD 6 /* single char command (eg, X) */ +#define VREDO 7 /* . */ +#define VLIT 8 /* ^V */ +#define VSEARCH 9 /* /, ? */ +#define VVERSION 10 /* <ESC> ^V */ + +static char undocbuf[CMDLEN]; + +static struct edstate *save_edstate ARGS((struct edstate *old)); +static void restore_edstate ARGS((struct edstate *old, struct edstate *new)); +static void free_edstate ARGS((struct edstate *old)); + +static struct edstate ebuf; +static struct edstate undobuf = { 0, undocbuf, CMDLEN, 0, 0 }; + +static struct edstate *es; /* current editor state */ +static struct edstate *undo; + +static char ibuf[CMDLEN]; /* input buffer */ +static int first_insert; /* set when starting in insert mode */ +static int saved_inslen; /* saved inslen for first insert */ +static int inslen; /* length of input buffer */ +static int srchlen; /* length of current search pattern */ +static char ybuf[CMDLEN]; /* yank buffer */ +static int yanklen; /* length of yank buffer */ +static int fsavecmd = ' '; /* last find command */ +static int fsavech; /* character to find */ +static char lastcmd[MAXVICMD]; /* last non-move command */ +static int lastac; /* argcnt for lastcmd */ +static int lastsearch = ' '; /* last search command */ +static char srchpat[SRCHLEN]; /* last search pattern */ +static int insert; /* non-zero in insert mode */ +static int hnum; /* position in history */ +static int ohnum; /* history line copied (after mod) */ +static int hlast; /* 1 past last position in history */ +static int modified; /* buffer has been "modified" */ +static int state; + +/* Information for keeping track of macros that are being expanded. + * The format of buf is the alias contents followed by a null byte followed + * by the name (letter) of the alias. The end of the buffer is marked by + * a double null. The name of the alias is stored so recursive macros can + * be detected. + */ +struct macro_state { + unsigned char *p; /* current position in buf */ + unsigned char *buf; /* pointer to macro(s) being expanded */ + int len; /* how much data in buffer */ +}; +static struct macro_state macro; + +enum expand_mode { NONE, EXPAND, COMPLETE, PRINT }; +static enum expand_mode expanded = NONE;/* last input was expanded */ + +int +x_vi(buf, len) + char *buf; + size_t len; +{ + int c; + + vi_reset(buf, len > CMDLEN ? CMDLEN : len); + vi_pprompt(1); + x_flush(); + while (1) { + if (macro.p) { + c = *macro.p++; + /* end of current macro? */ + if (!c) { + /* more macros left to finish? */ + if (*macro.p++) + continue; + /* must be the end of all the macros */ + vi_macro_reset(); + c = x_getc(); + } + } else + c = x_getc(); + if (c == -1) + break; + if (state != VLIT) { + if (c == edchars.intr || c == edchars.quit) { + /* pretend we got an interrupt */ + x_vi_zotc(c); + x_flush(); + trapsig(c == edchars.intr ? SIGINT : SIGQUIT); + x_mode(FALSE); + unwind(LSHELL); + } else if (c == edchars.eof && state != VVERSION) { + if (es->linelen == 0) { + x_vi_zotc(edchars.eof); + c = -1; + break; + } + continue; + } + } + if (vi_hook(c)) + break; + x_flush(); + } + + x_putc('\r'); x_putc('\n'); x_flush(); + + if (c == -1) + return -1; + + if (es->cbuf != buf) + memmove(buf, es->cbuf, es->linelen); + + buf[es->linelen++] = '\n'; + + return es->linelen; +} + +static int +vi_hook(ch) + int ch; +{ + static char curcmd[MAXVICMD]; + static char locpat[SRCHLEN]; + static int cmdlen; + static int argc1, argc2; + + switch (state) { + + case VNORMAL: + if (insert != 0) { + if (ch == Ctrl('v')) { + state = VLIT; + ch = '^'; + } + switch (vi_insert(ch)) { + case -1: +#ifdef OS2 + /* Arrow keys generate 0xe0X, where X is H.. */ + state = VCMD; + argc1 = 1; + switch (x_getc()) { + case 'H': + *curcmd='k'; + break; + case 'K': + *curcmd='h'; + break; + case 'P': + *curcmd='j'; + break; + case 'M': + *curcmd='l'; + break; + default: + vi_error(); + state = VNORMAL; + } + break; +#else /* OS2 */ + vi_error(); + state = VNORMAL; +#endif /* OS2 */ + break; + case 0: + if (state == VLIT) { + es->cursor--; + refresh(0); + } else + refresh(insert != 0); + break; + case 1: + return 1; + } + } else { + if (ch == '\r' || ch == '\n') + return 1; + cmdlen = 0; + argc1 = 0; + if (ch >= '1' && ch <= '9') { + argc1 = ch - '0'; + state = VARG1; + } else { + curcmd[cmdlen++] = ch; + state = nextstate(ch); + if (state == VSEARCH) { + save_cbuf(); + es->cursor = 0; + es->linelen = 0; + if (ch == '/') { + if (putbuf("/", 1, 0) != 0) { + return -1; + } + } else if (putbuf("?", 1, 0) != 0) + return -1; + refresh(0); + } + if (state == VVERSION) { + save_cbuf(); + es->cursor = 0; + es->linelen = 0; + putbuf(ksh_version + 4, + strlen(ksh_version + 4), 0); + refresh(0); + } + } + } + break; + + case VLIT: + if (is_bad(ch)) { + del_range(es->cursor, es->cursor + 1); + vi_error(); + } else + es->cbuf[es->cursor++] = ch; + refresh(1); + state = VNORMAL; + break; + + case VVERSION: + restore_cbuf(); + state = VNORMAL; + refresh(0); + break; + + case VARG1: + if (isdigit(ch)) + argc1 = argc1 * 10 + ch - '0'; + else { + curcmd[cmdlen++] = ch; + state = nextstate(ch); + } + break; + + case VEXTCMD: + argc2 = 0; + if (ch >= '1' && ch <= '9') { + argc2 = ch - '0'; + state = VARG2; + return 0; + } else { + curcmd[cmdlen++] = ch; + if (ch == curcmd[0]) + state = VCMD; + else if (is_move(ch)) + state = nextstate(ch); + else + state = VFAIL; + } + break; + + case VARG2: + if (isdigit(ch)) + argc2 = argc2 * 10 + ch - '0'; + else { + if (argc1 == 0) + argc1 = argc2; + else + argc1 *= argc2; + curcmd[cmdlen++] = ch; + if (ch == curcmd[0]) + state = VCMD; + else if (is_move(ch)) + state = nextstate(ch); + else + state = VFAIL; + } + break; + + case VXCH: + if (ch == Ctrl('[')) + state = VNORMAL; + else { + curcmd[cmdlen++] = ch; + state = VCMD; + } + break; + + case VSEARCH: + if (ch == '\r' || ch == '\n' /*|| ch == Ctrl('[')*/ ) { + restore_cbuf(); + /* Repeat last search? */ + if (srchlen == 0) { + if (!srchpat[0]) { + vi_error(); + state = VNORMAL; + refresh(0); + return 0; + } + } else { + locpat[srchlen] = '\0'; + (void) strcpy(srchpat, locpat); + } + state = VCMD; + } else if (ch == edchars.erase || ch == Ctrl('h')) { + if (srchlen != 0) { + srchlen--; + es->linelen -= char_len((unsigned char) locpat[srchlen]); + es->cursor = es->linelen; + refresh(0); + return 0; + } + restore_cbuf(); + state = VNORMAL; + refresh(0); + } else if (ch == edchars.kill) { + srchlen = 0; + es->linelen = 1; + es->cursor = 1; + refresh(0); + return 0; + } else if (ch == edchars.werase) { + int i; + int n = srchlen; + + while (n > 0 && isspace(locpat[n - 1])) + n--; + while (n > 0 && !isspace(locpat[n - 1])) + n--; + for (i = srchlen; --i >= n; ) + es->linelen -= char_len((unsigned char) locpat[i]); + srchlen = n; + es->cursor = es->linelen; + refresh(0); + return 0; + } else { + if (srchlen == SRCHLEN - 1) + vi_error(); + else { + locpat[srchlen++] = ch; + if ((ch & 0x80) && Flag(FVISHOW8)) { + es->cbuf[es->linelen++] = 'M'; + es->cbuf[es->linelen++] = '-'; + ch &= 0x7f; + } + if (ch < ' ' || ch == 0x7f) { + es->cbuf[es->linelen++] = '^'; + es->cbuf[es->linelen++] = ch ^ '@'; + } else + es->cbuf[es->linelen++] = ch; + es->cursor = es->linelen; + refresh(0); + } + return 0; + } + break; + } + + switch (state) { + case VCMD: + state = VNORMAL; + switch (vi_cmd(argc1, curcmd)) { + case -1: + vi_error(); + refresh(0); + break; + case 0: + if (insert != 0) + inslen = 0; + refresh(insert != 0); + break; + case 1: + refresh(0); + return 1; + case 2: + /* back from a 'v' command - don't redraw the screen */ + return 1; + } + break; + + case VREDO: + state = VNORMAL; + if (argc1 != 0) + lastac = argc1; + switch (vi_cmd(lastac, lastcmd) != 0) { + case -1: + vi_error(); + refresh(0); + break; + case 0: + if (insert != 0) { + if (lastcmd[0] == 's' || lastcmd[0] == 'c' || + lastcmd[0] == 'C') { + if (redo_insert(1) != 0) + vi_error(); + } else { + if (redo_insert(lastac) != 0) + vi_error(); + } + } + refresh(0); + break; + case 1: + refresh(0); + return 1; + case 2: + /* back from a 'v' command - don't redraw the screen */ + return 1; + } + break; + + case VFAIL: + state = VNORMAL; + vi_error(); + break; + } + return 0; +} + +static void +vi_reset(buf, len) + char *buf; + size_t len; +{ + state = VNORMAL; + ohnum = hnum = hlast = histnum(-1) + 1; + insert = INSERT; + saved_inslen = inslen; + first_insert = 1; + inslen = 0; + modified = 1; + vi_macro_reset(); + edit_reset(buf, len); +} + +static int +nextstate(ch) + int ch; +{ + if (is_extend(ch)) + return VEXTCMD; + else if (is_srch(ch)) + return VSEARCH; + else if (is_long(ch)) + return VXCH; + else if (ch == '.') + return VREDO; + else if (ch == Ctrl('v')) + return VVERSION; + else if (is_cmd(ch)) + return VCMD; + else + return VFAIL; +} + +static int +vi_insert(ch) + int ch; +{ + int tcursor; + + if (ch == edchars.erase || ch == Ctrl('h')) { + if (insert == REPLACE) { + if (es->cursor == undo->cursor) { + vi_error(); + return 0; + } + if (inslen > 0) + inslen--; + es->cursor--; + if (es->cursor >= undo->linelen) + es->linelen--; + else + es->cbuf[es->cursor] = undo->cbuf[es->cursor]; + } else { + if (es->cursor == 0) { + /* x_putc(BEL); no annoying bell here */ + return 0; + } + if (inslen > 0) + inslen--; + es->cursor--; + es->linelen--; + memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor+1], + es->linelen - es->cursor + 1); + } + expanded = NONE; + return 0; + } + if (ch == edchars.kill) { + if (es->cursor != 0) { + inslen = 0; + memmove(es->cbuf, &es->cbuf[es->cursor], + es->linelen - es->cursor); + es->linelen -= es->cursor; + es->cursor = 0; + } + expanded = NONE; + return 0; + } + if (ch == edchars.werase) { + if (es->cursor != 0) { + tcursor = Backword(1); + memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor], + es->linelen - es->cursor); + es->linelen -= es->cursor - tcursor; + if (inslen < es->cursor - tcursor) + inslen = 0; + else + inslen -= es->cursor - tcursor; + es->cursor = tcursor; + } + expanded = NONE; + return 0; + } + /* If any chars are entered before escape, trash the saved insert + * buffer (if user inserts & deletes char, ibuf gets trashed and + * we don't want to use it) + */ + if (first_insert && ch != Ctrl('[')) + saved_inslen = 0; + switch (ch) { + +#ifdef OS2 + case 224: /* function key prefix */ +#endif /* OS2 */ + case '\0': + return -1; + + case '\r': + case '\n': + return 1; + + case Ctrl('['): + expanded = NONE; + if (first_insert) { + first_insert = 0; + if (inslen == 0) { + inslen = saved_inslen; + return redo_insert(0); + } + lastcmd[0] = 'a'; + lastac = 1; + } + if (lastcmd[0] == 's' || lastcmd[0] == 'c' || + lastcmd[0] == 'C') + return redo_insert(0); + else + return redo_insert(lastac - 1); + + /* { Begin nonstandard vi commands */ + case Ctrl('x'): + expand_word(0); + break; + + case Ctrl('f'): + complete_word(0, 0); + break; + + case Ctrl('e'): + print_expansions(es, 0); + break; + + case Ctrl('i'): + if (Flag(FVITABCOMPLETE)) { + complete_word(0, 0); + break; + } + /* FALLTHROUGH */ + /* End nonstandard vi commands } */ + + default: + if (es->linelen == es->cbufsize - 1) + return -1; + ibuf[inslen++] = ch; + if (insert == INSERT) { + memmove(&es->cbuf[es->cursor+1], &es->cbuf[es->cursor], + es->linelen - es->cursor); + es->linelen++; + } + es->cbuf[es->cursor++] = ch; + if (insert == REPLACE && es->cursor > es->linelen) + es->linelen++; + expanded = NONE; + } + return 0; +} + +static int +vi_cmd(argcnt, cmd) + int argcnt; + const char *cmd; +{ + int ncursor; + int cur, c1, c2, c3 = 0; + int any; + struct edstate *t; + + if (argcnt == 0 && !is_zerocount(*cmd)) + argcnt = 1; + + if (is_move(*cmd)) { + if ((cur = domove(argcnt, cmd, 0)) >= 0) { + if (cur == es->linelen && cur != 0) + cur--; + es->cursor = cur; + } else + return -1; + } else { + /* Don't save state in middle of macro.. */ + if (is_undoable(*cmd) && !macro.p) { + undo->winleft = es->winleft; + memmove(undo->cbuf, es->cbuf, es->linelen); + undo->linelen = es->linelen; + undo->cursor = es->cursor; + lastac = argcnt; + memmove(lastcmd, cmd, MAXVICMD); + } + switch (*cmd) { + + case Ctrl('l'): + case Ctrl('r'): + redraw_line(1); + break; + + case '@': + { + static char alias[] = "_\0"; + struct tbl *ap; + int olen, nlen; + char *p, *nbuf; + + /* lookup letter in alias list... */ + alias[1] = cmd[1]; + ap = tsearch(&aliases, alias, hash(alias)); + if (!cmd[1] || !ap || !(ap->flag & ISSET)) + return -1; + /* check if this is a recursive call... */ + if ((p = (char *) macro.p)) + while ((p = strchr(p, '\0')) && p[1]) + if (*++p == cmd[1]) + return -1; + /* insert alias into macro buffer */ + nlen = strlen(ap->val.s) + 1; + olen = !macro.p ? 2 + : macro.len - (macro.p - macro.buf); + nbuf = alloc(nlen + 1 + olen, APERM); + memcpy(nbuf, ap->val.s, nlen); + nbuf[nlen++] = cmd[1]; + if (macro.p) { + memcpy(nbuf + nlen, macro.p, olen); + afree(macro.buf, APERM); + nlen += olen; + } else { + nbuf[nlen++] = '\0'; + nbuf[nlen++] = '\0'; + } + macro.p = macro.buf = (unsigned char *) nbuf; + macro.len = nlen; + } + break; + + case 'a': + modified = 1; hnum = hlast; + if (es->linelen != 0) + es->cursor++; + insert = INSERT; + break; + + case 'A': + modified = 1; hnum = hlast; + del_range(0, 0); + es->cursor = es->linelen; + insert = INSERT; + break; + + case 'S': + es->cursor = domove(1, "^", 1); + del_range(es->cursor, es->linelen); + modified = 1; hnum = hlast; + insert = INSERT; + break; + + case 'Y': + cmd = "y$"; + /* ahhhhhh... */ + case 'c': + case 'd': + case 'y': + if (*cmd == cmd[1]) { + c1 = *cmd == 'c' ? domove(1, "^", 1) : 0; + c2 = es->linelen; + } else if (!is_move(cmd[1])) + return -1; + else { + if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0) + return -1; + if (*cmd == 'c' && + (cmd[1]=='w' || cmd[1]=='W') && + !isspace(es->cbuf[es->cursor])) { + while (isspace(es->cbuf[--ncursor])) + ; + ncursor++; + } + if (ncursor > es->cursor) { + c1 = es->cursor; + c2 = ncursor; + } else { + c1 = ncursor; + c2 = es->cursor; + if (cmd[1] == '%') + c2++; + } + } + if (*cmd != 'c' && c1 != c2) + yank_range(c1, c2); + if (*cmd != 'y') { + del_range(c1, c2); + es->cursor = c1; + } + if (*cmd == 'c') { + modified = 1; hnum = hlast; + insert = INSERT; + } + break; + + case 'p': + modified = 1; hnum = hlast; + if (es->linelen != 0) + es->cursor++; + while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0) + ; + if (es->cursor != 0) + es->cursor--; + if (argcnt != 0) + return -1; + break; + + case 'P': + modified = 1; hnum = hlast; + any = 0; + while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0) + any = 1; + if (any && es->cursor != 0) + es->cursor--; + if (argcnt != 0) + return -1; + break; + + case 'C': + modified = 1; hnum = hlast; + del_range(es->cursor, es->linelen); + insert = INSERT; + break; + + case 'D': + yank_range(es->cursor, es->linelen); + del_range(es->cursor, es->linelen); + if (es->cursor != 0) + es->cursor--; + break; + + case 'g': + if (!argcnt) + argcnt = hlast + 1; + /* fall through */ + case 'G': + if (!argcnt) + argcnt = 1; + else + argcnt = hlast - (source->line - argcnt); + if (grabhist(modified, argcnt - 1) < 0) + return -1; + else { + modified = 0; + hnum = argcnt - 1; + } + break; + + case 'i': + modified = 1; hnum = hlast; + insert = INSERT; + break; + + case 'I': + modified = 1; hnum = hlast; + es->cursor = domove(1, "^", 1); + insert = INSERT; + break; + + case 'j': + case '+': + case Ctrl('n'): + if (grabhist(modified, hnum + argcnt) < 0) + return -1; + else { + modified = 0; + hnum += argcnt; + } + break; + + case 'k': + case '-': + case Ctrl('p'): + if (grabhist(modified, hnum - argcnt) < 0) + return -1; + else { + modified = 0; + hnum -= argcnt; + } + break; + + case 'r': + if (es->linelen == 0) + return -1; + modified = 1; hnum = hlast; + if (cmd[1] == 0) + vi_error(); + else + es->cbuf[es->cursor] = cmd[1]; + break; + + case 'R': + modified = 1; hnum = hlast; + insert = REPLACE; + break; + + case 's': + if (es->linelen == 0) + return -1; + modified = 1; hnum = hlast; + if (es->cursor + argcnt > es->linelen) + argcnt = es->linelen - es->cursor; + del_range(es->cursor, es->cursor + argcnt); + insert = INSERT; + break; + + case 'v': + if (es->linelen == 0) + return -1; + if (!argcnt) { + if (modified) { + es->cbuf[es->linelen] = '\0'; + source->line++; + histsave(source->line, es->cbuf, 1); + } else + argcnt = source->line + 1 + - (hlast - hnum); + } + shf_snprintf(es->cbuf, es->cbufsize, + argcnt ? "%s %d" : "%s", + "fc -e ${VISUAL:-${EDITOR:-vi}} --", + argcnt); + es->linelen = strlen(es->cbuf); + return 2; + + case 'x': + if (es->linelen == 0) + return -1; + modified = 1; hnum = hlast; + if (es->cursor + argcnt > es->linelen) + argcnt = es->linelen - es->cursor; + yank_range(es->cursor, es->cursor + argcnt); + del_range(es->cursor, es->cursor + argcnt); + break; + + case 'X': + if (es->cursor > 0) { + modified = 1; hnum = hlast; + if (es->cursor < argcnt) + argcnt = es->cursor; + yank_range(es->cursor - argcnt, es->cursor); + del_range(es->cursor - argcnt, es->cursor); + es->cursor -= argcnt; + } else + return -1; + break; + + case 'u': + t = es; + es = undo; + undo = t; + break; + + case 'U': + if (!modified) + return -1; + if (grabhist(modified, ohnum) < 0) + return -1; + modified = 0; + hnum = ohnum; + break; + + case '?': + if (hnum == hlast) + hnum = -1; + /* ahhh */ + case '/': + c3 = 1; + srchlen = 0; + lastsearch = *cmd; + /* fall through */ + case 'n': + case 'N': + if (lastsearch == ' ') + return -1; + if (lastsearch == '?') + c1 = 1; + else + c1 = 0; + if (*cmd == 'N') + c1 = !c1; + if ((c2 = grabsearch(modified, hnum, + c1, srchpat)) < 0) { + if (c3) { + restore_cbuf(); + refresh(0); + } + return -1; + } else { + modified = 0; + hnum = c2; + ohnum = hnum; + } + break; + case '_': { + int inspace; + char *p, *sp; + + if (histnum(-1) < 0) + return -1; + p = *histpos(); +#define issp(c) (isspace((c)) || (c) == '\n') + if (argcnt) { + while (*p && issp(*p)) + p++; + while (*p && --argcnt) { + while (*p && !issp(*p)) + p++; + while (*p && issp(*p)) + p++; + } + if (!*p) + return -1; + sp = p; + } else { + sp = p; + inspace = 0; + while (*p) { + if (issp(*p)) + inspace = 1; + else if (inspace) { + inspace = 0; + sp = p; + } + p++; + } + p = sp; + } + modified = 1; hnum = hlast; + if (es->cursor != es->linelen) + es->cursor++; + while (*p && !issp(*p)) { + argcnt++; + p++; + } + if (putbuf(space, 1, 0) != 0) + argcnt = -1; + else if (putbuf(sp, argcnt, 0) != 0) + argcnt = -1; + if (argcnt < 0) { + if (es->cursor != 0) + es->cursor--; + return -1; + } + insert = INSERT; + } + break; + + case '~': { + char *p; + int i; + + if (es->linelen == 0) + return -1; + for (i = 0; i < argcnt; i++) { + p = &es->cbuf[es->cursor]; + if (islower(*p)) { + modified = 1; hnum = hlast; + *p = toupper(*p); + } else if (isupper(*p)) { + modified = 1; hnum = hlast; + *p = tolower(*p); + } + if (es->cursor < es->linelen - 1) + es->cursor++; + } + break; + } + + case '#': + return do_comment(); + + case '=': /* at&t ksh */ + case Ctrl('e'): /* Nonstandard vi/ksh */ + print_expansions(es, 1); + break; + + + case Ctrl('i'): /* Nonstandard vi/ksh */ + if (!Flag(FVITABCOMPLETE)) + return -1; + /* FALLTHROUGH */ + + case Ctrl('['): /* some annoying at&t ksh's */ + case '\\': /* at&t ksh */ + case Ctrl('f'): /* Nonstandard vi/ksh */ + complete_word(1, argcnt); + break; + + + case '*': /* at&t ksh */ + case Ctrl('x'): /* Nonstandard vi/ksh */ + expand_word(1); + break; + } + if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen) + es->cursor--; + } + return 0; +} + +static int +domove(argcnt, cmd, sub) + int argcnt; + const char *cmd; + int sub; +{ + int bcount, UNINITIALIZED(i), t; + int UNINITIALIZED(ncursor); + + switch (*cmd) { + + case 'b': + if (!sub && es->cursor == 0) + return -1; + ncursor = backword(argcnt); + break; + + case 'B': + if (!sub && es->cursor == 0) + return -1; + ncursor = Backword(argcnt); + break; + + case 'e': + if (!sub && es->cursor + 1 >= es->linelen) + return -1; + ncursor = endword(argcnt); + if (sub && ncursor < es->linelen) + ncursor++; + break; + + case 'E': + if (!sub && es->cursor + 1 >= es->linelen) + return -1; + ncursor = Endword(argcnt); + if (sub && ncursor < es->linelen) + ncursor++; + break; + + case 'f': + case 'F': + case 't': + case 'T': + fsavecmd = *cmd; + fsavech = cmd[1]; + /* drop through */ + + case ',': + case ';': + if (fsavecmd == ' ') + return -1; + i = fsavecmd == 'f' || fsavecmd == 'F'; + t = fsavecmd > 'a'; + if (*cmd == ',') + t = !t; + if ((ncursor = findch(fsavech, argcnt, t, i)) < 0) + return -1; + if (sub && t) + ncursor++; + break; + + case 'h': + case Ctrl('h'): + if (!sub && es->cursor == 0) + return -1; + ncursor = es->cursor - argcnt; + if (ncursor < 0) + ncursor = 0; + break; + + case ' ': + case 'l': + if (!sub && es->cursor + 1 >= es->linelen) + return -1; + if (es->linelen != 0) { + ncursor = es->cursor + argcnt; + if (ncursor > es->linelen) + ncursor = es->linelen; + } + break; + + case 'w': + if (!sub && es->cursor + 1 >= es->linelen) + return -1; + ncursor = forwword(argcnt); + break; + + case 'W': + if (!sub && es->cursor + 1 >= es->linelen) + return -1; + ncursor = Forwword(argcnt); + break; + + case '0': + ncursor = 0; + break; + + case '^': + ncursor = 0; + while (ncursor < es->linelen - 1 && isspace(es->cbuf[ncursor])) + ncursor++; + break; + + case '|': + ncursor = argcnt; + if (ncursor > es->linelen) + ncursor = es->linelen; + if (ncursor) + ncursor--; + break; + + case '$': + if (es->linelen != 0) + ncursor = es->linelen; + else + ncursor = 0; + break; + + case '%': + ncursor = es->cursor; + while (ncursor < es->linelen && + (i = bracktype(es->cbuf[ncursor])) == 0) + ncursor++; + if (ncursor == es->linelen) + return -1; + bcount = 1; + do { + if (i > 0) { + if (++ncursor >= es->linelen) + return -1; + } else { + if (--ncursor < 0) + return -1; + } + t = bracktype(es->cbuf[ncursor]); + if (t == i) + bcount++; + else if (t == -i) + bcount--; + } while (bcount != 0); + if (sub && i > 0) + ncursor++; + break; + + default: + return -1; + } + return ncursor; +} + +static int +redo_insert(count) + int count; +{ + while (count-- > 0) + if (putbuf(ibuf, inslen, insert==REPLACE) != 0) + return -1; + if (es->cursor > 0) + es->cursor--; + insert = 0; + return 0; +} + +static void +yank_range(a, b) + int a, b; +{ + yanklen = b - a; + if (yanklen != 0) + memmove(ybuf, &es->cbuf[a], yanklen); +} + +static int +bracktype(ch) + int ch; +{ + switch (ch) { + + case '(': + return 1; + + case '[': + return 2; + + case '{': + return 3; + + case ')': + return -1; + + case ']': + return -2; + + case '}': + return -3; + + default: + return 0; + } +} + +/* + * Non user interface editor routines below here + */ + +static int cur_col; /* current column on line */ +static int pwidth; /* width of prompt */ +static int prompt_trunc; /* how much of prompt to truncate */ +static int prompt_skip; /* how much of prompt to skip */ +static int winwidth; /* width of window */ +static char *wbuf[2]; /* window buffers */ +static int wbuf_len; /* length of window buffers (x_cols-3)*/ +static int win; /* window buffer in use */ +static char morec; /* more character at right of window */ +static int lastref; /* argument to last refresh() */ +static char holdbuf[CMDLEN]; /* place to hold last edit buffer */ +static int holdlen; /* length of holdbuf */ + +static void +save_cbuf() +{ + memmove(holdbuf, es->cbuf, es->linelen); + holdlen = es->linelen; + holdbuf[holdlen] = '\0'; +} + +static void +restore_cbuf() +{ + es->cursor = 0; + es->linelen = holdlen; + memmove(es->cbuf, holdbuf, holdlen); +} + +/* return a new edstate */ +static struct edstate * +save_edstate(old) + struct edstate *old; +{ + struct edstate *new; + + new = (struct edstate *)alloc(sizeof(struct edstate), APERM); + new->cbuf = alloc(old->cbufsize, APERM); + new->cbufsize = old->cbufsize; + strcpy(new->cbuf, old->cbuf); + new->linelen = old->linelen; + new->cursor = old->cursor; + new->winleft = old->winleft; + return new; +} + +static void +restore_edstate(new, old) + struct edstate *old, *new; +{ + strncpy(new->cbuf, old->cbuf, old->linelen); + new->linelen = old->linelen; + new->cursor = old->cursor; + new->winleft = old->winleft; + free_edstate(old); +} + +static void +free_edstate(old) + struct edstate *old; +{ + afree(old->cbuf, APERM); + afree((char *)old, APERM); +} + + + +static void +edit_reset(buf, len) + char *buf; + size_t len; +{ + const char *p; + + es = &ebuf; + es->cbuf = buf; + es->cbufsize = len; + undo = &undobuf; + undo->cbufsize = len; + + es->linelen = undo->linelen = 0; + es->cursor = undo->cursor = 0; + es->winleft = undo->winleft = 0; + + cur_col = pwidth = promptlen(prompt, &p); + prompt_skip = p - prompt; + if (pwidth > x_cols - 3 - MIN_EDIT_SPACE) { + cur_col = x_cols - 3 - MIN_EDIT_SPACE; + prompt_trunc = pwidth - cur_col; + pwidth -= prompt_trunc; + } else + prompt_trunc = 0; + if (!wbuf_len || wbuf_len != x_cols - 3) { + wbuf_len = x_cols - 3; + wbuf[0] = aresize(wbuf[0], wbuf_len, APERM); + wbuf[1] = aresize(wbuf[1], wbuf_len, APERM); + } + (void) memset(wbuf[0], ' ', wbuf_len); + (void) memset(wbuf[1], ' ', wbuf_len); + winwidth = x_cols - pwidth - 3; + win = 0; + morec = ' '; + lastref = 1; + holdlen = 0; +} + +static int +putbuf(buf, len, repl) + const char *buf; + int len; + int repl; +{ + if (len == 0) + return 0; + if (repl) { + if (es->cursor + len >= es->cbufsize) + return -1; + if (es->cursor + len > es->linelen) + es->linelen = es->cursor + len; + } else { + if (es->linelen + len >= es->cbufsize) + return -1; + memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor], + es->linelen - es->cursor); + es->linelen += len; + } + memmove(&es->cbuf[es->cursor], buf, len); + es->cursor += len; + return 0; +} + +static void +del_range(a, b) + int a, b; +{ + if (es->linelen != b) + memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b); + es->linelen -= b - a; +} + +static int +findch(ch, cnt, forw, incl) + int ch; + int cnt; + int forw; + int incl; +{ + int ncursor; + + if (es->linelen == 0) + return -1; + ncursor = es->cursor; + while (cnt--) { + do { + if (forw) { + if (++ncursor == es->linelen) + return -1; + } else { + if (--ncursor < 0) + return -1; + } + } while (es->cbuf[ncursor] != ch); + } + if (!incl) { + if (forw) + ncursor--; + else + ncursor++; + } + return ncursor; +} + +static int +forwword(argcnt) + int argcnt; +{ + int ncursor; + + ncursor = es->cursor; + while (ncursor < es->linelen && argcnt--) { + if (is_wordch(es->cbuf[ncursor])) + while (is_wordch(es->cbuf[ncursor]) && + ncursor < es->linelen) + ncursor++; + else if (!isspace(es->cbuf[ncursor])) + while (!is_wordch(es->cbuf[ncursor]) && + !isspace(es->cbuf[ncursor]) && + ncursor < es->linelen) + ncursor++; + while (isspace(es->cbuf[ncursor]) && ncursor < es->linelen) + ncursor++; + } + return ncursor; +} + +static int +backword(argcnt) + int argcnt; +{ + int ncursor; + + ncursor = es->cursor; + while (ncursor > 0 && argcnt--) { + while (--ncursor > 0 && isspace(es->cbuf[ncursor])) + ; + if (ncursor > 0) { + if (is_wordch(es->cbuf[ncursor])) + while (--ncursor >= 0 && + is_wordch(es->cbuf[ncursor])) + ; + else + while (--ncursor >= 0 && + !is_wordch(es->cbuf[ncursor]) && + !isspace(es->cbuf[ncursor])) + ; + ncursor++; + } + } + return ncursor; +} + +static int +endword(argcnt) + int argcnt; +{ + int ncursor; + + ncursor = es->cursor; + while (ncursor < es->linelen && argcnt--) { + while (++ncursor < es->linelen - 1 && + isspace(es->cbuf[ncursor])) + ; + if (ncursor < es->linelen - 1) { + if (is_wordch(es->cbuf[ncursor])) + while (++ncursor < es->linelen && + is_wordch(es->cbuf[ncursor])) + ; + else + while (++ncursor < es->linelen && + !is_wordch(es->cbuf[ncursor]) && + !isspace(es->cbuf[ncursor])) + ; + ncursor--; + } + } + return ncursor; +} + +static int +Forwword(argcnt) + int argcnt; +{ + int ncursor; + + ncursor = es->cursor; + while (ncursor < es->linelen && argcnt--) { + while (!isspace(es->cbuf[ncursor]) && ncursor < es->linelen) + ncursor++; + while (isspace(es->cbuf[ncursor]) && ncursor < es->linelen) + ncursor++; + } + return ncursor; +} + +static int +Backword(argcnt) + int argcnt; +{ + int ncursor; + + ncursor = es->cursor; + while (ncursor > 0 && argcnt--) { + while (--ncursor >= 0 && isspace(es->cbuf[ncursor])) + ; + while (ncursor >= 0 && !isspace(es->cbuf[ncursor])) + ncursor--; + ncursor++; + } + return ncursor; +} + +static int +Endword(argcnt) + int argcnt; +{ + int ncursor; + + ncursor = es->cursor; + while (ncursor < es->linelen - 1 && argcnt--) { + while (++ncursor < es->linelen - 1 && + isspace(es->cbuf[ncursor])) + ; + if (ncursor < es->linelen - 1) { + while (++ncursor < es->linelen && + !isspace(es->cbuf[ncursor])) + ; + ncursor--; + } + } + return ncursor; +} + +static int +grabhist(save, n) + int save; + int n; +{ + char *hptr; + + if (n < 0 || n > hlast) + return -1; + if (n == hlast) { + restore_cbuf(); + ohnum = n; + return 0; + } + (void) histnum(n); + if ((hptr = *histpos()) == NULL) { + internal_errorf(0, "grabhist: bad history array"); + return -1; + } + if (save) + save_cbuf(); + if ((es->linelen = strlen(hptr)) >= es->cbufsize) + es->linelen = es->cbufsize - 1; + memmove(es->cbuf, hptr, es->linelen); + es->cursor = 0; + ohnum = n; + return 0; +} + +static int +grabsearch(save, start, fwd, pat) + int save, start, fwd; + char *pat; +{ + char *hptr; + int hist; + int anchored; + + if ((start == 0 && fwd == 0) || (start >= hlast-1 && fwd == 1)) + return -1; + if (fwd) + start++; + else + start--; + anchored = *pat == '^' ? (++pat, 1) : 0; + if ((hist = findhist(start, fwd, pat, anchored)) < 0) { + /* if (start != 0 && fwd && match(holdbuf, pat) >= 0) { */ + /* XXX should FILECMP be strncmp? */ + if (start != 0 && fwd && FILECMP(holdbuf, pat) >= 0) { + restore_cbuf(); + return 0; + } else + return -1; + } + if (save) + save_cbuf(); + histnum(hist); + hptr = *histpos(); + if ((es->linelen = strlen(hptr)) >= es->cbufsize) + es->linelen = es->cbufsize - 1; + memmove(es->cbuf, hptr, es->linelen); + es->cursor = 0; + return hist; +} + +static void +redraw_line(newline) + int newline; +{ + (void) memset(wbuf[win], ' ', wbuf_len); + if (newline) { + x_putc('\r'); + x_putc('\n'); + } + vi_pprompt(0); + cur_col = pwidth; + morec = ' '; +} + +static void +refresh(leftside) + int leftside; +{ + if (leftside < 0) + leftside = lastref; + else + lastref = leftside; + if (outofwin()) + rewindow(); + display(wbuf[1 - win], wbuf[win], leftside); + win = 1 - win; +} + +static int +outofwin() +{ + int cur, col; + + if (es->cursor < es->winleft) + return 1; + col = 0; + cur = es->winleft; + while (cur < es->cursor) + col = newcol((unsigned char) es->cbuf[cur++], col); + if (col >= winwidth) + return 1; + return 0; +} + +static void +rewindow() +{ + register int tcur, tcol; + int holdcur1, holdcol1; + int holdcur2, holdcol2; + + holdcur1 = holdcur2 = tcur = 0; + holdcol1 = holdcol2 = tcol = 0; + while (tcur < es->cursor) { + if (tcol - holdcol2 > winwidth / 2) { + holdcur1 = holdcur2; + holdcol1 = holdcol2; + holdcur2 = tcur; + holdcol2 = tcol; + } + tcol = newcol((unsigned char) es->cbuf[tcur++], tcol); + } + while (tcol - holdcol1 > winwidth / 2) + holdcol1 = newcol((unsigned char) es->cbuf[holdcur1++], + holdcol1); + es->winleft = holdcur1; +} + +static int +newcol(ch, col) + int ch, col; +{ + if (ch == '\t') + return (col | 7) + 1; + return col + char_len(ch); +} + +static void +display(wb1, wb2, leftside) + char *wb1, *wb2; + int leftside; +{ + unsigned char ch; + char *twb1, *twb2, mc; + int cur, col, cnt; + int UNINITIALIZED(ncol); + int moreright; + + col = 0; + cur = es->winleft; + moreright = 0; + twb1 = wb1; + while (col < winwidth && cur < es->linelen) { + if (cur == es->cursor && leftside) + ncol = col + pwidth; + if ((ch = es->cbuf[cur]) == '\t') { + do { + *twb1++ = ' '; + } while (++col < winwidth && (col & 7) != 0); + } else { + if ((ch & 0x80) && Flag(FVISHOW8)) { + *twb1++ = 'M'; + if (++col < winwidth) { + *twb1++ = '-'; + col++; + } + ch &= 0x7f; + } + if (col < winwidth) { + if (ch < ' ' || ch == 0x7f) { + *twb1++ = '^'; + if (++col < winwidth) { + *twb1++ = es->cbuf[cur] ^ '@'; + col++; + } + } else { + *twb1++ = es->cbuf[cur]; + col++; + } + } + } + if (cur == es->cursor && !leftside) + ncol = col + pwidth - 1; + cur++; + } + if (cur == es->cursor) + ncol = col + pwidth; + if (col < winwidth) { + while (col < winwidth) { + *twb1++ = ' '; + col++; + } + } else + moreright++; + *twb1 = ' '; + + col = pwidth; + cnt = winwidth; + twb1 = wb1; + twb2 = wb2; + while (cnt--) { + if (*twb1 != *twb2) { + if (cur_col != col) + ed_mov_opt(col, wb1); + x_putc(*twb1); + cur_col++; + } + twb1++; + twb2++; + col++; + } + if (es->winleft > 0 && moreright) + /* POSIX says to use * for this but that is a globbing + * character and may confuse people; + is more innocuous + */ + mc = '+'; + else if (es->winleft > 0) + mc = '<'; + else if (moreright) + mc = '>'; + else + mc = ' '; + if (mc != morec) { + ed_mov_opt(x_cols - 2, wb1); + x_putc(mc); + cur_col++; + morec = mc; + } + if (cur_col != ncol) + ed_mov_opt(ncol, wb1); +} + +static void +ed_mov_opt(col, wb) + int col; + char *wb; +{ + if (col < cur_col) { + if (col + 1 < cur_col - col) { + x_putc('\r'); + vi_pprompt(0); + cur_col = pwidth; + while (cur_col++ < col) + x_putc(*wb++); + } else { + while (cur_col-- > col) + x_putc('\b'); + } + } else { + wb = &wb[cur_col - pwidth]; + while (cur_col++ < col) + x_putc(*wb++); + } + cur_col = col; +} + +/* Handle the commenting/uncommenting of a line */ +static int +do_comment() +{ + int i, j; + + if (es->linelen == 0) + return 1; /* somewhat arbitrary - it's what at&t ksh does */ + + /* Already commented? */ + if (es->cbuf[0] == '#') { + int saw_nl = 0; + + for (j = 0, i = 1; i < es->linelen; i++) { + if (!saw_nl || es->cbuf[i] != '#') + es->cbuf[j++] = es->cbuf[i]; + saw_nl = es->cbuf[i] == '\n'; + } + es->linelen = j; + es->cursor = 0; + return 0; + } else { + int n = 1; + + /* See if there's room for the #'s - 1 per \n */ + for (i = 0; i < es->linelen; i++) + if (es->cbuf[i] == '\n') + n++; + if (es->linelen + n >= es->cbufsize) + return -1; + /* Now add them... */ + for (i = es->linelen, j = es->linelen + n; --i >= 0; ) { + if (es->cbuf[i] == '\n') + es->cbuf[--j] = '#'; + es->cbuf[--j] = es->cbuf[i]; + } + es->cbuf[0] = '#'; + es->linelen += n; + es->cursor = 0; + return 1; + } +} + +/* replace word with all expansions (ie, expand word*) */ +static int +expand_word(command) + int command; +{ + static struct edstate *buf; + int rval = 0; + int nwords; + int start, end; + char **words; + int i; + + /* Undo previous expansion */ + if (command == 0 && expanded == EXPAND && buf) { + restore_edstate(es, buf); + buf = 0; + expanded = NONE; + return 0; + } + if (buf) { + free_edstate(buf); + buf = 0; + } + + nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH, + es->cbuf, es->linelen, es->cursor, + &start, &end, &words, (int *) 0); + if (nwords == 0) { + vi_error(); + return -1; + } + + buf = save_edstate(es); + expanded = EXPAND; + del_range(start, end); + es->cursor = start; + for (i = 0; i < nwords; ) { + if (putbuf(words[i], (int) strlen(words[i]), 0) != 0) { + rval = -1; + break; + } + if (++i < nwords && putbuf(space, 1, 0) != 0) { + rval = -1; + break; + } + } + i = buf->cursor - end; + if (rval == 0 && i > 0) + es->cursor += i; + modified = 1; hnum = hlast; + insert = INSERT; + lastac = 0; + refresh(0); + return rval; +} + +static int +complete_word(command, count) + int command; + int count; +{ + static struct edstate *buf; + int rval = 0; + int nwords; + int start, end; + char **words; + char *match; + int match_len; + int is_unique; + int is_command; + + /* Undo previous completion */ + if (command == 0 && expanded == COMPLETE && buf) { + print_expansions(buf, 0); + expanded = PRINT; + return 0; + } + if (command == 0 && expanded == PRINT && buf) { + restore_edstate(es, buf); + buf = 0; + expanded = NONE; + return 0; + } + if (buf) { + free_edstate(buf); + buf = 0; + } + + /* XCF_FULLPATH for count 'cause the menu printed by print_expansions() + * was done this way. + */ + nwords = x_cf_glob(XCF_COMMAND_FILE | (count ? XCF_FULLPATH : 0), + es->cbuf, es->linelen, es->cursor, + &start, &end, &words, &is_command); + if (nwords == 0) { + vi_error(); + return -1; + } + if (count) { + int i; + + count--; + if (count >= nwords) { + vi_error(); + x_print_expansions(nwords, words, is_command); + x_free_words(nwords, words); + redraw_line(0); + return -1; + } + /* + * Expand the count'th word to its basename + */ + if (is_command) { + match = words[count] + + x_basename(words[count], (char *) 0); + /* If more than one possible match, use full path */ + for (i = 0; i < nwords; i++) + if (i != count && + FILECMP(words[i] + + x_basename(words[i], (char *) 0), + match) == 0) + { + match = words[count]; + break; + } + } else + match = words[count]; + match_len = strlen(match); + is_unique = 1; + /* expanded = PRINT; next call undo */ + } else { + match = words[0]; + match_len = x_longest_prefix(nwords, words); + expanded = COMPLETE; /* next call will list completions */ + is_unique = nwords == 1; + } + + buf = save_edstate(es); + del_range(start, end); + es->cursor = start; + if (putbuf(match, match_len, 0) != 0) + rval = -1; + else if (is_unique) { + /* If exact match, don't undo. Allows directory completions + * to be used (ie, complete the next portion of the path). + */ + expanded = NONE; + + /* If not a directory, add a space to the end... */ + if (match_len > 0 && !ISDIRSEP(match[match_len - 1])) + rval = putbuf(space, 1, 0); + } + x_free_words(nwords, words); + + modified = 1; hnum = hlast; + insert = INSERT; + lastac = 0; /* prevent this from being redone... */ + refresh(0); + + return rval; +} + +static int +print_expansions(e, command) + struct edstate *e; + int command; +{ + int nwords; + int start, end; + char **words; + int is_command; + + nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH, + e->cbuf, e->linelen, e->cursor, + &start, &end, &words, &is_command); + if (nwords == 0) { + vi_error(); + return -1; + } + x_print_expansions(nwords, words, is_command); + x_free_words(nwords, words); + redraw_line(0); + return 0; +} + +/* How long is char when displayed (not counting tabs) */ +static int +char_len(c) + int c; +{ + int len = 1; + + if ((c & 0x80) && Flag(FVISHOW8)) { + len += 2; + c &= 0x7f; + } + if (c < ' ' || c == 0x7f) + len++; + return len; +} + +/* Similar to x_zotc(emacs.c), but no tab wierdness */ +static void +x_vi_zotc(c) + int c; +{ + if (Flag(FVISHOW8) && (c & 0x80)) { + x_puts("M-"); + c &= 0x7f; + } + if (c < ' ' || c == 0x7f) { + x_putc('^'); + c ^= '@'; + } + x_putc(c); +} + +static void +vi_pprompt(full) + int full; +{ + pprompt(prompt + (full ? 0 : prompt_skip), prompt_trunc); +} + +static void +vi_error() +{ + /* Beem out of any macros as soon as an error occurs */ + vi_macro_reset(); + x_putc(BEL); + x_flush(); +} + +static void +vi_macro_reset() +{ + if (macro.p) { + afree(macro.buf, APERM); + memset((char *) ¯o, 0, sizeof(macro)); + } +} + +#endif /* VI */ |