OSDN Git Service

Merge "Upgrade to mksh R52b."
[android-x86/external-mksh.git] / src / edit.c
1 /*      $OpenBSD: edit.c,v 1.41 2015/09/01 13:12:31 tedu Exp $  */
2 /*      $OpenBSD: edit.h,v 1.9 2011/05/30 17:14:35 martynas Exp $       */
3 /*      $OpenBSD: emacs.c,v 1.52 2015/09/10 22:48:58 nicm Exp $ */
4 /*      $OpenBSD: vi.c,v 1.30 2015/09/10 22:48:58 nicm Exp $    */
5
6 /*-
7  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
8  *               2011, 2012, 2013, 2014, 2015
9  *      mirabilos <m@mirbsd.org>
10  *
11  * Provided that these terms and disclaimer and all copyright notices
12  * are retained or reproduced in an accompanying document, permission
13  * is granted to deal in this work without restriction, including un-
14  * limited rights to use, publicly perform, distribute, sell, modify,
15  * merge, give away, or sublicence.
16  *
17  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
18  * the utmost extent permitted by applicable law, neither express nor
19  * implied; without malicious intent or gross negligence. In no event
20  * may a licensor, author or contributor be held liable for indirect,
21  * direct, other damage, loss, or other issues arising in any way out
22  * of dealing in the work, even if advised of the possibility of such
23  * damage or existence of a defect, except proven that it results out
24  * of said person's immediate fault when using the work as intended.
25  */
26
27 #include "sh.h"
28
29 #ifndef MKSH_NO_CMDLINE_EDITING
30
31 __RCSID("$MirOS: src/bin/mksh/edit.c,v 1.292 2015/10/09 16:11:13 tg Exp $");
32
33 /*
34  * in later versions we might use libtermcap for this, but since external
35  * dependencies are problematic, this has not yet been decided on; another
36  * good string is "\033c" except on hardware terminals like the DEC VT420
37  * which do a full power cycle then...
38  */
39 #ifndef MKSH_CLS_STRING
40 #define MKSH_CLS_STRING         "\033[;H\033[J"
41 #endif
42 #ifndef MKSH_CLRTOEOL_STRING
43 #define MKSH_CLRTOEOL_STRING    "\033[K"
44 #endif
45
46 /* tty driver characters we are interested in */
47 typedef struct {
48         int erase;
49         int kill;
50         int werase;
51         int intr;
52         int quit;
53         int eof;
54 } X_chars;
55
56 static X_chars edchars;
57
58 /* x_cf_glob() flags */
59 #define XCF_COMMAND     BIT(0)  /* Do command completion */
60 #define XCF_FILE        BIT(1)  /* Do file completion */
61 #define XCF_FULLPATH    BIT(2)  /* command completion: store full path */
62 #define XCF_COMMAND_FILE (XCF_COMMAND | XCF_FILE)
63 #define XCF_IS_COMMAND  BIT(3)  /* return flag: is command */
64 #define XCF_IS_NOSPACE  BIT(4)  /* return flag: do not append a space */
65
66 static char editmode;
67 static int xx_cols;                     /* for Emacs mode */
68 static int modified;                    /* buffer has been "modified" */
69 static char *holdbufp;                  /* place to hold last edit buffer */
70
71 static int x_getc(void);
72 static void x_putcf(int);
73 static void x_modified(void);
74 static void x_mode(bool);
75 static int x_do_comment(char *, ssize_t, ssize_t *);
76 static void x_print_expansions(int, char * const *, bool);
77 static int x_cf_glob(int *, const char *, int, int, int *, int *, char ***);
78 static size_t x_longest_prefix(int, char * const *);
79 static void x_glob_hlp_add_qchar(char *);
80 static char *x_glob_hlp_tilde_and_rem_qchar(char *, bool);
81 static int x_basename(const char *, const char *);
82 static void x_free_words(int, char **);
83 static int x_escape(const char *, size_t, int (*)(const char *, size_t));
84 static int x_emacs(char *);
85 static void x_init_prompt(bool);
86 #if !MKSH_S_NOVI
87 static int x_vi(char *);
88 #endif
89
90 #define x_flush()       shf_flush(shl_out)
91 #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
92 #define x_putc(c)       x_putcf(c)
93 #else
94 #define x_putc(c)       shf_putc((c), shl_out)
95 #endif
96
97 static int path_order_cmp(const void *, const void *);
98 static void glob_table(const char *, XPtrV *, struct table *);
99 static void glob_path(int, const char *, XPtrV *, const char *);
100 static int x_file_glob(int *, char *, char ***);
101 static int x_command_glob(int, char *, char ***);
102 static int x_locate_word(const char *, int, int, int *, bool *);
103
104 static int x_e_getmbc(char *);
105 static int x_e_rebuildline(const char *);
106
107 /* +++ generic editing functions +++ */
108
109 /*
110  * read an edited command line
111  */
112 int
113 x_read(char *buf)
114 {
115         int i;
116
117         x_mode(true);
118         modified = 1;
119         if (Flag(FEMACS) || Flag(FGMACS))
120                 i = x_emacs(buf);
121 #if !MKSH_S_NOVI
122         else if (Flag(FVI))
123                 i = x_vi(buf);
124 #endif
125         else
126                 /* internal error */
127                 i = -1;
128         editmode = 0;
129         x_mode(false);
130         return (i);
131 }
132
133 /* tty I/O */
134
135 static int
136 x_getc(void)
137 {
138         char c;
139         ssize_t n;
140
141         while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR)
142                 if (trap) {
143                         x_mode(false);
144                         runtraps(0);
145 #ifdef SIGWINCH
146                         if (got_winch) {
147                                 change_winsz();
148                                 if (x_cols != xx_cols && editmode == 1) {
149                                         /* redraw line in Emacs mode */
150                                         xx_cols = x_cols;
151                                         x_init_prompt(false);
152                                         x_e_rebuildline(MKSH_CLRTOEOL_STRING);
153                                 }
154                         }
155 #endif
156                         x_mode(true);
157                 }
158         return ((n == 1) ? (int)(unsigned char)c : -1);
159 }
160
161 static void
162 x_putcf(int c)
163 {
164         shf_putc(c, shl_out);
165 }
166
167 /*********************************
168  * Misc common code for vi/emacs *
169  *********************************/
170
171 /*-
172  * Handle the commenting/uncommenting of a line.
173  * Returns:
174  *      1 if a carriage return is indicated (comment added)
175  *      0 if no return (comment removed)
176  *      -1 if there is an error (not enough room for comment chars)
177  * If successful, *lenp contains the new length. Note: cursor should be
178  * moved to the start of the line after (un)commenting.
179  */
180 static int
181 x_do_comment(char *buf, ssize_t bsize, ssize_t *lenp)
182 {
183         ssize_t i, j, len = *lenp;
184
185         if (len == 0)
186                 /* somewhat arbitrary - it's what AT&T ksh does */
187                 return (1);
188
189         /* Already commented? */
190         if (buf[0] == '#') {
191                 bool saw_nl = false;
192
193                 for (j = 0, i = 1; i < len; i++) {
194                         if (!saw_nl || buf[i] != '#')
195                                 buf[j++] = buf[i];
196                         saw_nl = buf[i] == '\n';
197                 }
198                 *lenp = j;
199                 return (0);
200         } else {
201                 int n = 1;
202
203                 /* See if there's room for the #s - 1 per \n */
204                 for (i = 0; i < len; i++)
205                         if (buf[i] == '\n')
206                                 n++;
207                 if (len + n >= bsize)
208                         return (-1);
209                 /* Now add them... */
210                 for (i = len, j = len + n; --i >= 0; ) {
211                         if (buf[i] == '\n')
212                                 buf[--j] = '#';
213                         buf[--j] = buf[i];
214                 }
215                 buf[0] = '#';
216                 *lenp += n;
217                 return (1);
218         }
219 }
220
221 /****************************************************
222  * Common file/command completion code for vi/emacs *
223  ****************************************************/
224
225 static void
226 x_print_expansions(int nwords, char * const *words, bool is_command)
227 {
228         bool use_copy = false;
229         int prefix_len;
230         XPtrV l = { NULL, 0, 0 };
231
232         /*
233          * Check if all matches are in the same directory (in this
234          * case, we want to omit the directory name)
235          */
236         if (!is_command &&
237             (prefix_len = x_longest_prefix(nwords, words)) > 0) {
238                 int i;
239
240                 /* Special case for 1 match (prefix is whole word) */
241                 if (nwords == 1)
242                         prefix_len = x_basename(words[0], NULL);
243                 /* Any (non-trailing) slashes in non-common word suffixes? */
244                 for (i = 0; i < nwords; i++)
245                         if (x_basename(words[i] + prefix_len, NULL) >
246                             prefix_len)
247                                 break;
248                 /* All in same directory? */
249                 if (i == nwords) {
250                         while (prefix_len > 0 && words[0][prefix_len - 1] != '/')
251                                 prefix_len--;
252                         use_copy = true;
253                         XPinit(l, nwords + 1);
254                         for (i = 0; i < nwords; i++)
255                                 XPput(l, words[i] + prefix_len);
256                         XPput(l, NULL);
257                 }
258         }
259         /*
260          * Enumerate expansions
261          */
262         x_putc('\r');
263         x_putc('\n');
264         pr_list(use_copy ? (char **)XPptrv(l) : words);
265
266         if (use_copy)
267                 /* not x_free_words() */
268                 XPfree(l);
269 }
270
271 /*
272  * Convert backslash-escaped string to QCHAR-escaped
273  * string useful for globbing; loses QCHAR unless it
274  * can squeeze in, eg. by previous loss of backslash
275  */
276 static void
277 x_glob_hlp_add_qchar(char *cp)
278 {
279         char ch, *dp = cp;
280         bool escaping = false;
281
282         while ((ch = *cp++)) {
283                 if (ch == '\\' && !escaping) {
284                         escaping = true;
285                         continue;
286                 }
287                 if (escaping || (ch == QCHAR && (cp - dp) > 1)) {
288                         /*
289                          * empirically made list of chars to escape
290                          * for globbing as well as QCHAR itself
291                          */
292                         switch (ch) {
293                         case QCHAR:
294                         case '$':
295                         case '*':
296                         case '?':
297                         case '[':
298                         case '\\':
299                         case '`':
300                                 *dp++ = QCHAR;
301                                 break;
302                         }
303                         escaping = false;
304                 }
305                 *dp++ = ch;
306         }
307         *dp = '\0';
308 }
309
310 /*
311  * Run tilde expansion on argument string, return the result
312  * after unescaping; if the flag is set, the original string
313  * is freed if changed and assumed backslash-escaped, if not
314  * it is assumed QCHAR-escaped
315  */
316 static char *
317 x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag)
318 {
319         char ch, *cp, *dp;
320
321         /*
322          * On the string, check whether we have a tilde expansion,
323          * and if so, discern "~foo/bar" and "~/baz" from "~blah";
324          * if we have a directory part (the former), try to expand
325          */
326         if (*s == '~' && (cp = strchr(s, '/')) != NULL) {
327                 /* ok, so split into "~foo"/"bar" or "~"/"baz" */
328                 *cp++ = 0;
329                 /* try to expand the tilde */
330                 if (!(dp = do_tilde(s + 1))) {
331                         /* nope, revert damage */
332                         *--cp = '/';
333                 } else {
334                         /* ok, expand and replace */
335                         cp = shf_smprintf("%s/%s", dp, cp);
336                         if (magic_flag)
337                                 afree(s, ATEMP);
338                         s = cp;
339                 }
340         }
341
342         /* ... convert it from backslash-escaped via QCHAR-escaped... */
343         if (magic_flag)
344                 x_glob_hlp_add_qchar(s);
345         /* ... to unescaped, for comparison with the matches */
346         cp = dp = s;
347
348         while ((ch = *cp++)) {
349                 if (ch == QCHAR && !(ch = *cp++))
350                         break;
351                 *dp++ = ch;
352         }
353         *dp = '\0';
354
355         return (s);
356 }
357
358 /**
359  * Do file globbing:
360  *      - does expansion, checks for no match, etc.
361  *      - sets *wordsp to array of matching strings
362  *      - returns number of matching strings
363  */
364 static int
365 x_file_glob(int *flagsp, char *toglob, char ***wordsp)
366 {
367         char **words, *cp;
368         int nwords;
369         XPtrV w;
370         struct source *s, *sold;
371
372         /* remove all escaping backward slashes */
373         x_glob_hlp_add_qchar(toglob);
374
375         /*
376          * Convert "foo*" (toglob) to an array of strings (words)
377          */
378         sold = source;
379         s = pushs(SWSTR, ATEMP);
380         s->start = s->str = toglob;
381         source = s;
382         if (yylex(ONEWORD | LQCHAR) != LWORD) {
383                 source = sold;
384                 internal_warningf("%s: %s", "fileglob", "bad substitution");
385                 return (0);
386         }
387         source = sold;
388         afree(s, ATEMP);
389         XPinit(w, 32);
390         cp = yylval.cp;
391         while (*cp == CHAR || *cp == QCHAR)
392                 cp += 2;
393         nwords = DOGLOB | DOTILDE | DOMARKDIRS;
394         if (*cp != EOS) {
395                 /* probably a $FOO expansion */
396                 *flagsp |= XCF_IS_NOSPACE;
397                 /* this always results in at most one match */
398                 nwords = 0;
399         }
400         expand(yylval.cp, &w, nwords);
401         XPput(w, NULL);
402         words = (char **)XPclose(w);
403
404         for (nwords = 0; words[nwords]; nwords++)
405                 ;
406         if (nwords == 1) {
407                 struct stat statb;
408
409                 /* Expand any tilde and drop all QCHAR for comparison */
410                 toglob = x_glob_hlp_tilde_and_rem_qchar(toglob, false);
411
412                 /*
413                  * Check if globbing failed (returned glob pattern),
414                  * but be careful (e.g. toglob == "ab*" when the file
415                  * "ab*" exists is not an error).
416                  * Also, check for empty result - happens if we tried
417                  * to glob something which evaluated to an empty
418                  * string (e.g., "$FOO" when there is no FOO, etc).
419                  */
420                 if ((strcmp(words[0], toglob) == 0 &&
421                     stat(words[0], &statb) < 0) ||
422                     words[0][0] == '\0') {
423                         x_free_words(nwords, words);
424                         words = NULL;
425                         nwords = 0;
426                 }
427         }
428
429         if ((*wordsp = nwords ? words : NULL) == NULL && words != NULL)
430                 x_free_words(nwords, words);
431
432         return (nwords);
433 }
434
435 /* Data structure used in x_command_glob() */
436 struct path_order_info {
437         char *word;
438         int base;
439         int path_order;
440 };
441
442 /* Compare routine used in x_command_glob() */
443 static int
444 path_order_cmp(const void *aa, const void *bb)
445 {
446         const struct path_order_info *a = (const struct path_order_info *)aa;
447         const struct path_order_info *b = (const struct path_order_info *)bb;
448         int t;
449
450         t = strcmp(a->word + a->base, b->word + b->base);
451         return (t ? t : a->path_order - b->path_order);
452 }
453
454 static int
455 x_command_glob(int flags, char *toglob, char ***wordsp)
456 {
457         char *pat, *fpath;
458         size_t nwords;
459         XPtrV w;
460         struct block *l;
461
462         /* Convert "foo*" (toglob) to a pattern for future use */
463         pat = evalstr(toglob, DOPAT | DOTILDE);
464
465         XPinit(w, 32);
466
467         glob_table(pat, &w, &keywords);
468         glob_table(pat, &w, &aliases);
469         glob_table(pat, &w, &builtins);
470         for (l = e->loc; l; l = l->next)
471                 glob_table(pat, &w, &l->funs);
472
473         glob_path(flags, pat, &w, path);
474         if ((fpath = str_val(global("FPATH"))) != null)
475                 glob_path(flags, pat, &w, fpath);
476
477         nwords = XPsize(w);
478
479         if (!nwords) {
480                 *wordsp = NULL;
481                 XPfree(w);
482                 return (0);
483         }
484         /* Sort entries */
485         if (flags & XCF_FULLPATH) {
486                 /* Sort by basename, then path order */
487                 struct path_order_info *info, *last_info = NULL;
488                 char **words = (char **)XPptrv(w);
489                 size_t i, path_order = 0;
490
491                 info = (struct path_order_info *)
492                     alloc2(nwords, sizeof(struct path_order_info), ATEMP);
493                 for (i = 0; i < nwords; i++) {
494                         info[i].word = words[i];
495                         info[i].base = x_basename(words[i], NULL);
496                         if (!last_info || info[i].base != last_info->base ||
497                             strncmp(words[i], last_info->word, info[i].base) != 0) {
498                                 last_info = &info[i];
499                                 path_order++;
500                         }
501                         info[i].path_order = path_order;
502                 }
503                 qsort(info, nwords, sizeof(struct path_order_info),
504                     path_order_cmp);
505                 for (i = 0; i < nwords; i++)
506                         words[i] = info[i].word;
507                 afree(info, ATEMP);
508         } else {
509                 /* Sort and remove duplicate entries */
510                 char **words = (char **)XPptrv(w);
511                 size_t i, j;
512
513                 qsort(words, nwords, sizeof(void *), xstrcmp);
514                 for (i = j = 0; i < nwords - 1; i++) {
515                         if (strcmp(words[i], words[i + 1]))
516                                 words[j++] = words[i];
517                         else
518                                 afree(words[i], ATEMP);
519                 }
520                 words[j++] = words[i];
521                 w.len = nwords = j;
522         }
523
524         XPput(w, NULL);
525         *wordsp = (char **)XPclose(w);
526
527         return (nwords);
528 }
529
530 #define IS_WORDC(c)     (!ctype(c, C_LEX1) && (c) != '\'' && (c) != '"' && \
531                             (c) != '`' && (c) != '=' && (c) != ':')
532
533 static int
534 x_locate_word(const char *buf, int buflen, int pos, int *startp,
535     bool *is_commandp)
536 {
537         int start, end;
538
539         /* Bad call? Probably should report error */
540         if (pos < 0 || pos > buflen) {
541                 *startp = pos;
542                 *is_commandp = false;
543                 return (0);
544         }
545         /* The case where pos == buflen happens to take care of itself... */
546
547         start = pos;
548         /*
549          * Keep going backwards to start of word (has effect of allowing
550          * one blank after the end of a word)
551          */
552         for (; (start > 0 && IS_WORDC(buf[start - 1])) ||
553             (start > 1 && buf[start - 2] == '\\'); start--)
554                 ;
555         /* Go forwards to end of word */
556         for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
557                 if (buf[end] == '\\' && (end + 1) < buflen)
558                         end++;
559         }
560
561         if (is_commandp) {
562                 bool iscmd;
563                 int p = start - 1;
564
565                 /* Figure out if this is a command */
566                 while (p >= 0 && ksh_isspace(buf[p]))
567                         p--;
568                 iscmd = p < 0 || vstrchr(";|&()`", buf[p]);
569                 if (iscmd) {
570                         /*
571                          * If command has a /, path, etc. is not searched;
572                          * only current directory is searched which is just
573                          * like file globbing.
574                          */
575                         for (p = start; p < end; p++)
576                                 if (buf[p] == '/')
577                                         break;
578                         iscmd = p == end;
579                 }
580                 *is_commandp = iscmd;
581         }
582         *startp = start;
583
584         return (end - start);
585 }
586
587 static int
588 x_cf_glob(int *flagsp, const char *buf, int buflen, int pos, int *startp,
589     int *endp, char ***wordsp)
590 {
591         int len, nwords = 0;
592         char **words = NULL;
593         bool is_command;
594
595         len = x_locate_word(buf, buflen, pos, startp, &is_command);
596         if (!((*flagsp) & XCF_COMMAND))
597                 is_command = false;
598         /*
599          * Don't do command globing on zero length strings - it takes too
600          * long and isn't very useful. File globs are more likely to be
601          * useful, so allow these.
602          */
603         if (len == 0 && is_command)
604                 return (0);
605
606         if (len >= 0) {
607                 char *toglob, *s;
608
609                 /*
610                  * Given a string, copy it and possibly add a '*' to the end.
611                  */
612
613                 strndupx(toglob, buf + *startp, len + /* the '*' */ 1, ATEMP);
614                 toglob[len] = '\0';
615
616                 /*
617                  * If the pathname contains a wildcard (an unquoted '*',
618                  * '?', or '[') or an extglob, then it is globbed based
619                  * on that value (i.e., without the appended '*'). Same
620                  * for parameter substitutions (as in â€ścat $HOME/.ss↹”)
621                  * without appending a trailing space (LP: #710539), as
622                  * well as for â€ś~foo” (but not â€ś~foo/”).
623                  */
624                 for (s = toglob; *s; s++) {
625                         if (*s == '\\' && s[1])
626                                 s++;
627                         else if (*s == '?' || *s == '*' || *s == '[' ||
628                             *s == '$' ||
629                             /* ?() *() +() @() !() but two already checked */
630                             (s[1] == '(' /*)*/ &&
631                             (*s == '+' || *s == '@' || *s == '!'))) {
632                                 /*
633                                  * just expand based on the extglob
634                                  * or parameter
635                                  */
636                                 goto dont_add_glob;
637                         }
638                 }
639
640                 if (*toglob == '~' && !vstrchr(toglob, '/')) {
641                         /* neither for '~foo' (but '~foo/bar') */
642                         *flagsp |= XCF_IS_NOSPACE;
643                         goto dont_add_glob;
644                 }
645
646                 /* append a glob */
647                 toglob[len] = '*';
648                 toglob[len + 1] = '\0';
649  dont_add_glob:
650                 /*
651                  * Expand (glob) it now.
652                  */
653
654                 nwords = is_command ?
655                     x_command_glob(*flagsp, toglob, &words) :
656                     x_file_glob(flagsp, toglob, &words);
657                 afree(toglob, ATEMP);
658         }
659         if (nwords == 0) {
660                 *wordsp = NULL;
661                 return (0);
662         }
663         if (is_command)
664                 *flagsp |= XCF_IS_COMMAND;
665         *wordsp = words;
666         *endp = *startp + len;
667
668         return (nwords);
669 }
670
671 /*
672  * Find longest common prefix
673  */
674 static size_t
675 x_longest_prefix(int nwords, char * const * words)
676 {
677         int i;
678         size_t j, prefix_len;
679         char *p;
680
681         if (nwords <= 0)
682                 return (0);
683
684         prefix_len = strlen(words[0]);
685         for (i = 1; i < nwords; i++)
686                 for (j = 0, p = words[i]; j < prefix_len; j++)
687                         if (p[j] != words[0][j]) {
688                                 prefix_len = j;
689                                 break;
690                         }
691         /* false for nwords==1 as 0 = words[0][prefix_len] then */
692         if (UTFMODE && prefix_len && (words[0][prefix_len] & 0xC0) == 0x80)
693                 while (prefix_len && (words[0][prefix_len] & 0xC0) != 0xC0)
694                         --prefix_len;
695         return (prefix_len);
696 }
697
698 static void
699 x_free_words(int nwords, char **words)
700 {
701         while (nwords)
702                 afree(words[--nwords], ATEMP);
703         afree(words, ATEMP);
704 }
705
706 /*-
707  * Return the offset of the basename of string s (which ends at se - need not
708  * be null terminated). Trailing slashes are ignored. If s is just a slash,
709  * then the offset is 0 (actually, length - 1).
710  *      s               Return
711  *      /etc            1
712  *      /etc/           1
713  *      /etc//          1
714  *      /etc/fo         5
715  *      foo             0
716  *      ///             2
717  *                      0
718  */
719 static int
720 x_basename(const char *s, const char *se)
721 {
722         const char *p;
723
724         if (se == NULL)
725                 se = s + strlen(s);
726         if (s == se)
727                 return (0);
728
729         /* Skip trailing slashes */
730         for (p = se - 1; p > s && *p == '/'; p--)
731                 ;
732         for (; p > s && *p != '/'; p--)
733                 ;
734         if (*p == '/' && p + 1 < se)
735                 p++;
736
737         return (p - s);
738 }
739
740 /*
741  * Apply pattern matching to a table: all table entries that match a pattern
742  * are added to wp.
743  */
744 static void
745 glob_table(const char *pat, XPtrV *wp, struct table *tp)
746 {
747         struct tstate ts;
748         struct tbl *te;
749
750         ktwalk(&ts, tp);
751         while ((te = ktnext(&ts)))
752                 if (gmatchx(te->name, pat, false)) {
753                         char *cp;
754
755                         strdupx(cp, te->name, ATEMP);
756                         XPput(*wp, cp);
757                 }
758 }
759
760 static void
761 glob_path(int flags, const char *pat, XPtrV *wp, const char *lpath)
762 {
763         const char *sp = lpath, *p;
764         char *xp, **words;
765         size_t pathlen, patlen, oldsize, newsize, i, j;
766         XString xs;
767
768         patlen = strlen(pat);
769         checkoktoadd(patlen, 129 + X_EXTRA);
770         ++patlen;
771         Xinit(xs, xp, patlen + 128, ATEMP);
772         while (sp) {
773                 xp = Xstring(xs, xp);
774                 if (!(p = cstrchr(sp, MKSH_PATHSEPC)))
775                         p = sp + strlen(sp);
776                 pathlen = p - sp;
777                 if (pathlen) {
778                         /*
779                          * Copy sp into xp, stuffing any MAGIC characters
780                          * on the way
781                          */
782                         const char *s = sp;
783
784                         XcheckN(xs, xp, pathlen * 2);
785                         while (s < p) {
786                                 if (ISMAGIC(*s))
787                                         *xp++ = MAGIC;
788                                 *xp++ = *s++;
789                         }
790                         *xp++ = '/';
791                         pathlen++;
792                 }
793                 sp = p;
794                 XcheckN(xs, xp, patlen);
795                 memcpy(xp, pat, patlen);
796
797                 oldsize = XPsize(*wp);
798                 /* mark dirs */
799                 glob_str(Xstring(xs, xp), wp, true);
800                 newsize = XPsize(*wp);
801
802                 /* Check that each match is executable... */
803                 words = (char **)XPptrv(*wp);
804                 for (i = j = oldsize; i < newsize; i++) {
805                         if (ksh_access(words[i], X_OK) == 0) {
806                                 words[j] = words[i];
807                                 if (!(flags & XCF_FULLPATH))
808                                         memmove(words[j], words[j] + pathlen,
809                                             strlen(words[j] + pathlen) + 1);
810                                 j++;
811                         } else
812                                 afree(words[i], ATEMP);
813                 }
814                 wp->len = j;
815
816                 if (!*sp++)
817                         break;
818         }
819         Xfree(xs, xp);
820 }
821
822 /*
823  * if argument string contains any special characters, they will
824  * be escaped and the result will be put into edit buffer by
825  * keybinding-specific function
826  */
827 static int
828 x_escape(const char *s, size_t len, int (*putbuf_func)(const char *, size_t))
829 {
830         size_t add = 0, wlen = len;
831         const char *ifs = str_val(local("IFS", 0));
832         int rval = 0;
833
834         while (wlen - add > 0)
835                 if (vstrchr("\"#$&'()*:;<=>?[\\`{|}", s[add]) ||
836                     vstrchr(ifs, s[add])) {
837                         if (putbuf_func(s, add) != 0) {
838                                 rval = -1;
839                                 break;
840                         }
841                         putbuf_func(s[add] == '\n' ? "'" : "\\", 1);
842                         putbuf_func(&s[add], 1);
843                         if (s[add] == '\n')
844                                 putbuf_func("'", 1);
845
846                         add++;
847                         wlen -= add;
848                         s += add;
849                         add = 0;
850                 } else
851                         ++add;
852         if (wlen > 0 && rval == 0)
853                 rval = putbuf_func(s, wlen);
854
855         return (rval);
856 }
857
858
859 /* +++ emacs editing mode +++ */
860
861 static  Area    aedit;
862 #define AEDIT   &aedit          /* area for kill ring and macro defns */
863
864 /* values returned by keyboard functions */
865 #define KSTD    0
866 #define KEOL    1               /* ^M, ^J */
867 #define KINTR   2               /* ^G, ^C */
868
869 struct x_ftab {
870         int (*xf_func)(int c);
871         const char *xf_name;
872         short xf_flags;
873 };
874
875 struct x_defbindings {
876         unsigned char xdb_func; /* XFUNC_* */
877         unsigned char xdb_tab;
878         unsigned char xdb_char;
879 };
880
881 #define XF_ARG          1       /* command takes number prefix */
882 #define XF_NOBIND       2       /* not allowed to bind to function */
883 #define XF_PREFIX       4       /* function sets prefix */
884
885 /* Separator for completion */
886 #define is_cfs(c)       ((c) == ' ' || (c) == '\t' || (c) == '"' || (c) == '\'')
887 /* Separator for motion */
888 #define is_mfs(c)       (!(ksh_isalnux(c) || (c) == '$' || ((c) & 0x80)))
889
890 #define X_NTABS         4                       /* normal, meta1, meta2, pc */
891 #define X_TABSZ         256                     /* size of keydef tables etc */
892
893 /*-
894  * Arguments for do_complete()
895  * 0 = enumerate        M-=     complete as much as possible and then list
896  * 1 = complete         M-Esc
897  * 2 = list             M-?
898  */
899 typedef enum {
900         CT_LIST,        /* list the possible completions */
901         CT_COMPLETE,    /* complete to longest prefix */
902         CT_COMPLIST     /* complete and then list (if non-exact) */
903 } Comp_type;
904
905 /*
906  * The following are used for my horizontal scrolling stuff
907  */
908 static char *xbuf;              /* beg input buffer */
909 static char *xend;              /* end input buffer */
910 static char *xcp;               /* current position */
911 static char *xep;               /* current end */
912 static char *xbp;               /* start of visible portion of input buffer */
913 static char *xlp;               /* last char visible on screen */
914 static bool x_adj_ok;
915 /*
916  * we use x_adj_done so that functions can tell
917  * whether x_adjust() has been called while they are active.
918  */
919 static int x_adj_done;          /* is incremented by x_adjust() */
920
921 static int x_displen;
922 static int x_arg;               /* general purpose arg */
923 static bool x_arg_defaulted;    /* x_arg not explicitly set; defaulted to 1 */
924
925 static bool xlp_valid;          /* lastvis pointer was recalculated */
926
927 static char **x_histp;          /* history position */
928 static int x_nextcmd;           /* for newline-and-next */
929 static char **x_histncp;        /* saved x_histp for " */
930 static char *xmp;               /* mark pointer */
931 static unsigned char x_last_command;
932 static unsigned char (*x_tab)[X_TABSZ]; /* key definition */
933 #ifndef MKSH_SMALL
934 static char *(*x_atab)[X_TABSZ];        /* macro definitions */
935 #endif
936 static unsigned char x_bound[(X_TABSZ * X_NTABS + 7) / 8];
937 #define KILLSIZE        20
938 static char *killstack[KILLSIZE];
939 static int killsp, killtp;
940 static int x_curprefix;
941 #ifndef MKSH_SMALL
942 static char *macroptr;          /* bind key macro active? */
943 #endif
944 #if !MKSH_S_NOVI
945 static int winwidth;            /* width of window */
946 static char *wbuf[2];           /* window buffers */
947 static int wbuf_len;            /* length of window buffers (x_cols - 3) */
948 static int win;                 /* window buffer in use */
949 static char morec;              /* more character at right of window */
950 static int lastref;             /* argument to last refresh() */
951 static int holdlen;             /* length of holdbuf */
952 #endif
953 static int pwidth;              /* width of prompt */
954 static int prompt_trunc;        /* how much of prompt to truncate or -1 */
955 static int x_col;               /* current column on line */
956
957 static int x_ins(const char *);
958 static void x_delete(size_t, bool);
959 static size_t x_bword(void);
960 static size_t x_fword(bool);
961 static void x_goto(char *);
962 static char *x_bs0(char *, char *) MKSH_A_PURE;
963 static void x_bs3(char **);
964 static int x_size_str(char *);
965 static int x_size2(char *, char **);
966 static void x_zots(char *);
967 static void x_zotc3(char **);
968 static void x_load_hist(char **);
969 static int x_search(char *, int, int);
970 #ifndef MKSH_SMALL
971 static int x_search_dir(int);
972 #endif
973 static int x_match(char *, char *);
974 static void x_redraw(int);
975 static void x_push(int);
976 static char *x_mapin(const char *, Area *);
977 static char *x_mapout(int);
978 static void x_mapout2(int, char **);
979 static void x_print(int, int);
980 static void x_adjust(void);
981 static void x_e_ungetc(int);
982 static int x_e_getc(void);
983 static void x_e_putc2(int);
984 static void x_e_putc3(const char **);
985 static void x_e_puts(const char *);
986 #ifndef MKSH_SMALL
987 static int x_fold_case(int);
988 #endif
989 static char *x_lastcp(void);
990 static void do_complete(int, Comp_type);
991 static size_t x_nb2nc(size_t) MKSH_A_PURE;
992
993 static int unget_char = -1;
994
995 static int x_do_ins(const char *, size_t);
996 static void bind_if_not_bound(int, int, int);
997
998 enum emacs_funcs {
999 #define EMACSFN_ENUMS
1000 #include "emacsfn.h"
1001         XFUNC_MAX
1002 };
1003
1004 #define EMACSFN_DEFNS
1005 #include "emacsfn.h"
1006
1007 static const struct x_ftab x_ftab[] = {
1008 #define EMACSFN_ITEMS
1009 #include "emacsfn.h"
1010 };
1011
1012 static struct x_defbindings const x_defbindings[] = {
1013         { XFUNC_del_back,               0, CTRL('?')    },
1014         { XFUNC_del_bword,              1, CTRL('?')    },
1015         { XFUNC_eot_del,                0, CTRL('D')    },
1016         { XFUNC_del_back,               0, CTRL('H')    },
1017         { XFUNC_del_bword,              1, CTRL('H')    },
1018         { XFUNC_del_bword,              1,      'h'     },
1019         { XFUNC_mv_bword,               1,      'b'     },
1020         { XFUNC_mv_fword,               1,      'f'     },
1021         { XFUNC_del_fword,              1,      'd'     },
1022         { XFUNC_mv_back,                0, CTRL('B')    },
1023         { XFUNC_mv_forw,                0, CTRL('F')    },
1024         { XFUNC_search_char_forw,       0, CTRL(']')    },
1025         { XFUNC_search_char_back,       1, CTRL(']')    },
1026         { XFUNC_newline,                0, CTRL('M')    },
1027         { XFUNC_newline,                0, CTRL('J')    },
1028         { XFUNC_end_of_text,            0, CTRL('_')    },
1029         { XFUNC_abort,                  0, CTRL('G')    },
1030         { XFUNC_prev_com,               0, CTRL('P')    },
1031         { XFUNC_next_com,               0, CTRL('N')    },
1032         { XFUNC_nl_next_com,            0, CTRL('O')    },
1033         { XFUNC_search_hist,            0, CTRL('R')    },
1034         { XFUNC_beg_hist,               1,      '<'     },
1035         { XFUNC_end_hist,               1,      '>'     },
1036         { XFUNC_goto_hist,              1,      'g'     },
1037         { XFUNC_mv_end,                 0, CTRL('E')    },
1038         { XFUNC_mv_begin,               0, CTRL('A')    },
1039         { XFUNC_draw_line,              0, CTRL('L')    },
1040         { XFUNC_cls,                    1, CTRL('L')    },
1041         { XFUNC_meta1,                  0, CTRL('[')    },
1042         { XFUNC_meta2,                  0, CTRL('X')    },
1043         { XFUNC_kill,                   0, CTRL('K')    },
1044         { XFUNC_yank,                   0, CTRL('Y')    },
1045         { XFUNC_meta_yank,              1,      'y'     },
1046         { XFUNC_literal,                0, CTRL('^')    },
1047         { XFUNC_comment,                1,      '#'     },
1048         { XFUNC_transpose,              0, CTRL('T')    },
1049         { XFUNC_complete,               1, CTRL('[')    },
1050         { XFUNC_comp_list,              0, CTRL('I')    },
1051         { XFUNC_comp_list,              1,      '='     },
1052         { XFUNC_enumerate,              1,      '?'     },
1053         { XFUNC_expand,                 1,      '*'     },
1054         { XFUNC_comp_file,              1, CTRL('X')    },
1055         { XFUNC_comp_comm,              2, CTRL('[')    },
1056         { XFUNC_list_comm,              2,      '?'     },
1057         { XFUNC_list_file,              2, CTRL('Y')    },
1058         { XFUNC_set_mark,               1,      ' '     },
1059         { XFUNC_kill_region,            0, CTRL('W')    },
1060         { XFUNC_xchg_point_mark,        2, CTRL('X')    },
1061         { XFUNC_literal,                0, CTRL('V')    },
1062         { XFUNC_version,                1, CTRL('V')    },
1063         { XFUNC_prev_histword,          1,      '.'     },
1064         { XFUNC_prev_histword,          1,      '_'     },
1065         { XFUNC_set_arg,                1,      '0'     },
1066         { XFUNC_set_arg,                1,      '1'     },
1067         { XFUNC_set_arg,                1,      '2'     },
1068         { XFUNC_set_arg,                1,      '3'     },
1069         { XFUNC_set_arg,                1,      '4'     },
1070         { XFUNC_set_arg,                1,      '5'     },
1071         { XFUNC_set_arg,                1,      '6'     },
1072         { XFUNC_set_arg,                1,      '7'     },
1073         { XFUNC_set_arg,                1,      '8'     },
1074         { XFUNC_set_arg,                1,      '9'     },
1075 #ifndef MKSH_SMALL
1076         { XFUNC_fold_upper,             1,      'U'     },
1077         { XFUNC_fold_upper,             1,      'u'     },
1078         { XFUNC_fold_lower,             1,      'L'     },
1079         { XFUNC_fold_lower,             1,      'l'     },
1080         { XFUNC_fold_capitalise,        1,      'C'     },
1081         { XFUNC_fold_capitalise,        1,      'c'     },
1082 #endif
1083         /*
1084          * These for ANSI arrow keys: arguablely shouldn't be here by
1085          * default, but its simpler/faster/smaller than using termcap
1086          * entries.
1087          */
1088         { XFUNC_meta2,                  1,      '['     },
1089         { XFUNC_meta2,                  1,      'O'     },
1090         { XFUNC_prev_com,               2,      'A'     },
1091         { XFUNC_next_com,               2,      'B'     },
1092         { XFUNC_mv_forw,                2,      'C'     },
1093         { XFUNC_mv_back,                2,      'D'     },
1094 #ifndef MKSH_SMALL
1095         { XFUNC_vt_hack,                2,      '1'     },
1096         { XFUNC_mv_begin | 0x80,        2,      '7'     },
1097         { XFUNC_mv_begin,               2,      'H'     },
1098         { XFUNC_mv_end | 0x80,          2,      '4'     },
1099         { XFUNC_mv_end | 0x80,          2,      '8'     },
1100         { XFUNC_mv_end,                 2,      'F'     },
1101         { XFUNC_del_char | 0x80,        2,      '3'     },
1102         { XFUNC_del_char,               2,      'P'     },
1103         { XFUNC_search_hist_up | 0x80,  2,      '5'     },
1104         { XFUNC_search_hist_dn | 0x80,  2,      '6'     },
1105 #endif
1106         /* PC scancodes */
1107 #if !defined(MKSH_SMALL) || defined(__OS2__)
1108         { XFUNC_meta3,                  0,      0       },
1109         { XFUNC_mv_begin,               3,      71      },
1110         { XFUNC_prev_com,               3,      72      },
1111 #ifndef MKSH_SMALL
1112         { XFUNC_search_hist_up,         3,      73      },
1113 #endif
1114         { XFUNC_mv_back,                3,      75      },
1115         { XFUNC_mv_forw,                3,      77      },
1116         { XFUNC_mv_end,                 3,      79      },
1117         { XFUNC_next_com,               3,      80      },
1118 #ifndef MKSH_SMALL
1119         { XFUNC_search_hist_dn,         3,      81      },
1120 #endif
1121         { XFUNC_del_char,               3,      83      },
1122 #endif
1123 #ifndef MKSH_SMALL
1124         /* more non-standard ones */
1125         { XFUNC_edit_line,              2,      'e'     }
1126 #endif
1127 };
1128
1129 static size_t
1130 x_nb2nc(size_t nb)
1131 {
1132         char *cp;
1133         size_t nc = 0;
1134
1135         for (cp = xcp; cp < (xcp + nb); ++nc)
1136                 cp += utf_ptradj(cp);
1137         return (nc);
1138 }
1139
1140 static void
1141 x_modified(void)
1142 {
1143         if (!modified) {
1144                 x_histp = histptr + 1;
1145                 modified = 1;
1146         }
1147 }
1148
1149 #ifdef MKSH_SMALL
1150 #define XFUNC_VALUE(f) (f)
1151 #else
1152 #define XFUNC_VALUE(f) (f & 0x7F)
1153 #endif
1154
1155 static int
1156 x_e_getmbc(char *sbuf)
1157 {
1158         int c, pos = 0;
1159         unsigned char *buf = (unsigned char *)sbuf;
1160
1161         memset(buf, 0, 4);
1162         buf[pos++] = c = x_e_getc();
1163         if (c == -1)
1164                 return (-1);
1165         if (UTFMODE) {
1166                 if ((buf[0] >= 0xC2) && (buf[0] < 0xF0)) {
1167                         c = x_e_getc();
1168                         if (c == -1)
1169                                 return (-1);
1170                         if ((c & 0xC0) != 0x80) {
1171                                 x_e_ungetc(c);
1172                                 return (1);
1173                         }
1174                         buf[pos++] = c;
1175                 }
1176                 if ((buf[0] >= 0xE0) && (buf[0] < 0xF0)) {
1177                         /* XXX x_e_ungetc is one-octet only */
1178                         buf[pos++] = c = x_e_getc();
1179                         if (c == -1)
1180                                 return (-1);
1181                 }
1182         }
1183         return (pos);
1184 }
1185
1186 static void
1187 x_init_prompt(bool doprint)
1188 {
1189         prompt_trunc = pprompt(prompt, doprint ? 0 : -1);
1190         pwidth = prompt_trunc % x_cols;
1191         prompt_trunc -= pwidth;
1192         if ((mksh_uari_t)pwidth > ((mksh_uari_t)x_cols - 3 - MIN_EDIT_SPACE)) {
1193                 /* force newline after prompt */
1194                 prompt_trunc = -1;
1195                 pwidth = 0;
1196                 if (doprint)
1197                         x_e_putc2('\n');
1198         }
1199 }
1200
1201 static int
1202 x_emacs(char *buf)
1203 {
1204         int c, i;
1205         unsigned char f;
1206
1207         xbp = xbuf = buf;
1208         xend = buf + LINE;
1209         xlp = xcp = xep = buf;
1210         *xcp = 0;
1211         xlp_valid = true;
1212         xmp = NULL;
1213         x_curprefix = 0;
1214         x_histp = histptr + 1;
1215         x_last_command = XFUNC_error;
1216
1217         x_init_prompt(true);
1218         x_displen = (xx_cols = x_cols) - 2 - (x_col = pwidth);
1219         x_adj_done = 0;
1220         x_adj_ok = true;
1221
1222         x_histncp = NULL;
1223         if (x_nextcmd >= 0) {
1224                 int off = source->line - x_nextcmd;
1225                 if (histptr - history >= off) {
1226                         x_load_hist(histptr - off);
1227                         x_histncp = x_histp;
1228                 }
1229                 x_nextcmd = -1;
1230         }
1231         editmode = 1;
1232         while (/* CONSTCOND */ 1) {
1233                 x_flush();
1234                 if ((c = x_e_getc()) < 0)
1235                         return (0);
1236
1237                 f = x_curprefix == -1 ? XFUNC_insert :
1238                     x_tab[x_curprefix][c];
1239 #ifndef MKSH_SMALL
1240                 if (f & 0x80) {
1241                         f &= 0x7F;
1242                         if ((i = x_e_getc()) != '~')
1243                                 x_e_ungetc(i);
1244                 }
1245
1246                 /* avoid bind key macro recursion */
1247                 if (macroptr && f == XFUNC_ins_string)
1248                         f = XFUNC_insert;
1249 #endif
1250
1251                 if (!(x_ftab[f].xf_flags & XF_PREFIX) &&
1252                     x_last_command != XFUNC_set_arg) {
1253                         x_arg = 1;
1254                         x_arg_defaulted = true;
1255                 }
1256                 i = c | (x_curprefix << 8);
1257                 x_curprefix = 0;
1258                 switch ((*x_ftab[f].xf_func)(i)) {
1259                 case KSTD:
1260                         if (!(x_ftab[f].xf_flags & XF_PREFIX))
1261                                 x_last_command = f;
1262                         break;
1263                 case KEOL:
1264                         i = xep - xbuf;
1265                         return (i);
1266                 case KINTR:
1267                         /* special case for interrupt */
1268                         trapsig(SIGINT);
1269                         x_mode(false);
1270                         unwind(LSHELL);
1271                 }
1272                 /* ad-hoc hack for fixing the cursor position */
1273                 x_goto(xcp);
1274         }
1275 }
1276
1277 static int
1278 x_insert(int c)
1279 {
1280         static int left, pos, save_arg;
1281         static char str[4];
1282
1283         /*
1284          * Should allow tab and control chars.
1285          */
1286         if (c == 0) {
1287  invmbs:
1288                 left = 0;
1289                 x_e_putc2(7);
1290                 return (KSTD);
1291         }
1292         if (UTFMODE) {
1293                 if (((c & 0xC0) == 0x80) && left) {
1294                         str[pos++] = c;
1295                         if (!--left) {
1296                                 str[pos] = '\0';
1297                                 x_arg = save_arg;
1298                                 while (x_arg--)
1299                                         x_ins(str);
1300                         }
1301                         return (KSTD);
1302                 }
1303                 if (left) {
1304                         if (x_curprefix == -1) {
1305                                 /* flush invalid multibyte */
1306                                 str[pos] = '\0';
1307                                 while (save_arg--)
1308                                         x_ins(str);
1309                         }
1310                 }
1311                 if ((c >= 0xC2) && (c < 0xE0))
1312                         left = 1;
1313                 else if ((c >= 0xE0) && (c < 0xF0))
1314                         left = 2;
1315                 else if (c > 0x7F)
1316                         goto invmbs;
1317                 else
1318                         left = 0;
1319                 if (left) {
1320                         save_arg = x_arg;
1321                         pos = 1;
1322                         str[0] = c;
1323                         return (KSTD);
1324                 }
1325         }
1326         left = 0;
1327         str[0] = c;
1328         str[1] = '\0';
1329         while (x_arg--)
1330                 x_ins(str);
1331         return (KSTD);
1332 }
1333
1334 #ifndef MKSH_SMALL
1335 static int
1336 x_ins_string(int c)
1337 {
1338         macroptr = x_atab[c >> 8][c & 255];
1339         /*
1340          * we no longer need to bother checking if macroptr is
1341          * not NULL but first char is NUL; x_e_getc() does it
1342          */
1343         return (KSTD);
1344 }
1345 #endif
1346
1347 static int
1348 x_do_ins(const char *cp, size_t len)
1349 {
1350         if (xep + len >= xend) {
1351                 x_e_putc2(7);
1352                 return (-1);
1353         }
1354         memmove(xcp + len, xcp, xep - xcp + 1);
1355         memmove(xcp, cp, len);
1356         xcp += len;
1357         xep += len;
1358         x_modified();
1359         return (0);
1360 }
1361
1362 static int
1363 x_ins(const char *s)
1364 {
1365         char *cp = xcp;
1366         int adj = x_adj_done;
1367
1368         if (x_do_ins(s, strlen(s)) < 0)
1369                 return (-1);
1370         /*
1371          * x_zots() may result in a call to x_adjust()
1372          * we want xcp to reflect the new position.
1373          */
1374         xlp_valid = false;
1375         x_lastcp();
1376         x_adj_ok = tobool(xcp >= xlp);
1377         x_zots(cp);
1378         /* has x_adjust() been called? */
1379         if (adj == x_adj_done) {
1380                 /* no */
1381                 cp = xlp;
1382                 while (cp > xcp)
1383                         x_bs3(&cp);
1384         }
1385         if (xlp == xep - 1)
1386                 x_redraw(xx_cols);
1387         x_adj_ok = true;
1388         return (0);
1389 }
1390
1391 static int
1392 x_del_back(int c MKSH_A_UNUSED)
1393 {
1394         ssize_t i = 0;
1395
1396         if (xcp == xbuf) {
1397                 x_e_putc2(7);
1398                 return (KSTD);
1399         }
1400         do {
1401                 x_goto(xcp - 1);
1402         } while ((++i < x_arg) && (xcp != xbuf));
1403         x_delete(i, false);
1404         return (KSTD);
1405 }
1406
1407 static int
1408 x_del_char(int c MKSH_A_UNUSED)
1409 {
1410         char *cp, *cp2;
1411         size_t i = 0;
1412
1413         cp = xcp;
1414         while (i < (size_t)x_arg) {
1415                 utf_ptradjx(cp, cp2);
1416                 if (cp2 > xep)
1417                         break;
1418                 cp = cp2;
1419                 i++;
1420         }
1421
1422         if (!i) {
1423                 x_e_putc2(7);
1424                 return (KSTD);
1425         }
1426         x_delete(i, false);
1427         return (KSTD);
1428 }
1429
1430 /* Delete nc chars to the right of the cursor (including cursor position) */
1431 static void
1432 x_delete(size_t nc, bool push)
1433 {
1434         size_t i, nb, nw;
1435         char *cp;
1436
1437         if (nc == 0)
1438                 return;
1439
1440         nw = 0;
1441         cp = xcp;
1442         for (i = 0; i < nc; ++i) {
1443                 char *cp2;
1444                 int j;
1445
1446                 j = x_size2(cp, &cp2);
1447                 if (cp2 > xep)
1448                         break;
1449                 cp = cp2;
1450                 nw += j;
1451         }
1452         nb = cp - xcp;
1453         /* nc = i; */
1454
1455         if (xmp != NULL && xmp > xcp) {
1456                 if (xcp + nb > xmp)
1457                         xmp = xcp;
1458                 else
1459                         xmp -= nb;
1460         }
1461         /*
1462          * This lets us yank a word we have deleted.
1463          */
1464         if (push)
1465                 x_push(nb);
1466
1467         xep -= nb;
1468         /* Copies the NUL */
1469         memmove(xcp, xcp + nb, xep - xcp + 1);
1470         /* don't redraw */
1471         x_adj_ok = false;
1472         xlp_valid = false;
1473         x_zots(xcp);
1474         /*
1475          * if we are already filling the line,
1476          * there is no need to ' ', '\b'.
1477          * But if we must, make sure we do the minimum.
1478          */
1479         if ((i = xx_cols - 2 - x_col) > 0 || xep - xlp == 0) {
1480                 nw = i = (nw < i) ? nw : i;
1481                 while (i--)
1482                         x_e_putc2(' ');
1483                 if (x_col == xx_cols - 2) {
1484                         x_e_putc2((xep > xlp) ? '>' : (xbp > xbuf) ? '<' : ' ');
1485                         ++nw;
1486                 }
1487                 while (nw--)
1488                         x_e_putc2('\b');
1489         }
1490         /*x_goto(xcp);*/
1491         x_adj_ok = true;
1492         xlp_valid = false;
1493         cp = x_lastcp();
1494         while (cp > xcp)
1495                 x_bs3(&cp);
1496
1497         x_modified();
1498         return;
1499 }
1500
1501 static int
1502 x_del_bword(int c MKSH_A_UNUSED)
1503 {
1504         x_delete(x_bword(), true);
1505         return (KSTD);
1506 }
1507
1508 static int
1509 x_mv_bword(int c MKSH_A_UNUSED)
1510 {
1511         x_bword();
1512         return (KSTD);
1513 }
1514
1515 static int
1516 x_mv_fword(int c MKSH_A_UNUSED)
1517 {
1518         x_fword(true);
1519         return (KSTD);
1520 }
1521
1522 static int
1523 x_del_fword(int c MKSH_A_UNUSED)
1524 {
1525         x_delete(x_fword(false), true);
1526         return (KSTD);
1527 }
1528
1529 static size_t
1530 x_bword(void)
1531 {
1532         size_t nb = 0;
1533         char *cp = xcp;
1534
1535         if (cp == xbuf) {
1536                 x_e_putc2(7);
1537                 return (0);
1538         }
1539         while (x_arg--) {
1540                 while (cp != xbuf && is_mfs(cp[-1])) {
1541                         cp--;
1542                         nb++;
1543                 }
1544                 while (cp != xbuf && !is_mfs(cp[-1])) {
1545                         cp--;
1546                         nb++;
1547                 }
1548         }
1549         x_goto(cp);
1550         return (x_nb2nc(nb));
1551 }
1552
1553 static size_t
1554 x_fword(bool move)
1555 {
1556         size_t nc;
1557         char *cp = xcp;
1558
1559         if (cp == xep) {
1560                 x_e_putc2(7);
1561                 return (0);
1562         }
1563         while (x_arg--) {
1564                 while (cp != xep && is_mfs(*cp))
1565                         cp++;
1566                 while (cp != xep && !is_mfs(*cp))
1567                         cp++;
1568         }
1569         nc = x_nb2nc(cp - xcp);
1570         if (move)
1571                 x_goto(cp);
1572         return (nc);
1573 }
1574
1575 static void
1576 x_goto(char *cp)
1577 {
1578         cp = cp >= xep ? xep : x_bs0(cp, xbuf);
1579         if (cp < xbp || cp >= utf_skipcols(xbp, x_displen)) {
1580                 /* we are heading off screen */
1581                 xcp = cp;
1582                 x_adjust();
1583         } else if (cp < xcp) {
1584                 /* move back */
1585                 while (cp < xcp)
1586                         x_bs3(&xcp);
1587         } else if (cp > xcp) {
1588                 /* move forward */
1589                 while (cp > xcp)
1590                         x_zotc3(&xcp);
1591         }
1592 }
1593
1594 static char *
1595 x_bs0(char *cp, char *lower_bound)
1596 {
1597         if (UTFMODE)
1598                 while ((!lower_bound || (cp > lower_bound)) &&
1599                     ((*(unsigned char *)cp & 0xC0) == 0x80))
1600                         --cp;
1601         return (cp);
1602 }
1603
1604 static void
1605 x_bs3(char **p)
1606 {
1607         int i;
1608
1609         *p = x_bs0((*p) - 1, NULL);
1610         i = x_size2(*p, NULL);
1611         while (i--)
1612                 x_e_putc2('\b');
1613 }
1614
1615 static int
1616 x_size_str(char *cp)
1617 {
1618         int size = 0;
1619         while (*cp)
1620                 size += x_size2(cp, &cp);
1621         return (size);
1622 }
1623
1624 static int
1625 x_size2(char *cp, char **dcp)
1626 {
1627         uint8_t c = *(unsigned char *)cp;
1628
1629         if (UTFMODE && (c > 0x7F))
1630                 return (utf_widthadj(cp, (const char **)dcp));
1631         if (dcp)
1632                 *dcp = cp + 1;
1633         if (c == '\t')
1634                 /* Kludge, tabs are always four spaces. */
1635                 return (4);
1636         if (ISCTRL(c) && /* but not C1 */ c < 0x80)
1637                 /* control unsigned char */
1638                 return (2);
1639         return (1);
1640 }
1641
1642 static void
1643 x_zots(char *str)
1644 {
1645         int adj = x_adj_done;
1646
1647         x_lastcp();
1648         while (*str && str < xlp && x_col < xx_cols && adj == x_adj_done)
1649                 x_zotc3(&str);
1650 }
1651
1652 static void
1653 x_zotc3(char **cp)
1654 {
1655         unsigned char c = **(unsigned char **)cp;
1656
1657         if (c == '\t') {
1658                 /* Kludge, tabs are always four spaces. */
1659                 x_e_puts("    ");
1660                 (*cp)++;
1661         } else if (ISCTRL(c) && /* but not C1 */ c < 0x80) {
1662                 x_e_putc2('^');
1663                 x_e_putc2(UNCTRL(c));
1664                 (*cp)++;
1665         } else
1666                 x_e_putc3((const char **)cp);
1667 }
1668
1669 static int
1670 x_mv_back(int c MKSH_A_UNUSED)
1671 {
1672         if (xcp == xbuf) {
1673                 x_e_putc2(7);
1674                 return (KSTD);
1675         }
1676         while (x_arg--) {
1677                 x_goto(xcp - 1);
1678                 if (xcp == xbuf)
1679                         break;
1680         }
1681         return (KSTD);
1682 }
1683
1684 static int
1685 x_mv_forw(int c MKSH_A_UNUSED)
1686 {
1687         char *cp = xcp, *cp2;
1688
1689         if (xcp == xep) {
1690                 x_e_putc2(7);
1691                 return (KSTD);
1692         }
1693         while (x_arg--) {
1694                 utf_ptradjx(cp, cp2);
1695                 if (cp2 > xep)
1696                         break;
1697                 cp = cp2;
1698         }
1699         x_goto(cp);
1700         return (KSTD);
1701 }
1702
1703 static int
1704 x_search_char_forw(int c MKSH_A_UNUSED)
1705 {
1706         char *cp = xcp;
1707         char tmp[4];
1708
1709         *xep = '\0';
1710         if (x_e_getmbc(tmp) < 0) {
1711                 x_e_putc2(7);
1712                 return (KSTD);
1713         }
1714         while (x_arg--) {
1715                 if ((cp = (cp == xep) ? NULL : strstr(cp + 1, tmp)) == NULL &&
1716                     (cp = strstr(xbuf, tmp)) == NULL) {
1717                         x_e_putc2(7);
1718                         return (KSTD);
1719                 }
1720         }
1721         x_goto(cp);
1722         return (KSTD);
1723 }
1724
1725 static int
1726 x_search_char_back(int c MKSH_A_UNUSED)
1727 {
1728         char *cp = xcp, *p, tmp[4];
1729         bool b;
1730
1731         if (x_e_getmbc(tmp) < 0) {
1732                 x_e_putc2(7);
1733                 return (KSTD);
1734         }
1735         for (; x_arg--; cp = p)
1736                 for (p = cp; ; ) {
1737                         if (p-- == xbuf)
1738                                 p = xep;
1739                         if (p == cp) {
1740                                 x_e_putc2(7);
1741                                 return (KSTD);
1742                         }
1743                         if ((tmp[1] && ((p+1) > xep)) ||
1744                             (tmp[2] && ((p+2) > xep)))
1745                                 continue;
1746                         b = true;
1747                         if (*p != tmp[0])
1748                                 b = false;
1749                         if (b && tmp[1] && p[1] != tmp[1])
1750                                 b = false;
1751                         if (b && tmp[2] && p[2] != tmp[2])
1752                                 b = false;
1753                         if (b)
1754                                 break;
1755                 }
1756         x_goto(cp);
1757         return (KSTD);
1758 }
1759
1760 static int
1761 x_newline(int c MKSH_A_UNUSED)
1762 {
1763         x_e_putc2('\r');
1764         x_e_putc2('\n');
1765         x_flush();
1766         *xep++ = '\n';
1767         return (KEOL);
1768 }
1769
1770 static int
1771 x_end_of_text(int c MKSH_A_UNUSED)
1772 {
1773         char tmp = edchars.eof;
1774         char *cp = &tmp;
1775
1776         x_zotc3(&cp);
1777         x_putc('\r');
1778         x_putc('\n');
1779         x_flush();
1780         return (KEOL);
1781 }
1782
1783 static int
1784 x_beg_hist(int c MKSH_A_UNUSED)
1785 {
1786         x_load_hist(history);
1787         return (KSTD);
1788 }
1789
1790 static int
1791 x_end_hist(int c MKSH_A_UNUSED)
1792 {
1793         x_load_hist(histptr);
1794         return (KSTD);
1795 }
1796
1797 static int
1798 x_prev_com(int c MKSH_A_UNUSED)
1799 {
1800         x_load_hist(x_histp - x_arg);
1801         return (KSTD);
1802 }
1803
1804 static int
1805 x_next_com(int c MKSH_A_UNUSED)
1806 {
1807         x_load_hist(x_histp + x_arg);
1808         return (KSTD);
1809 }
1810
1811 /*
1812  * Goto a particular history number obtained from argument.
1813  * If no argument is given history 1 is probably not what you
1814  * want so we'll simply go to the oldest one.
1815  */
1816 static int
1817 x_goto_hist(int c MKSH_A_UNUSED)
1818 {
1819         if (x_arg_defaulted)
1820                 x_load_hist(history);
1821         else
1822                 x_load_hist(histptr + x_arg - source->line);
1823         return (KSTD);
1824 }
1825
1826 static void
1827 x_load_hist(char **hp)
1828 {
1829         int oldsize;
1830         char *sp = NULL;
1831
1832         if (hp == histptr + 1) {
1833                 sp = holdbufp;
1834                 modified = 0;
1835         } else if (hp < history || hp > histptr) {
1836                 x_e_putc2(7);
1837                 return;
1838         }
1839         if (sp == NULL)
1840                 sp = *hp;
1841         x_histp = hp;
1842         oldsize = x_size_str(xbuf);
1843         if (modified)
1844                 strlcpy(holdbufp, xbuf, LINE);
1845         strlcpy(xbuf, sp, xend - xbuf);
1846         xbp = xbuf;
1847         xep = xcp = xbuf + strlen(xbuf);
1848         xlp_valid = false;
1849         if (xep <= x_lastcp()) {
1850                 x_redraw(oldsize);
1851         }
1852         x_goto(xep);
1853         modified = 0;
1854 }
1855
1856 static int
1857 x_nl_next_com(int c MKSH_A_UNUSED)
1858 {
1859         if (!x_histncp || (x_histp != x_histncp && x_histp != histptr + 1))
1860                 /* fresh start of ^O */
1861                 x_histncp = x_histp;
1862         x_nextcmd = source->line - (histptr - x_histncp) + 1;
1863         return (x_newline('\n'));
1864 }
1865
1866 static int
1867 x_eot_del(int c)
1868 {
1869         if (xep == xbuf && x_arg_defaulted)
1870                 return (x_end_of_text(c));
1871         else
1872                 return (x_del_char(c));
1873 }
1874
1875 /* reverse incremental history search */
1876 static int
1877 x_search_hist(int c)
1878 {
1879         int offset = -1;        /* offset of match in xbuf, else -1 */
1880         char pat[80 + 1];       /* pattern buffer */
1881         char *p = pat;
1882         unsigned char f;
1883
1884         *p = '\0';
1885         while (/* CONSTCOND */ 1) {
1886                 if (offset < 0) {
1887                         x_e_puts("\nI-search: ");
1888                         x_e_puts(pat);
1889                 }
1890                 x_flush();
1891                 if ((c = x_e_getc()) < 0)
1892                         return (KSTD);
1893                 f = x_tab[0][c];
1894                 if (c == CTRL('[')) {
1895                         if ((f & 0x7F) == XFUNC_meta1) {
1896                                 if ((c = x_e_getc()) < 0)
1897                                         return (KSTD);
1898                                 f = x_tab[1][c] & 0x7F;
1899                                 if (f == XFUNC_meta1 || f == XFUNC_meta2)
1900                                         x_meta1(CTRL('['));
1901                                 x_e_ungetc(c);
1902                         }
1903                         break;
1904                 }
1905 #ifndef MKSH_SMALL
1906                 if (f & 0x80) {
1907                         f &= 0x7F;
1908                         if ((c = x_e_getc()) != '~')
1909                                 x_e_ungetc(c);
1910                 }
1911 #endif
1912                 if (f == XFUNC_search_hist)
1913                         offset = x_search(pat, 0, offset);
1914                 else if (f == XFUNC_del_back) {
1915                         if (p == pat) {
1916                                 offset = -1;
1917                                 break;
1918                         }
1919                         if (p > pat)
1920                                 *--p = '\0';
1921                         if (p == pat)
1922                                 offset = -1;
1923                         else
1924                                 offset = x_search(pat, 1, offset);
1925                         continue;
1926                 } else if (f == XFUNC_insert) {
1927                         /* add char to pattern */
1928                         /* overflow check... */
1929                         if ((size_t)(p - pat) >= sizeof(pat) - 1) {
1930                                 x_e_putc2(7);
1931                                 continue;
1932                         }
1933                         *p++ = c, *p = '\0';
1934                         if (offset >= 0) {
1935                                 /* already have partial match */
1936                                 offset = x_match(xbuf, pat);
1937                                 if (offset >= 0) {
1938                                         x_goto(xbuf + offset + (p - pat) -
1939                                             (*pat == '^'));
1940                                         continue;
1941                                 }
1942                         }
1943                         offset = x_search(pat, 0, offset);
1944                 } else if (f == XFUNC_abort) {
1945                         if (offset >= 0)
1946                                 x_load_hist(histptr + 1);
1947                         break;
1948                 } else {
1949                         /* other command */
1950                         x_e_ungetc(c);
1951                         break;
1952                 }
1953         }
1954         if (offset < 0)
1955                 x_redraw(-1);
1956         return (KSTD);
1957 }
1958
1959 /* search backward from current line */
1960 static int
1961 x_search(char *pat, int sameline, int offset)
1962 {
1963         char **hp;
1964         int i;
1965
1966         for (hp = x_histp - (sameline ? 0 : 1); hp >= history; --hp) {
1967                 i = x_match(*hp, pat);
1968                 if (i >= 0) {
1969                         if (offset < 0)
1970                                 x_e_putc2('\n');
1971                         x_load_hist(hp);
1972                         x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
1973                         return (i);
1974                 }
1975         }
1976         x_e_putc2(7);
1977         x_histp = histptr;
1978         return (-1);
1979 }
1980
1981 #ifndef MKSH_SMALL
1982 /* anchored search up from current line */
1983 static int
1984 x_search_hist_up(int c MKSH_A_UNUSED)
1985 {
1986         return (x_search_dir(-1));
1987 }
1988
1989 /* anchored search down from current line */
1990 static int
1991 x_search_hist_dn(int c MKSH_A_UNUSED)
1992 {
1993         return (x_search_dir(1));
1994 }
1995
1996 /* anchored search in the indicated direction */
1997 static int
1998 x_search_dir(int search_dir /* should've been bool */)
1999 {
2000         char **hp = x_histp + search_dir;
2001         size_t curs = xcp - xbuf;
2002
2003         while (histptr >= hp && hp >= history) {
2004                 if (strncmp(xbuf, *hp, curs) == 0) {
2005                         x_load_hist(hp);
2006                         x_goto(xbuf + curs);
2007                         break;
2008                 }
2009                 hp += search_dir;
2010         }
2011         return (KSTD);
2012 }
2013 #endif
2014
2015 /* return position of first match of pattern in string, else -1 */
2016 static int
2017 x_match(char *str, char *pat)
2018 {
2019         if (*pat == '^') {
2020                 return ((strncmp(str, pat + 1, strlen(pat + 1)) == 0) ? 0 : -1);
2021         } else {
2022                 char *q = strstr(str, pat);
2023                 return ((q == NULL) ? -1 : q - str);
2024         }
2025 }
2026
2027 static int
2028 x_del_line(int c MKSH_A_UNUSED)
2029 {
2030         int i, j;
2031
2032         *xep = 0;
2033         i = xep - xbuf;
2034         j = x_size_str(xbuf);
2035         xcp = xbuf;
2036         x_push(i);
2037         xlp = xbp = xep = xbuf;
2038         xlp_valid = true;
2039         *xcp = 0;
2040         xmp = NULL;
2041         x_redraw(j);
2042         x_modified();
2043         return (KSTD);
2044 }
2045
2046 static int
2047 x_mv_end(int c MKSH_A_UNUSED)
2048 {
2049         x_goto(xep);
2050         return (KSTD);
2051 }
2052
2053 static int
2054 x_mv_begin(int c MKSH_A_UNUSED)
2055 {
2056         x_goto(xbuf);
2057         return (KSTD);
2058 }
2059
2060 static int
2061 x_draw_line(int c MKSH_A_UNUSED)
2062 {
2063         x_redraw(-1);
2064         return (KSTD);
2065 }
2066
2067 static int
2068 x_e_rebuildline(const char *clrstr)
2069 {
2070         shf_puts(clrstr, shl_out);
2071         x_adjust();
2072         return (KSTD);
2073 }
2074
2075 static int
2076 x_cls(int c MKSH_A_UNUSED)
2077 {
2078         return (x_e_rebuildline(MKSH_CLS_STRING));
2079 }
2080
2081 /*
2082  * Redraw (part of) the line. If limit is < 0, the everything is redrawn
2083  * on a NEW line, otherwise limit is the screen column up to which needs
2084  * redrawing.
2085  */
2086 static void
2087 x_redraw(int limit)
2088 {
2089         int i, j;
2090         char *cp;
2091
2092         x_adj_ok = false;
2093         if (limit == -1)
2094                 x_e_putc2('\n');
2095         else
2096                 x_e_putc2('\r');
2097         x_flush();
2098         if (xbp == xbuf) {
2099                 if (prompt_trunc != -1)
2100                         pprompt(prompt, prompt_trunc);
2101                 x_col = pwidth;
2102         }
2103         x_displen = xx_cols - 2 - x_col;
2104         xlp_valid = false;
2105         x_zots(xbp);
2106         if (xbp != xbuf || xep > xlp)
2107                 limit = xx_cols;
2108         if (limit >= 0) {
2109                 if (xep > xlp)
2110                         /* we fill the line */
2111                         i = 0;
2112                 else {
2113                         char *cpl = xbp;
2114
2115                         i = limit;
2116                         while (cpl < xlp)
2117                                 i -= x_size2(cpl, &cpl);
2118                 }
2119
2120                 j = 0;
2121                 while ((j < i) || (x_col < (xx_cols - 2))) {
2122                         if (!(x_col < (xx_cols - 2)))
2123                                 break;
2124                         x_e_putc2(' ');
2125                         j++;
2126                 }
2127                 i = ' ';
2128                 if (xep > xlp) {
2129                         /* more off screen */
2130                         if (xbp > xbuf)
2131                                 i = '*';
2132                         else
2133                                 i = '>';
2134                 } else if (xbp > xbuf)
2135                         i = '<';
2136                 x_e_putc2(i);
2137                 j++;
2138                 while (j--)
2139                         x_e_putc2('\b');
2140         }
2141         cp = xlp;
2142         while (cp > xcp)
2143                 x_bs3(&cp);
2144         x_adj_ok = true;
2145         return;
2146 }
2147
2148 static int
2149 x_transpose(int c MKSH_A_UNUSED)
2150 {
2151         unsigned int tmpa, tmpb;
2152
2153         /*-
2154          * What transpose is meant to do seems to be up for debate. This
2155          * is a general summary of the options; the text is abcd with the
2156          * upper case character or underscore indicating the cursor position:
2157          *      Who                     Before  After   Before  After
2158          *      AT&T ksh in emacs mode: abCd    abdC    abcd_   (bell)
2159          *      AT&T ksh in gmacs mode: abCd    baCd    abcd_   abdc_
2160          *      gnu emacs:              abCd    acbD    abcd_   abdc_
2161          * Pdksh currently goes with GNU behavior since I believe this is the
2162          * most common version of emacs, unless in gmacs mode, in which case
2163          * it does the AT&T ksh gmacs mode.
2164          * This should really be broken up into 3 functions so users can bind
2165          * to the one they want.
2166          */
2167         if (xcp == xbuf) {
2168                 x_e_putc2(7);
2169                 return (KSTD);
2170         } else if (xcp == xep || Flag(FGMACS)) {
2171                 if (xcp - xbuf == 1) {
2172                         x_e_putc2(7);
2173                         return (KSTD);
2174                 }
2175                 /*
2176                  * Gosling/Unipress emacs style: Swap two characters before
2177                  * the cursor, do not change cursor position
2178                  */
2179                 x_bs3(&xcp);
2180                 if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
2181                         x_e_putc2(7);
2182                         return (KSTD);
2183                 }
2184                 x_bs3(&xcp);
2185                 if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
2186                         x_e_putc2(7);
2187                         return (KSTD);
2188                 }
2189                 utf_wctomb(xcp, tmpa);
2190                 x_zotc3(&xcp);
2191                 utf_wctomb(xcp, tmpb);
2192                 x_zotc3(&xcp);
2193         } else {
2194                 /*
2195                  * GNU emacs style: Swap the characters before and under the
2196                  * cursor, move cursor position along one.
2197                  */
2198                 if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
2199                         x_e_putc2(7);
2200                         return (KSTD);
2201                 }
2202                 x_bs3(&xcp);
2203                 if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
2204                         x_e_putc2(7);
2205                         return (KSTD);
2206                 }
2207                 utf_wctomb(xcp, tmpa);
2208                 x_zotc3(&xcp);
2209                 utf_wctomb(xcp, tmpb);
2210                 x_zotc3(&xcp);
2211         }
2212         x_modified();
2213         return (KSTD);
2214 }
2215
2216 static int
2217 x_literal(int c MKSH_A_UNUSED)
2218 {
2219         x_curprefix = -1;
2220         return (KSTD);
2221 }
2222
2223 static int
2224 x_meta1(int c MKSH_A_UNUSED)
2225 {
2226         x_curprefix = 1;
2227         return (KSTD);
2228 }
2229
2230 static int
2231 x_meta2(int c MKSH_A_UNUSED)
2232 {
2233         x_curprefix = 2;
2234         return (KSTD);
2235 }
2236
2237 static int
2238 x_meta3(int c MKSH_A_UNUSED)
2239 {
2240         x_curprefix = 3;
2241         return (KSTD);
2242 }
2243
2244 static int
2245 x_kill(int c MKSH_A_UNUSED)
2246 {
2247         size_t col = xcp - xbuf;
2248         size_t lastcol = xep - xbuf;
2249         size_t ndel, narg;
2250
2251         if (x_arg_defaulted || (narg = x_arg) > lastcol)
2252                 narg = lastcol;
2253         if (narg < col) {
2254                 x_goto(xbuf + narg);
2255                 ndel = col - narg;
2256         } else
2257                 ndel = narg - col;
2258         x_delete(x_nb2nc(ndel), true);
2259         return (KSTD);
2260 }
2261
2262 static void
2263 x_push(int nchars)
2264 {
2265         afree(killstack[killsp], AEDIT);
2266         strndupx(killstack[killsp], xcp, nchars, AEDIT);
2267         killsp = (killsp + 1) % KILLSIZE;
2268 }
2269
2270 static int
2271 x_yank(int c MKSH_A_UNUSED)
2272 {
2273         if (killsp == 0)
2274                 killtp = KILLSIZE;
2275         else
2276                 killtp = killsp;
2277         killtp--;
2278         if (killstack[killtp] == 0) {
2279                 x_e_puts("\nnothing to yank");
2280                 x_redraw(-1);
2281                 return (KSTD);
2282         }
2283         xmp = xcp;
2284         x_ins(killstack[killtp]);
2285         return (KSTD);
2286 }
2287
2288 static int
2289 x_meta_yank(int c MKSH_A_UNUSED)
2290 {
2291         size_t len;
2292
2293         if ((x_last_command != XFUNC_yank && x_last_command != XFUNC_meta_yank) ||
2294             killstack[killtp] == 0) {
2295                 killtp = killsp;
2296                 x_e_puts("\nyank something first");
2297                 x_redraw(-1);
2298                 return (KSTD);
2299         }
2300         len = strlen(killstack[killtp]);
2301         x_goto(xcp - len);
2302         x_delete(x_nb2nc(len), false);
2303         do {
2304                 if (killtp == 0)
2305                         killtp = KILLSIZE - 1;
2306                 else
2307                         killtp--;
2308         } while (killstack[killtp] == 0);
2309         x_ins(killstack[killtp]);
2310         return (KSTD);
2311 }
2312
2313 static int
2314 x_abort(int c MKSH_A_UNUSED)
2315 {
2316         /* x_zotc(c); */
2317         xlp = xep = xcp = xbp = xbuf;
2318         xlp_valid = true;
2319         *xcp = 0;
2320         x_modified();
2321         return (KINTR);
2322 }
2323
2324 static int
2325 x_error(int c MKSH_A_UNUSED)
2326 {
2327         x_e_putc2(7);
2328         return (KSTD);
2329 }
2330
2331 #ifndef MKSH_SMALL
2332 /* special VT100 style key sequence hack */
2333 static int
2334 x_vt_hack(int c)
2335 {
2336         /* we only support PF2-'1' for now */
2337         if (c != (2 << 8 | '1'))
2338                 return (x_error(c));
2339
2340         /* what's the next character? */
2341         switch ((c = x_e_getc())) {
2342         case '~':
2343                 x_arg = 1;
2344                 x_arg_defaulted = true;
2345                 return (x_mv_begin(0));
2346         case ';':
2347                 /* "interesting" sequence detected */
2348                 break;
2349         default:
2350                 goto unwind_err;
2351         }
2352
2353         /* XXX x_e_ungetc is one-octet only */
2354         if ((c = x_e_getc()) != '5' && c != '3')
2355                 goto unwind_err;
2356
2357         /*-
2358          * At this point, we have read the following octets so far:
2359          * - ESC+[ or ESC+O or Ctrl-X (Prefix 2)
2360          * - 1 (vt_hack)
2361          * - ;
2362          * - 5 (Ctrl key combiner) or 3 (Alt key combiner)
2363          * We can now accept one more octet designating the key.
2364          */
2365
2366         switch ((c = x_e_getc())) {
2367         case 'C':
2368                 return (x_mv_fword(c));
2369         case 'D':
2370                 return (x_mv_bword(c));
2371         }
2372
2373  unwind_err:
2374         x_e_ungetc(c);
2375         return (x_error(c));
2376 }
2377 #endif
2378
2379 static char *
2380 x_mapin(const char *cp, Area *ap)
2381 {
2382         char *news, *op;
2383
2384         strdupx(news, cp, ap);
2385         op = news;
2386         while (*cp) {
2387                 /* XXX -- should handle \^ escape? */
2388                 if (*cp == '^') {
2389                         cp++;
2390                         /*XXX or ^^ escape? this is ugly. */
2391                         if (*cp >= '?')
2392                                 /* includes '?'; ASCII */
2393                                 *op++ = CTRL(*cp);
2394                         else {
2395                                 *op++ = '^';
2396                                 cp--;
2397                         }
2398                 } else
2399                         *op++ = *cp;
2400                 cp++;
2401         }
2402         *op = '\0';
2403
2404         return (news);
2405 }
2406
2407 static void
2408 x_mapout2(int c, char **buf)
2409 {
2410         char *p = *buf;
2411
2412         if (ISCTRL(c)) {
2413                 *p++ = '^';
2414                 *p++ = UNCTRL(c);
2415         } else
2416                 *p++ = c;
2417         *p = 0;
2418         *buf = p;
2419 }
2420
2421 static char *
2422 x_mapout(int c)
2423 {
2424         static char buf[8];
2425         char *bp = buf;
2426
2427         x_mapout2(c, &bp);
2428         return (buf);
2429 }
2430
2431 static void
2432 x_print(int prefix, int key)
2433 {
2434         int f = x_tab[prefix][key];
2435
2436         if (prefix)
2437                 /* prefix == 1 || prefix == 2 */
2438                 shf_puts(x_mapout(prefix == 1 ? CTRL('[') :
2439                     prefix == 2 ? CTRL('X') : 0), shl_stdout);
2440 #ifdef MKSH_SMALL
2441         shprintf("%s = ", x_mapout(key));
2442 #else
2443         shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : "");
2444         if (XFUNC_VALUE(f) != XFUNC_ins_string)
2445 #endif
2446                 shprintf("%s\n", x_ftab[XFUNC_VALUE(f)].xf_name);
2447 #ifndef MKSH_SMALL
2448         else
2449                 shprintf("'%s'\n", x_atab[prefix][key]);
2450 #endif
2451 }
2452
2453 int
2454 x_bind(const char *a1, const char *a2,
2455 #ifndef MKSH_SMALL
2456     /* bind -m */
2457     bool macro,
2458 #endif
2459     /* bind -l */
2460     bool list)
2461 {
2462         unsigned char f;
2463         int prefix, key;
2464         char *m1, *m2;
2465 #ifndef MKSH_SMALL
2466         char *sp = NULL;
2467         bool hastilde;
2468 #endif
2469
2470         if (x_tab == NULL) {
2471                 bi_errorf("can't bind, not a tty");
2472                 return (1);
2473         }
2474         /* List function names */
2475         if (list) {
2476                 for (f = 0; f < NELEM(x_ftab); f++)
2477                         if (!(x_ftab[f].xf_flags & XF_NOBIND))
2478                                 shprintf("%s\n", x_ftab[f].xf_name);
2479                 return (0);
2480         }
2481         if (a1 == NULL) {
2482                 for (prefix = 0; prefix < X_NTABS; prefix++)
2483                         for (key = 0; key < X_TABSZ; key++) {
2484                                 f = XFUNC_VALUE(x_tab[prefix][key]);
2485                                 if (f == XFUNC_insert || f == XFUNC_error
2486 #ifndef MKSH_SMALL
2487                                     || (macro && f != XFUNC_ins_string)
2488 #endif
2489                                     )
2490                                         continue;
2491                                 x_print(prefix, key);
2492                         }
2493                 return (0);
2494         }
2495         m2 = m1 = x_mapin(a1, ATEMP);
2496         prefix = 0;
2497         for (;; m1++) {
2498                 key = (unsigned char)*m1;
2499                 f = XFUNC_VALUE(x_tab[prefix][key]);
2500                 if (f == XFUNC_meta1)
2501                         prefix = 1;
2502                 else if (f == XFUNC_meta2)
2503                         prefix = 2;
2504                 else if (f == XFUNC_meta3)
2505                         prefix = 3;
2506                 else
2507                         break;
2508         }
2509         if (*++m1
2510 #ifndef MKSH_SMALL
2511             && ((*m1 != '~') || *(m1 + 1))
2512 #endif
2513             ) {
2514                 char msg[256];
2515                 const char *c = a1;
2516                 m1 = msg;
2517                 while (*c && (size_t)(m1 - msg) < sizeof(msg) - 3)
2518                         x_mapout2(*c++, &m1);
2519                 bi_errorf("%s: %s", "too long key sequence", msg);
2520                 return (1);
2521         }
2522 #ifndef MKSH_SMALL
2523         hastilde = tobool(*m1);
2524 #endif
2525         afree(m2, ATEMP);
2526
2527         if (a2 == NULL) {
2528                 x_print(prefix, key);
2529                 return (0);
2530         }
2531         if (*a2 == 0) {
2532                 f = XFUNC_insert;
2533 #ifndef MKSH_SMALL
2534         } else if (macro) {
2535                 f = XFUNC_ins_string;
2536                 sp = x_mapin(a2, AEDIT);
2537 #endif
2538         } else {
2539                 for (f = 0; f < NELEM(x_ftab); f++)
2540                         if (!strcmp(x_ftab[f].xf_name, a2))
2541                                 break;
2542                 if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) {
2543                         bi_errorf("%s: %s %s", a2, "no such", Tfunction);
2544                         return (1);
2545                 }
2546         }
2547
2548 #ifndef MKSH_SMALL
2549         if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string &&
2550             x_atab[prefix][key])
2551                 afree(x_atab[prefix][key], AEDIT);
2552 #endif
2553         x_tab[prefix][key] = f
2554 #ifndef MKSH_SMALL
2555             | (hastilde ? 0x80 : 0)
2556 #endif
2557             ;
2558 #ifndef MKSH_SMALL
2559         x_atab[prefix][key] = sp;
2560 #endif
2561
2562         /* Track what the user has bound so x_mode(true) won't toast things */
2563         if (f == XFUNC_insert)
2564                 x_bound[(prefix * X_TABSZ + key) / 8] &=
2565                     ~(1 << ((prefix * X_TABSZ + key) % 8));
2566         else
2567                 x_bound[(prefix * X_TABSZ + key) / 8] |=
2568                     (1 << ((prefix * X_TABSZ + key) % 8));
2569
2570         return (0);
2571 }
2572
2573 static void
2574 bind_if_not_bound(int p, int k, int func)
2575 {
2576         int t;
2577
2578         /*
2579          * Has user already bound this key?
2580          * If so, do not override it.
2581          */
2582         t = p * X_TABSZ + k;
2583         if (x_bound[t >> 3] & (1 << (t & 7)))
2584                 return;
2585
2586         x_tab[p][k] = func;
2587 }
2588
2589 static int
2590 x_set_mark(int c MKSH_A_UNUSED)
2591 {
2592         xmp = xcp;
2593         return (KSTD);
2594 }
2595
2596 static int
2597 x_kill_region(int c MKSH_A_UNUSED)
2598 {
2599         size_t rsize;
2600         char *xr;
2601
2602         if (xmp == NULL) {
2603                 x_e_putc2(7);
2604                 return (KSTD);
2605         }
2606         if (xmp > xcp) {
2607                 rsize = xmp - xcp;
2608                 xr = xcp;
2609         } else {
2610                 rsize = xcp - xmp;
2611                 xr = xmp;
2612         }
2613         x_goto(xr);
2614         x_delete(x_nb2nc(rsize), true);
2615         xmp = xr;
2616         return (KSTD);
2617 }
2618
2619 static int
2620 x_xchg_point_mark(int c MKSH_A_UNUSED)
2621 {
2622         char *tmp;
2623
2624         if (xmp == NULL) {
2625                 x_e_putc2(7);
2626                 return (KSTD);
2627         }
2628         tmp = xmp;
2629         xmp = xcp;
2630         x_goto(tmp);
2631         return (KSTD);
2632 }
2633
2634 static int
2635 x_noop(int c MKSH_A_UNUSED)
2636 {
2637         return (KSTD);
2638 }
2639
2640 /*
2641  *      File/command name completion routines
2642  */
2643 static int
2644 x_comp_comm(int c MKSH_A_UNUSED)
2645 {
2646         do_complete(XCF_COMMAND, CT_COMPLETE);
2647         return (KSTD);
2648 }
2649
2650 static int
2651 x_list_comm(int c MKSH_A_UNUSED)
2652 {
2653         do_complete(XCF_COMMAND, CT_LIST);
2654         return (KSTD);
2655 }
2656
2657 static int
2658 x_complete(int c MKSH_A_UNUSED)
2659 {
2660         do_complete(XCF_COMMAND_FILE, CT_COMPLETE);
2661         return (KSTD);
2662 }
2663
2664 static int
2665 x_enumerate(int c MKSH_A_UNUSED)
2666 {
2667         do_complete(XCF_COMMAND_FILE, CT_LIST);
2668         return (KSTD);
2669 }
2670
2671 static int
2672 x_comp_file(int c MKSH_A_UNUSED)
2673 {
2674         do_complete(XCF_FILE, CT_COMPLETE);
2675         return (KSTD);
2676 }
2677
2678 static int
2679 x_list_file(int c MKSH_A_UNUSED)
2680 {
2681         do_complete(XCF_FILE, CT_LIST);
2682         return (KSTD);
2683 }
2684
2685 static int
2686 x_comp_list(int c MKSH_A_UNUSED)
2687 {
2688         do_complete(XCF_COMMAND_FILE, CT_COMPLIST);
2689         return (KSTD);
2690 }
2691
2692 static int
2693 x_expand(int c MKSH_A_UNUSED)
2694 {
2695         char **words;
2696         int start, end, nwords, i;
2697
2698         i = XCF_FILE;
2699         nwords = x_cf_glob(&i, xbuf, xep - xbuf, xcp - xbuf,
2700             &start, &end, &words);
2701
2702         if (nwords == 0) {
2703                 x_e_putc2(7);
2704                 return (KSTD);
2705         }
2706         x_goto(xbuf + start);
2707         x_delete(x_nb2nc(end - start), false);
2708
2709         i = 0;
2710         while (i < nwords) {
2711                 if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
2712                     (++i < nwords && x_ins(" ") < 0)) {
2713                         x_e_putc2(7);
2714                         return (KSTD);
2715                 }
2716         }
2717         x_adjust();
2718
2719         return (KSTD);
2720 }
2721
2722 static void
2723 do_complete(
2724     /* XCF_{COMMAND,FILE,COMMAND_FILE} */
2725     int flags,
2726     /* 0 for list, 1 for complete and 2 for complete-list */
2727     Comp_type type)
2728 {
2729         char **words;
2730         int start, end, nlen, olen, nwords;
2731         bool completed;
2732
2733         nwords = x_cf_glob(&flags, xbuf, xep - xbuf, xcp - xbuf,
2734             &start, &end, &words);
2735         /* no match */
2736         if (nwords == 0) {
2737                 x_e_putc2(7);
2738                 return;
2739         }
2740         if (type == CT_LIST) {
2741                 x_print_expansions(nwords, words,
2742                     tobool(flags & XCF_IS_COMMAND));
2743                 x_redraw(0);
2744                 x_free_words(nwords, words);
2745                 return;
2746         }
2747         olen = end - start;
2748         nlen = x_longest_prefix(nwords, words);
2749         if (nwords == 1) {
2750                 /*
2751                  * always complete single matches;
2752                  * any expansion of parameter substitution
2753                  * is always at most one result, too
2754                  */
2755                 completed = true;
2756         } else {
2757                 char *unescaped;
2758
2759                 /* make a copy of the original string part */
2760                 strndupx(unescaped, xbuf + start, olen, ATEMP);
2761
2762                 /* expand any tilde and unescape the string for comparison */
2763                 unescaped = x_glob_hlp_tilde_and_rem_qchar(unescaped, true);
2764
2765                 /*
2766                  * match iff entire original string is part of the
2767                  * longest prefix, implying the latter is at least
2768                  * the same size (after unescaping)
2769                  */
2770                 completed = !strncmp(words[0], unescaped, strlen(unescaped));
2771
2772                 afree(unescaped, ATEMP);
2773         }
2774         if (type == CT_COMPLIST && nwords > 1) {
2775                 /*
2776                  * print expansions, since we didn't get back
2777                  * just a single match
2778                  */
2779                 x_print_expansions(nwords, words,
2780                     tobool(flags & XCF_IS_COMMAND));
2781         }
2782         if (completed) {
2783                 /* expand on the command line */
2784                 xmp = NULL;
2785                 xcp = xbuf + start;
2786                 xep -= olen;
2787                 memmove(xcp, xcp + olen, xep - xcp + 1);
2788                 x_escape(words[0], nlen, x_do_ins);
2789         }
2790         x_adjust();
2791         /*
2792          * append a space if this is a single non-directory match
2793          * and not a parameter or homedir substitution
2794          */
2795         if (nwords == 1 && words[0][nlen - 1] != '/' &&
2796             !(flags & XCF_IS_NOSPACE)) {
2797                 x_ins(" ");
2798         }
2799
2800         x_free_words(nwords, words);
2801 }
2802
2803 /*-
2804  * NAME:
2805  *      x_adjust - redraw the line adjusting starting point etc.
2806  *
2807  * DESCRIPTION:
2808  *      This function is called when we have exceeded the bounds
2809  *      of the edit window. It increments x_adj_done so that
2810  *      functions like x_ins and x_delete know that we have been
2811  *      called and can skip the x_bs() stuff which has already
2812  *      been done by x_redraw.
2813  *
2814  * RETURN VALUE:
2815  *      None
2816  */
2817 static void
2818 x_adjust(void)
2819 {
2820         int col_left, n;
2821
2822         /* flag the fact that we were called */
2823         x_adj_done++;
2824
2825         /*
2826          * calculate the amount of columns we need to "go back"
2827          * from xcp to set xbp to (but never < xbuf) to 2/3 of
2828          * the display width; take care of pwidth though
2829          */
2830         if ((col_left = xx_cols * 2 / 3) < MIN_EDIT_SPACE) {
2831                 /*
2832                  * cowardly refuse to do anything
2833                  * if the available space is too small;
2834                  * fall back to dumb pdksh code
2835                  */
2836                 if ((xbp = xcp - (x_displen / 2)) < xbuf)
2837                         xbp = xbuf;
2838                 /* elide UTF-8 fixup as penalty */
2839                 goto x_adjust_out;
2840         }
2841
2842         /* fix up xbp to just past a character end first */
2843         xbp = xcp >= xep ? xep : x_bs0(xcp, xbuf);
2844         /* walk backwards */
2845         while (xbp > xbuf && col_left > 0) {
2846                 xbp = x_bs0(xbp - 1, xbuf);
2847                 col_left -= (n = x_size2(xbp, NULL));
2848         }
2849         /* check if we hit the prompt */
2850         if (xbp == xbuf && xcp != xbuf && col_left >= 0 && col_left < pwidth) {
2851                 /* so we did; force scrolling occurs */
2852                 xbp += utf_ptradj(xbp);
2853         }
2854
2855  x_adjust_out:
2856         xlp_valid = false;
2857         x_redraw(xx_cols);
2858         x_flush();
2859 }
2860
2861 static void
2862 x_e_ungetc(int c)
2863 {
2864         unget_char = c < 0 ? -1 : (c & 255);
2865 }
2866
2867 static int
2868 x_e_getc(void)
2869 {
2870         int c;
2871
2872         if (unget_char >= 0) {
2873                 c = unget_char;
2874                 unget_char = -1;
2875                 return (c);
2876         }
2877
2878 #ifndef MKSH_SMALL
2879         if (macroptr) {
2880                 if ((c = (unsigned char)*macroptr++))
2881                         return (c);
2882                 macroptr = NULL;
2883         }
2884 #endif
2885
2886         return (x_getc());
2887 }
2888
2889 static void
2890 x_e_putc2(int c)
2891 {
2892         int width = 1;
2893
2894         if (c == '\r' || c == '\n')
2895                 x_col = 0;
2896         if (x_col < xx_cols) {
2897                 if (UTFMODE && (c > 0x7F)) {
2898                         char utf_tmp[3];
2899                         size_t x;
2900
2901                         if (c < 0xA0)
2902                                 c = 0xFFFD;
2903                         x = utf_wctomb(utf_tmp, c);
2904                         x_putc(utf_tmp[0]);
2905                         if (x > 1)
2906                                 x_putc(utf_tmp[1]);
2907                         if (x > 2)
2908                                 x_putc(utf_tmp[2]);
2909                         width = utf_wcwidth(c);
2910                 } else
2911                         x_putc(c);
2912                 switch (c) {
2913                 case 7:
2914                         break;
2915                 case '\r':
2916                 case '\n':
2917                         break;
2918                 case '\b':
2919                         x_col--;
2920                         break;
2921                 default:
2922                         x_col += width;
2923                         break;
2924                 }
2925         }
2926         if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
2927                 x_adjust();
2928 }
2929
2930 static void
2931 x_e_putc3(const char **cp)
2932 {
2933         int width = 1, c = **(const unsigned char **)cp;
2934
2935         if (c == '\r' || c == '\n')
2936                 x_col = 0;
2937         if (x_col < xx_cols) {
2938                 if (UTFMODE && (c > 0x7F)) {
2939                         char *cp2;
2940
2941                         width = utf_widthadj(*cp, (const char **)&cp2);
2942                         while (*cp < cp2)
2943                                 x_putcf(*(*cp)++);
2944                 } else {
2945                         (*cp)++;
2946                         x_putc(c);
2947                 }
2948                 switch (c) {
2949                 case 7:
2950                         break;
2951                 case '\r':
2952                 case '\n':
2953                         break;
2954                 case '\b':
2955                         x_col--;
2956                         break;
2957                 default:
2958                         x_col += width;
2959                         break;
2960                 }
2961         }
2962         if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
2963                 x_adjust();
2964 }
2965
2966 static void
2967 x_e_puts(const char *s)
2968 {
2969         int adj = x_adj_done;
2970
2971         while (*s && adj == x_adj_done)
2972                 x_e_putc3(&s);
2973 }
2974
2975 /*-
2976  * NAME:
2977  *      x_set_arg - set an arg value for next function
2978  *
2979  * DESCRIPTION:
2980  *      This is a simple implementation of M-[0-9].
2981  *
2982  * RETURN VALUE:
2983  *      KSTD
2984  */
2985 static int
2986 x_set_arg(int c)
2987 {
2988         unsigned int n = 0;
2989         bool first = true;
2990
2991         /* strip command prefix */
2992         c &= 255;
2993         while (c >= 0 && ksh_isdigit(c)) {
2994                 n = n * 10 + ksh_numdig(c);
2995                 if (n > LINE)
2996                         /* upper bound for repeat */
2997                         goto x_set_arg_too_big;
2998                 c = x_e_getc();
2999                 first = false;
3000         }
3001         if (c < 0 || first) {
3002  x_set_arg_too_big:
3003                 x_e_putc2(7);
3004                 x_arg = 1;
3005                 x_arg_defaulted = true;
3006         } else {
3007                 x_e_ungetc(c);
3008                 x_arg = n;
3009                 x_arg_defaulted = false;
3010         }
3011         return (KSTD);
3012 }
3013
3014 /* Comment or uncomment the current line. */
3015 static int
3016 x_comment(int c MKSH_A_UNUSED)
3017 {
3018         int oldsize = x_size_str(xbuf);
3019         ssize_t len = xep - xbuf;
3020         int ret = x_do_comment(xbuf, xend - xbuf, &len);
3021
3022         if (ret < 0)
3023                 x_e_putc2(7);
3024         else {
3025                 x_modified();
3026                 xep = xbuf + len;
3027                 *xep = '\0';
3028                 xcp = xbp = xbuf;
3029                 x_redraw(oldsize);
3030                 if (ret > 0)
3031                         return (x_newline('\n'));
3032         }
3033         return (KSTD);
3034 }
3035
3036 static int
3037 x_version(int c MKSH_A_UNUSED)
3038 {
3039         char *o_xbuf = xbuf, *o_xend = xend;
3040         char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp;
3041         int lim = x_lastcp() - xbp;
3042         size_t vlen;
3043         char *v;
3044
3045         strdupx(v, KSH_VERSION, ATEMP);
3046
3047         xbuf = xbp = xcp = v;
3048         xend = xep = v + (vlen = strlen(v));
3049         x_redraw(lim);
3050         x_flush();
3051
3052         c = x_e_getc();
3053         xbuf = o_xbuf;
3054         xend = o_xend;
3055         xbp = o_xbp;
3056         xep = o_xep;
3057         xcp = o_xcp;
3058         x_redraw((int)vlen);
3059
3060         if (c < 0)
3061                 return (KSTD);
3062         /* This is what AT&T ksh seems to do... Very bizarre */
3063         if (c != ' ')
3064                 x_e_ungetc(c);
3065
3066         afree(v, ATEMP);
3067         return (KSTD);
3068 }
3069
3070 #ifndef MKSH_SMALL
3071 static int
3072 x_edit_line(int c MKSH_A_UNUSED)
3073 {
3074         if (x_arg_defaulted) {
3075                 if (xep == xbuf) {
3076                         x_e_putc2(7);
3077                         return (KSTD);
3078                 }
3079                 if (modified) {
3080                         *xep = '\0';
3081                         histsave(&source->line, xbuf, HIST_STORE, true);
3082                         x_arg = 0;
3083                 } else
3084                         x_arg = source->line - (histptr - x_histp);
3085         }
3086         if (x_arg)
3087                 shf_snprintf(xbuf, xend - xbuf, "%s %d",
3088                     "fc -e ${VISUAL:-${EDITOR:-vi}} --", x_arg);
3089         else
3090                 strlcpy(xbuf, "fc -e ${VISUAL:-${EDITOR:-vi}} --", xend - xbuf);
3091         xep = xbuf + strlen(xbuf);
3092         return (x_newline('\n'));
3093 }
3094 #endif
3095
3096 /*-
3097  * NAME:
3098  *      x_prev_histword - recover word from prev command
3099  *
3100  * DESCRIPTION:
3101  *      This function recovers the last word from the previous
3102  *      command and inserts it into the current edit line. If a
3103  *      numeric arg is supplied then the n'th word from the
3104  *      start of the previous command is used.
3105  *      As a side effect, trashes the mark in order to achieve
3106  *      being called in a repeatable fashion.
3107  *
3108  *      Bound to M-.
3109  *
3110  * RETURN VALUE:
3111  *      KSTD
3112  */
3113 static int
3114 x_prev_histword(int c MKSH_A_UNUSED)
3115 {
3116         char *rcp, *cp;
3117         char **xhp;
3118         int m = 1;
3119         /* -1 = defaulted; 0+ = argument */
3120         static int last_arg = -1;
3121
3122         if (x_last_command == XFUNC_prev_histword) {
3123                 if (xmp && modified > 1)
3124                         x_kill_region(0);
3125                 if (modified)
3126                         m = modified;
3127         } else
3128                 last_arg = x_arg_defaulted ? -1 : x_arg;
3129         xhp = histptr - (m - 1);
3130         if ((xhp < history) || !(cp = *xhp)) {
3131                 x_e_putc2(7);
3132                 x_modified();
3133                 return (KSTD);
3134         }
3135         x_set_mark(0);
3136         if ((x_arg = last_arg) == -1) {
3137                 /* x_arg_defaulted */
3138
3139                 rcp = &cp[strlen(cp) - 1];
3140                 /*
3141                  * ignore white-space after the last word
3142                  */
3143                 while (rcp > cp && is_cfs(*rcp))
3144                         rcp--;
3145                 while (rcp > cp && !is_cfs(*rcp))
3146                         rcp--;
3147                 if (is_cfs(*rcp))
3148                         rcp++;
3149                 x_ins(rcp);
3150         } else {
3151                 /* not x_arg_defaulted */
3152                 char ch;
3153
3154                 rcp = cp;
3155                 /*
3156                  * ignore white-space at start of line
3157                  */
3158                 while (*rcp && is_cfs(*rcp))
3159                         rcp++;
3160                 while (x_arg-- > 0) {
3161                         while (*rcp && !is_cfs(*rcp))
3162                                 rcp++;
3163                         while (*rcp && is_cfs(*rcp))
3164                                 rcp++;
3165                 }
3166                 cp = rcp;
3167                 while (*rcp && !is_cfs(*rcp))
3168                         rcp++;
3169                 ch = *rcp;
3170                 *rcp = '\0';
3171                 x_ins(cp);
3172                 *rcp = ch;
3173         }
3174         modified = m + 1;
3175         return (KSTD);
3176 }
3177
3178 #ifndef MKSH_SMALL
3179 /* Uppercase N(1) words */
3180 static int
3181 x_fold_upper(int c MKSH_A_UNUSED)
3182 {
3183         return (x_fold_case('U'));
3184 }
3185
3186 /* Lowercase N(1) words */
3187 static int
3188 x_fold_lower(int c MKSH_A_UNUSED)
3189 {
3190         return (x_fold_case('L'));
3191 }
3192
3193 /* Titlecase N(1) words */
3194 static int
3195 x_fold_capitalise(int c MKSH_A_UNUSED)
3196 {
3197         return (x_fold_case('C'));
3198 }
3199
3200 /*-
3201  * NAME:
3202  *      x_fold_case - convert word to UPPER/lower/Capital case
3203  *
3204  * DESCRIPTION:
3205  *      This function is used to implement M-U/M-u, M-L/M-l, M-C/M-c
3206  *      to UPPER CASE, lower case or Capitalise Words.
3207  *
3208  * RETURN VALUE:
3209  *      None
3210  */
3211 static int
3212 x_fold_case(int c)
3213 {
3214         char *cp = xcp;
3215
3216         if (cp == xep) {
3217                 x_e_putc2(7);
3218                 return (KSTD);
3219         }
3220         while (x_arg--) {
3221                 /*
3222                  * first skip over any white-space
3223                  */
3224                 while (cp != xep && is_mfs(*cp))
3225                         cp++;
3226                 /*
3227                  * do the first char on its own since it may be
3228                  * a different action than for the rest.
3229                  */
3230                 if (cp != xep) {
3231                         if (c == 'L')
3232                                 /* lowercase */
3233                                 *cp = ksh_tolower(*cp);
3234                         else
3235                                 /* uppercase, capitalise */
3236                                 *cp = ksh_toupper(*cp);
3237                         cp++;
3238                 }
3239                 /*
3240                  * now for the rest of the word
3241                  */
3242                 while (cp != xep && !is_mfs(*cp)) {
3243                         if (c == 'U')
3244                                 /* uppercase */
3245                                 *cp = ksh_toupper(*cp);
3246                         else
3247                                 /* lowercase, capitalise */
3248                                 *cp = ksh_tolower(*cp);
3249                         cp++;
3250                 }
3251         }
3252         x_goto(cp);
3253         x_modified();
3254         return (KSTD);
3255 }
3256 #endif
3257
3258 /*-
3259  * NAME:
3260  *      x_lastcp - last visible char
3261  *
3262  * SYNOPSIS:
3263  *      x_lastcp()
3264  *
3265  * DESCRIPTION:
3266  *      This function returns a pointer to that char in the
3267  *      edit buffer that will be the last displayed on the
3268  *      screen. The sequence:
3269  *
3270  *      cp = x_lastcp();
3271  *      while (cp > xcp)
3272  *              x_bs3(&cp);
3273  *
3274  *      Will position the cursor correctly on the screen.
3275  *
3276  * RETURN VALUE:
3277  *      cp or NULL
3278  */
3279 static char *
3280 x_lastcp(void)
3281 {
3282         if (!xlp_valid) {
3283                 int i = 0, j;
3284                 char *xlp2;
3285
3286                 xlp = xbp;
3287                 while (xlp < xep) {
3288                         j = x_size2(xlp, &xlp2);
3289                         if ((i + j) > x_displen)
3290                                 break;
3291                         i += j;
3292                         xlp = xlp2;
3293                 }
3294         }
3295         xlp_valid = true;
3296         return (xlp);
3297 }
3298
3299 static void
3300 x_mode(bool onoff)
3301 {
3302         static bool x_cur_mode;
3303
3304         if (x_cur_mode == onoff)
3305                 return;
3306         x_cur_mode = onoff;
3307
3308         if (onoff) {
3309                 x_mkraw(tty_fd, NULL, false);
3310
3311                 edchars.erase = tty_state.c_cc[VERASE];
3312                 edchars.kill = tty_state.c_cc[VKILL];
3313                 edchars.intr = tty_state.c_cc[VINTR];
3314                 edchars.quit = tty_state.c_cc[VQUIT];
3315                 edchars.eof = tty_state.c_cc[VEOF];
3316 #ifdef VWERASE
3317                 edchars.werase = tty_state.c_cc[VWERASE];
3318 #else
3319                 edchars.werase = 0;
3320 #endif
3321
3322                 if (!edchars.erase)
3323                         edchars.erase = CTRL('H');
3324                 if (!edchars.kill)
3325                         edchars.kill = CTRL('U');
3326                 if (!edchars.intr)
3327                         edchars.intr = CTRL('C');
3328                 if (!edchars.quit)
3329                         edchars.quit = CTRL('\\');
3330                 if (!edchars.eof)
3331                         edchars.eof = CTRL('D');
3332                 if (!edchars.werase)
3333                         edchars.werase = CTRL('W');
3334
3335 #ifdef _POSIX_VDISABLE
3336                 /* Convert unset values to internal 'unset' value */
3337                 if (edchars.erase == _POSIX_VDISABLE)
3338                         edchars.erase = -1;
3339                 if (edchars.kill == _POSIX_VDISABLE)
3340                         edchars.kill = -1;
3341                 if (edchars.intr == _POSIX_VDISABLE)
3342                         edchars.intr = -1;
3343                 if (edchars.quit == _POSIX_VDISABLE)
3344                         edchars.quit = -1;
3345                 if (edchars.eof == _POSIX_VDISABLE)
3346                         edchars.eof = -1;
3347                 if (edchars.werase == _POSIX_VDISABLE)
3348                         edchars.werase = -1;
3349 #endif
3350
3351                 if (edchars.erase >= 0) {
3352                         bind_if_not_bound(0, edchars.erase, XFUNC_del_back);
3353                         bind_if_not_bound(1, edchars.erase, XFUNC_del_bword);
3354                 }
3355                 if (edchars.kill >= 0)
3356                         bind_if_not_bound(0, edchars.kill, XFUNC_del_line);
3357                 if (edchars.werase >= 0)
3358                         bind_if_not_bound(0, edchars.werase, XFUNC_del_bword);
3359                 if (edchars.intr >= 0)
3360                         bind_if_not_bound(0, edchars.intr, XFUNC_abort);
3361                 if (edchars.quit >= 0)
3362                         bind_if_not_bound(0, edchars.quit, XFUNC_noop);
3363         } else
3364                 mksh_tcset(tty_fd, &tty_state);
3365 }
3366
3367 #if !MKSH_S_NOVI
3368 /* +++ vi editing mode +++ */
3369
3370 struct edstate {
3371         char *cbuf;
3372         ssize_t winleft;
3373         ssize_t cbufsize;
3374         ssize_t linelen;
3375         ssize_t cursor;
3376 };
3377
3378 static int vi_hook(int);
3379 static int nextstate(int);
3380 static int vi_insert(int);
3381 static int vi_cmd(int, const char *);
3382 static int domove(int, const char *, int);
3383 static int redo_insert(int);
3384 static void yank_range(int, int);
3385 static int bracktype(int);
3386 static void save_cbuf(void);
3387 static void restore_cbuf(void);
3388 static int putbuf(const char *, ssize_t, bool);
3389 static void del_range(int, int);
3390 static int findch(int, int, bool, bool) MKSH_A_PURE;
3391 static int forwword(int);
3392 static int backword(int);
3393 static int endword(int);
3394 static int Forwword(int);
3395 static int Backword(int);
3396 static int Endword(int);
3397 static int grabhist(int, int);
3398 static int grabsearch(int, int, int, const char *);
3399 static void redraw_line(bool);
3400 static void refresh(int);
3401 static int outofwin(void);
3402 static void rewindow(void);
3403 static int newcol(unsigned char, int);
3404 static void display(char *, char *, int);
3405 static void ed_mov_opt(int, char *);
3406 static int expand_word(int);
3407 static int complete_word(int, int);
3408 static int print_expansions(struct edstate *, int);
3409 #define char_len(c)     ((ISCTRL((unsigned char)c) && \
3410                         /* but not C1 */ (unsigned char)c < 0x80) ? 2 : 1)
3411 static void x_vi_zotc(int);
3412 static void vi_error(void);
3413 static void vi_macro_reset(void);
3414 static int x_vi_putbuf(const char *, size_t);
3415
3416 #define vC      0x01            /* a valid command that isn't a vM, vE, vU */
3417 #define vM      0x02            /* movement command (h, l, etc.) */
3418 #define vE      0x04            /* extended command (c, d, y) */
3419 #define vX      0x08            /* long command (@, f, F, t, T, etc.) */
3420 #define vU      0x10            /* an UN-undoable command (that isn't a vM) */
3421 #define vB      0x20            /* bad command (^@) */
3422 #define vZ      0x40            /* repeat count defaults to 0 (not 1) */
3423 #define vS      0x80            /* search (/, ?) */
3424
3425 #define is_bad(c)       (classify[(c)&0x7f]&vB)
3426 #define is_cmd(c)       (classify[(c)&0x7f]&(vM|vE|vC|vU))
3427 #define is_move(c)      (classify[(c)&0x7f]&vM)
3428 #define is_extend(c)    (classify[(c)&0x7f]&vE)
3429 #define is_long(c)      (classify[(c)&0x7f]&vX)
3430 #define is_undoable(c)  (!(classify[(c)&0x7f]&vU))
3431 #define is_srch(c)      (classify[(c)&0x7f]&vS)
3432 #define is_zerocount(c) (classify[(c)&0x7f]&vZ)
3433
3434 static const unsigned char classify[128] = {
3435 /*       0      1       2       3       4       5       6       7       */
3436 /* 0    ^@      ^A      ^B      ^C      ^D      ^E      ^F      ^G      */
3437         vB,     0,      0,      0,      0,      vC|vU,  vC|vZ,  0,
3438 /* 1    ^H      ^I      ^J      ^K      ^L      ^M      ^N      ^O      */
3439         vM,     vC|vZ,  0,      0,      vC|vU,  0,      vC,     0,
3440 /* 2    ^P      ^Q      ^R      ^S      ^T      ^U      ^V      ^W      */
3441         vC,     0,      vC|vU,  0,      0,      0,      vC,     0,
3442 /* 3    ^X      ^Y      ^Z      ^[      ^\      ^]      ^^      ^_      */
3443         vC,     0,      0,      vC|vZ,  0,      0,      0,      0,
3444 /* 4    <space> !       "       #       $       %       &       '       */
3445         vM,     0,      0,      vC,     vM,     vM,     0,      0,
3446 /* 5    (       )       *       +       ,       -       .       /       */
3447         0,      0,      vC,     vC,     vM,     vC,     0,      vC|vS,
3448 /* 6    0       1       2       3       4       5       6       7       */
3449         vM,     0,      0,      0,      0,      0,      0,      0,
3450 /* 7    8       9       :       ;       <       =       >       ?       */
3451         0,      0,      0,      vM,     0,      vC,     0,      vC|vS,
3452 /* 8    @       A       B       C       D       E       F       G       */
3453         vC|vX,  vC,     vM,     vC,     vC,     vM,     vM|vX,  vC|vU|vZ,
3454 /* 9    H       I       J       K       L       M       N       O       */
3455         0,      vC,     0,      0,      0,      0,      vC|vU,  vU,
3456 /* A    P       Q       R       S       T       U       V       W       */
3457         vC,     0,      vC,     vC,     vM|vX,  vC,     0,      vM,
3458 /* B    X       Y       Z       [       \       ]       ^       _       */
3459         vC,     vC|vU,  0,      vU,     vC|vZ,  0,      vM,     vC|vZ,
3460 /* C    `       a       b       c       d       e       f       g       */
3461         0,      vC,     vM,     vE,     vE,     vM,     vM|vX,  vC|vZ,
3462 /* D    h       i       j       k       l       m       n       o       */
3463         vM,     vC,     vC|vU,  vC|vU,  vM,     0,      vC|vU,  0,
3464 /* E    p       q       r       s       t       u       v       w       */
3465         vC,     0,      vX,     vC,     vM|vX,  vC|vU,  vC|vU|vZ, vM,
3466 /* F    x       y       z       {       |       }       ~       ^?      */
3467         vC,     vE|vU,  0,      0,      vM|vZ,  0,      vC,     0
3468 };
3469
3470 #define MAXVICMD        3
3471 #define SRCHLEN         40
3472
3473 #define INSERT          1
3474 #define REPLACE         2
3475
3476 #define VNORMAL         0               /* command, insert or replace mode */
3477 #define VARG1           1               /* digit prefix (first, eg, 5l) */
3478 #define VEXTCMD         2               /* cmd + movement (eg, cl) */
3479 #define VARG2           3               /* digit prefix (second, eg, 2c3l) */
3480 #define VXCH            4               /* f, F, t, T, @ */
3481 #define VFAIL           5               /* bad command */
3482 #define VCMD            6               /* single char command (eg, X) */
3483 #define VREDO           7               /* . */
3484 #define VLIT            8               /* ^V */
3485 #define VSEARCH         9               /* /, ? */
3486 #define VVERSION        10              /* <ESC> ^V */
3487 #define VPREFIX2        11              /* ^[[ and ^[O in insert mode */
3488
3489 static struct edstate   *save_edstate(struct edstate *old);
3490 static void             restore_edstate(struct edstate *old, struct edstate *news);
3491 static void             free_edstate(struct edstate *old);
3492
3493 static struct edstate   ebuf;
3494 static struct edstate   undobuf;
3495
3496 static struct edstate   *es;            /* current editor state */
3497 static struct edstate   *undo;
3498
3499 static char *ibuf;                      /* input buffer */
3500 static bool first_insert;               /* set when starting in insert mode */
3501 static int saved_inslen;                /* saved inslen for first insert */
3502 static int inslen;                      /* length of input buffer */
3503 static int srchlen;                     /* length of current search pattern */
3504 static char *ybuf;                      /* yank buffer */
3505 static int yanklen;                     /* length of yank buffer */
3506 static int fsavecmd = ' ';              /* last find command */
3507 static int fsavech;                     /* character to find */
3508 static char lastcmd[MAXVICMD];          /* last non-move command */
3509 static int lastac;                      /* argcnt for lastcmd */
3510 static int lastsearch = ' ';            /* last search command */
3511 static char srchpat[SRCHLEN];           /* last search pattern */
3512 static int insert;                      /* <>0 in insert mode */
3513 static int hnum;                        /* position in history */
3514 static int ohnum;                       /* history line copied (after mod) */
3515 static int hlast;                       /* 1 past last position in history */
3516 static int state;
3517
3518 /*
3519  * Information for keeping track of macros that are being expanded.
3520  * The format of buf is the alias contents followed by a NUL byte followed
3521  * by the name (letter) of the alias. The end of the buffer is marked by
3522  * a double NUL. The name of the alias is stored so recursive macros can
3523  * be detected.
3524  */
3525 struct macro_state {
3526         unsigned char *p;       /* current position in buf */
3527         unsigned char *buf;     /* pointer to macro(s) being expanded */
3528         size_t len;             /* how much data in buffer */
3529 };
3530 static struct macro_state macro;
3531
3532 /* last input was expanded */
3533 static enum expand_mode {
3534         NONE = 0, EXPAND, COMPLETE, PRINT
3535 } expanded;
3536
3537 static int
3538 x_vi(char *buf)
3539 {
3540         int c;
3541
3542         state = VNORMAL;
3543         ohnum = hnum = hlast = histnum(-1) + 1;
3544         insert = INSERT;
3545         saved_inslen = inslen;
3546         first_insert = true;
3547         inslen = 0;
3548         vi_macro_reset();
3549
3550         ebuf.cbuf = buf;
3551         if (undobuf.cbuf == NULL) {
3552                 ibuf = alloc(LINE, AEDIT);
3553                 ybuf = alloc(LINE, AEDIT);
3554                 undobuf.cbuf = alloc(LINE, AEDIT);
3555         }
3556         undobuf.cbufsize = ebuf.cbufsize = LINE;
3557         undobuf.linelen = ebuf.linelen = 0;
3558         undobuf.cursor = ebuf.cursor = 0;
3559         undobuf.winleft = ebuf.winleft = 0;
3560         es = &ebuf;
3561         undo = &undobuf;
3562
3563         x_init_prompt(true);
3564         x_col = pwidth;
3565
3566         if (wbuf_len != x_cols - 3 && ((wbuf_len = x_cols - 3))) {
3567                 wbuf[0] = aresize(wbuf[0], wbuf_len, AEDIT);
3568                 wbuf[1] = aresize(wbuf[1], wbuf_len, AEDIT);
3569         }
3570         if (wbuf_len) {
3571                 memset(wbuf[0], ' ', wbuf_len);
3572                 memset(wbuf[1], ' ', wbuf_len);
3573         }
3574         winwidth = x_cols - pwidth - 3;
3575         win = 0;
3576         morec = ' ';
3577         lastref = 1;
3578         holdlen = 0;
3579
3580         editmode = 2;
3581         x_flush();
3582         while (/* CONSTCOND */ 1) {
3583                 if (macro.p) {
3584                         c = (unsigned char)*macro.p++;
3585                         /* end of current macro? */
3586                         if (!c) {
3587                                 /* more macros left to finish? */
3588                                 if (*macro.p++)
3589                                         continue;
3590                                 /* must be the end of all the macros */
3591                                 vi_macro_reset();
3592                                 c = x_getc();
3593                         }
3594                 } else
3595                         c = x_getc();
3596
3597                 if (c == -1)
3598                         break;
3599                 if (state != VLIT) {
3600                         if (c == edchars.intr || c == edchars.quit) {
3601                                 /* pretend we got an interrupt */
3602                                 x_vi_zotc(c);
3603                                 x_flush();
3604                                 trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
3605                                 x_mode(false);
3606                                 unwind(LSHELL);
3607                         } else if (c == edchars.eof && state != VVERSION) {
3608                                 if (es->linelen == 0) {
3609                                         x_vi_zotc(edchars.eof);
3610                                         c = -1;
3611                                         break;
3612                                 }
3613                                 continue;
3614                         }
3615                 }
3616                 if (vi_hook(c))
3617                         break;
3618                 x_flush();
3619         }
3620
3621         x_putc('\r');
3622         x_putc('\n');
3623         x_flush();
3624
3625         if (c == -1 || (ssize_t)LINE <= es->linelen)
3626                 return (-1);
3627
3628         if (es->cbuf != buf)
3629                 memcpy(buf, es->cbuf, es->linelen);
3630
3631         buf[es->linelen++] = '\n';
3632
3633         return (es->linelen);
3634 }
3635
3636 static int
3637 vi_hook(int ch)
3638 {
3639         static char curcmd[MAXVICMD], locpat[SRCHLEN];
3640         static int cmdlen, argc1, argc2;
3641
3642         switch (state) {
3643
3644         case VNORMAL:
3645                 /* PC scancodes */
3646                 if (!ch) switch (cmdlen = 0, (ch = x_getc())) {
3647                 case 71: ch = '0'; goto pseudo_vi_command;
3648                 case 72: ch = 'k'; goto pseudo_vi_command;
3649                 case 73: ch = 'A'; goto vi_xfunc_search_up;
3650                 case 75: ch = 'h'; goto pseudo_vi_command;
3651                 case 77: ch = 'l'; goto pseudo_vi_command;
3652                 case 79: ch = '$'; goto pseudo_vi_command;
3653                 case 80: ch = 'j'; goto pseudo_vi_command;
3654                 case 83: ch = 'x'; goto pseudo_vi_command;
3655                 default: ch = 0; goto vi_insert_failed;
3656                 }
3657                 if (insert != 0) {
3658                         if (ch == CTRL('v')) {
3659                                 state = VLIT;
3660                                 ch = '^';
3661                         }
3662                         switch (vi_insert(ch)) {
3663                         case -1:
3664  vi_insert_failed:
3665                                 vi_error();
3666                                 state = VNORMAL;
3667                                 break;
3668                         case 0:
3669                                 if (state == VLIT) {
3670                                         es->cursor--;
3671                                         refresh(0);
3672                                 } else
3673                                         refresh(insert != 0);
3674                                 break;
3675                         case 1:
3676                                 return (1);
3677                         }
3678                 } else {
3679                         if (ch == '\r' || ch == '\n')
3680                                 return (1);
3681                         cmdlen = 0;
3682                         argc1 = 0;
3683                         if (ch >= ord('1') && ch <= ord('9')) {
3684                                 argc1 = ksh_numdig(ch);
3685                                 state = VARG1;
3686                         } else {
3687  pseudo_vi_command:
3688                                 curcmd[cmdlen++] = ch;
3689                                 state = nextstate(ch);
3690                                 if (state == VSEARCH) {
3691                                         save_cbuf();
3692                                         es->cursor = 0;
3693                                         es->linelen = 0;
3694                                         if (putbuf(ch == '/' ? "/" : "?", 1,
3695                                             false) != 0)
3696                                                 return (-1);
3697                                         refresh(0);
3698                                 }
3699                                 if (state == VVERSION) {
3700                                         save_cbuf();
3701                                         es->cursor = 0;
3702                                         es->linelen = 0;
3703                                         putbuf(KSH_VERSION,
3704                                             strlen(KSH_VERSION), false);
3705                                         refresh(0);
3706                                 }
3707                         }
3708                 }
3709                 break;
3710
3711         case VLIT:
3712                 if (is_bad(ch)) {
3713                         del_range(es->cursor, es->cursor + 1);
3714                         vi_error();
3715                 } else
3716                         es->cbuf[es->cursor++] = ch;
3717                 refresh(1);
3718                 state = VNORMAL;
3719                 break;
3720
3721         case VVERSION:
3722                 restore_cbuf();
3723                 state = VNORMAL;
3724                 refresh(0);
3725                 break;
3726
3727         case VARG1:
3728                 if (ksh_isdigit(ch))
3729                         argc1 = argc1 * 10 + ksh_numdig(ch);
3730                 else {
3731                         curcmd[cmdlen++] = ch;
3732                         state = nextstate(ch);
3733                 }
3734                 break;
3735
3736         case VEXTCMD:
3737                 argc2 = 0;
3738                 if (ch >= ord('1') && ch <= ord('9')) {
3739                         argc2 = ksh_numdig(ch);
3740                         state = VARG2;
3741                         return (0);
3742                 } else {
3743                         curcmd[cmdlen++] = ch;
3744                         if (ch == curcmd[0])
3745                                 state = VCMD;
3746                         else if (is_move(ch))
3747                                 state = nextstate(ch);
3748                         else
3749                                 state = VFAIL;
3750                 }
3751                 break;
3752
3753         case VARG2:
3754                 if (ksh_isdigit(ch))
3755                         argc2 = argc2 * 10 + ksh_numdig(ch);
3756                 else {
3757                         if (argc1 == 0)
3758                                 argc1 = argc2;
3759                         else
3760                                 argc1 *= argc2;
3761                         curcmd[cmdlen++] = ch;
3762                         if (ch == curcmd[0])
3763                                 state = VCMD;
3764                         else if (is_move(ch))
3765                                 state = nextstate(ch);
3766                         else
3767                                 state = VFAIL;
3768                 }
3769                 break;
3770
3771         case VXCH:
3772                 if (ch == CTRL('['))
3773                         state = VNORMAL;
3774                 else {
3775                         curcmd[cmdlen++] = ch;
3776                         state = VCMD;
3777                 }
3778                 break;
3779
3780         case VSEARCH:
3781                 if (ch == '\r' || ch == '\n' /*|| ch == CTRL('[')*/ ) {
3782                         restore_cbuf();
3783                         /* Repeat last search? */
3784                         if (srchlen == 0) {
3785                                 if (!srchpat[0]) {
3786                                         vi_error();
3787                                         state = VNORMAL;
3788                                         refresh(0);
3789                                         return (0);
3790                                 }
3791                         } else {
3792                                 locpat[srchlen] = '\0';
3793                                 memcpy(srchpat, locpat, srchlen + 1);
3794                         }
3795                         state = VCMD;
3796                 } else if (ch == edchars.erase || ch == CTRL('h')) {
3797                         if (srchlen != 0) {
3798                                 srchlen--;
3799                                 es->linelen -= char_len(locpat[srchlen]);
3800                                 es->cursor = es->linelen;
3801                                 refresh(0);
3802                                 return (0);
3803                         }
3804                         restore_cbuf();
3805                         state = VNORMAL;
3806                         refresh(0);
3807                 } else if (ch == edchars.kill) {
3808                         srchlen = 0;
3809                         es->linelen = 1;
3810                         es->cursor = 1;
3811                         refresh(0);
3812                         return (0);
3813                 } else if (ch == edchars.werase) {
3814                         unsigned int i, n;
3815                         struct edstate new_es, *save_es;
3816
3817                         new_es.cursor = srchlen;
3818                         new_es.cbuf = locpat;
3819
3820                         save_es = es;
3821                         es = &new_es;
3822                         n = backword(1);
3823                         es = save_es;
3824
3825                         i = (unsigned)srchlen;
3826                         while (--i >= n)
3827                                 es->linelen -= char_len(locpat[i]);
3828                         srchlen = (int)n;
3829                         es->cursor = es->linelen;
3830                         refresh(0);
3831                         return (0);
3832                 } else {
3833                         if (srchlen == SRCHLEN - 1)
3834                                 vi_error();
3835                         else {
3836                                 locpat[srchlen++] = ch;
3837                                 if (ISCTRL(ch) && /* but not C1 */ ch < 0x80) {
3838                                         if ((size_t)es->linelen + 2 >
3839                                             (size_t)es->cbufsize)
3840                                                 vi_error();
3841                                         es->cbuf[es->linelen++] = '^';
3842                                         es->cbuf[es->linelen++] = UNCTRL(ch);
3843                                 } else {
3844                                         if (es->linelen >= es->cbufsize)
3845                                                 vi_error();
3846                                         es->cbuf[es->linelen++] = ch;
3847                                 }
3848                                 es->cursor = es->linelen;
3849                                 refresh(0);
3850                         }
3851                         return (0);
3852                 }
3853                 break;
3854
3855         case VPREFIX2:
3856  vi_xfunc_search_up:
3857                 state = VFAIL;
3858                 switch (ch) {
3859                 case 'A':
3860                         /* the cursor may not be at the BOL */
3861                         if (!es->cursor)
3862                                 break;
3863                         /* nor further in the line than we can search for */
3864                         if ((size_t)es->cursor >= sizeof(srchpat) - 1)
3865                                 es->cursor = sizeof(srchpat) - 2;
3866                         /* anchor the search pattern */
3867                         srchpat[0] = '^';
3868                         /* take the current line up to the cursor */
3869                         memmove(srchpat + 1, es->cbuf, es->cursor);
3870                         srchpat[es->cursor + 1] = '\0';
3871                         /* set a magic flag */
3872                         argc1 = 2 + (int)es->cursor;
3873                         /* and emulate a backwards history search */
3874                         lastsearch = '/';
3875                         *curcmd = 'n';
3876                         goto pseudo_VCMD;
3877                 }
3878                 break;
3879         }
3880
3881         switch (state) {
3882         case VCMD:
3883  pseudo_VCMD:
3884                 state = VNORMAL;
3885                 switch (vi_cmd(argc1, curcmd)) {
3886                 case -1:
3887                         vi_error();
3888                         refresh(0);
3889                         break;
3890                 case 0:
3891                         if (insert != 0)
3892                                 inslen = 0;
3893                         refresh(insert != 0);
3894                         break;
3895                 case 1:
3896                         refresh(0);
3897                         return (1);
3898                 case 2:
3899                         /* back from a 'v' command - don't redraw the screen */
3900                         return (1);
3901                 }
3902                 break;
3903
3904         case VREDO:
3905                 state = VNORMAL;
3906                 if (argc1 != 0)
3907                         lastac = argc1;
3908                 switch (vi_cmd(lastac, lastcmd)) {
3909                 case -1:
3910                         vi_error();
3911                         refresh(0);
3912                         break;
3913                 case 0:
3914                         if (insert != 0) {
3915                                 if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
3916                                     lastcmd[0] == 'C') {
3917                                         if (redo_insert(1) != 0)
3918                                                 vi_error();
3919                                 } else {
3920                                         if (redo_insert(lastac) != 0)
3921                                                 vi_error();
3922                                 }
3923                         }
3924                         refresh(0);
3925                         break;
3926                 case 1:
3927                         refresh(0);
3928                         return (1);
3929                 case 2:
3930                         /* back from a 'v' command - can't happen */
3931                         break;
3932                 }
3933                 break;
3934
3935         case VFAIL:
3936                 state = VNORMAL;
3937                 vi_error();
3938                 break;
3939         }
3940         return (0);
3941 }
3942
3943 static int
3944 nextstate(int ch)
3945 {
3946         if (is_extend(ch))
3947                 return (VEXTCMD);
3948         else if (is_srch(ch))
3949                 return (VSEARCH);
3950         else if (is_long(ch))
3951                 return (VXCH);
3952         else if (ch == '.')
3953                 return (VREDO);
3954         else if (ch == CTRL('v'))
3955                 return (VVERSION);
3956         else if (is_cmd(ch))
3957                 return (VCMD);
3958         else
3959                 return (VFAIL);
3960 }
3961
3962 static int
3963 vi_insert(int ch)
3964 {
3965         int tcursor;
3966
3967         if (ch == edchars.erase || ch == CTRL('h')) {
3968                 if (insert == REPLACE) {
3969                         if (es->cursor == undo->cursor) {
3970                                 vi_error();
3971                                 return (0);
3972                         }
3973                         if (inslen > 0)
3974                                 inslen--;
3975                         es->cursor--;
3976                         if (es->cursor >= undo->linelen)
3977                                 es->linelen--;
3978                         else
3979                                 es->cbuf[es->cursor] = undo->cbuf[es->cursor];
3980                 } else {
3981                         if (es->cursor == 0)
3982                                 return (0);
3983                         if (inslen > 0)
3984                                 inslen--;
3985                         es->cursor--;
3986                         es->linelen--;
3987                         memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor + 1],
3988                             es->linelen - es->cursor + 1);
3989                 }
3990                 expanded = NONE;
3991                 return (0);
3992         }
3993         if (ch == edchars.kill) {
3994                 if (es->cursor != 0) {
3995                         inslen = 0;
3996                         memmove(es->cbuf, &es->cbuf[es->cursor],
3997                             es->linelen - es->cursor);
3998                         es->linelen -= es->cursor;
3999                         es->cursor = 0;
4000                 }
4001                 expanded = NONE;
4002                 return (0);
4003         }
4004         if (ch == edchars.werase) {
4005                 if (es->cursor != 0) {
4006                         tcursor = backword(1);
4007                         memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
4008                             es->linelen - es->cursor);
4009                         es->linelen -= es->cursor - tcursor;
4010                         if (inslen < es->cursor - tcursor)
4011                                 inslen = 0;
4012                         else
4013                                 inslen -= es->cursor - tcursor;
4014                         es->cursor = tcursor;
4015                 }
4016                 expanded = NONE;
4017                 return (0);
4018         }
4019         /*
4020          * If any chars are entered before escape, trash the saved insert
4021          * buffer (if user inserts & deletes char, ibuf gets trashed and
4022          * we don't want to use it)
4023          */
4024         if (first_insert && ch != CTRL('['))
4025                 saved_inslen = 0;
4026         switch (ch) {
4027         case '\0':
4028                 return (-1);
4029
4030         case '\r':
4031         case '\n':
4032                 return (1);
4033
4034         case CTRL('['):
4035                 expanded = NONE;
4036                 if (first_insert) {
4037                         first_insert = false;
4038                         if (inslen == 0) {
4039                                 inslen = saved_inslen;
4040                                 return (redo_insert(0));
4041                         }
4042                         lastcmd[0] = 'a';
4043                         lastac = 1;
4044                 }
4045                 if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
4046                     lastcmd[0] == 'C')
4047                         return (redo_insert(0));
4048                 else
4049                         return (redo_insert(lastac - 1));
4050
4051         /* { Begin nonstandard vi commands */
4052         case CTRL('x'):
4053                 expand_word(0);
4054                 break;
4055
4056         case CTRL('f'):
4057                 complete_word(0, 0);
4058                 break;
4059
4060         case CTRL('e'):
4061                 print_expansions(es, 0);
4062                 break;
4063
4064         case CTRL('i'):
4065                 if (Flag(FVITABCOMPLETE)) {
4066                         complete_word(0, 0);
4067                         break;
4068                 }
4069                 /* FALLTHROUGH */
4070         /* End nonstandard vi commands } */
4071
4072         default:
4073                 if (es->linelen >= es->cbufsize - 1)
4074                         return (-1);
4075                 ibuf[inslen++] = ch;
4076                 if (insert == INSERT) {
4077                         memmove(&es->cbuf[es->cursor + 1], &es->cbuf[es->cursor],
4078                             es->linelen - es->cursor);
4079                         es->linelen++;
4080                 }
4081                 es->cbuf[es->cursor++] = ch;
4082                 if (insert == REPLACE && es->cursor > es->linelen)
4083                         es->linelen++;
4084                 expanded = NONE;
4085         }
4086         return (0);
4087 }
4088
4089 static int
4090 vi_cmd(int argcnt, const char *cmd)
4091 {
4092         int ncursor;
4093         int cur, c1, c2, c3 = 0;
4094         int any;
4095         struct edstate *t;
4096
4097         if (argcnt == 0 && !is_zerocount(*cmd))
4098                 argcnt = 1;
4099
4100         if (is_move(*cmd)) {
4101                 if ((cur = domove(argcnt, cmd, 0)) >= 0) {
4102                         if (cur == es->linelen && cur != 0)
4103                                 cur--;
4104                         es->cursor = cur;
4105                 } else
4106                         return (-1);
4107         } else {
4108                 /* Don't save state in middle of macro.. */
4109                 if (is_undoable(*cmd) && !macro.p) {
4110                         undo->winleft = es->winleft;
4111                         memmove(undo->cbuf, es->cbuf, es->linelen);
4112                         undo->linelen = es->linelen;
4113                         undo->cursor = es->cursor;
4114                         lastac = argcnt;
4115                         memmove(lastcmd, cmd, MAXVICMD);
4116                 }
4117                 switch (*cmd) {
4118
4119                 case CTRL('l'):
4120                 case CTRL('r'):
4121                         redraw_line(true);
4122                         break;
4123
4124                 case '@':
4125                         {
4126                                 static char alias[] = "_\0";
4127                                 struct tbl *ap;
4128                                 size_t olen, nlen;
4129                                 char *p, *nbuf;
4130
4131                                 /* lookup letter in alias list... */
4132                                 alias[1] = cmd[1];
4133                                 ap = ktsearch(&aliases, alias, hash(alias));
4134                                 if (!cmd[1] || !ap || !(ap->flag & ISSET))
4135                                         return (-1);
4136                                 /* check if this is a recursive call... */
4137                                 if ((p = (char *)macro.p))
4138                                         while ((p = strnul(p)) && p[1])
4139                                                 if (*++p == cmd[1])
4140                                                         return (-1);
4141                                 /* insert alias into macro buffer */
4142                                 nlen = strlen(ap->val.s) + 1;
4143                                 olen = !macro.p ? 2 :
4144                                     macro.len - (macro.p - macro.buf);
4145                                 /*
4146                                  * at this point, it's fairly reasonable that
4147                                  * nlen + olen + 2 doesn't overflow
4148                                  */
4149                                 nbuf = alloc(nlen + 1 + olen, AEDIT);
4150                                 memcpy(nbuf, ap->val.s, nlen);
4151                                 nbuf[nlen++] = cmd[1];
4152                                 if (macro.p) {
4153                                         memcpy(nbuf + nlen, macro.p, olen);
4154                                         afree(macro.buf, AEDIT);
4155                                         nlen += olen;
4156                                 } else {
4157                                         nbuf[nlen++] = '\0';
4158                                         nbuf[nlen++] = '\0';
4159                                 }
4160                                 macro.p = macro.buf = (unsigned char *)nbuf;
4161                                 macro.len = nlen;
4162                         }
4163                         break;
4164
4165                 case 'a':
4166                         modified = 1;
4167                         hnum = hlast;
4168                         if (es->linelen != 0)
4169                                 es->cursor++;
4170                         insert = INSERT;
4171                         break;
4172
4173                 case 'A':
4174                         modified = 1;
4175                         hnum = hlast;
4176                         del_range(0, 0);
4177                         es->cursor = es->linelen;
4178                         insert = INSERT;
4179                         break;
4180
4181                 case 'S':
4182                         es->cursor = domove(1, "^", 1);
4183                         del_range(es->cursor, es->linelen);
4184                         modified = 1;
4185                         hnum = hlast;
4186                         insert = INSERT;
4187                         break;
4188
4189                 case 'Y':
4190                         cmd = "y$";
4191                         /* ahhhhhh... */
4192                 case 'c':
4193                 case 'd':
4194                 case 'y':
4195                         if (*cmd == cmd[1]) {
4196                                 c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
4197                                 c2 = es->linelen;
4198                         } else if (!is_move(cmd[1]))
4199                                 return (-1);
4200                         else {
4201                                 if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
4202                                         return (-1);
4203                                 if (*cmd == 'c' &&
4204                                     (cmd[1] == 'w' || cmd[1] == 'W') &&
4205                                     !ksh_isspace(es->cbuf[es->cursor])) {
4206                                         do {
4207                                                 --ncursor;
4208                                         } while (ksh_isspace(es->cbuf[ncursor]));
4209                                         ncursor++;
4210                                 }
4211                                 if (ncursor > es->cursor) {
4212                                         c1 = es->cursor;
4213                                         c2 = ncursor;
4214                                 } else {
4215                                         c1 = ncursor;
4216                                         c2 = es->cursor;
4217                                         if (cmd[1] == '%')
4218                                                 c2++;
4219                                 }
4220                         }
4221                         if (*cmd != 'c' && c1 != c2)
4222                                 yank_range(c1, c2);
4223                         if (*cmd != 'y') {
4224                                 del_range(c1, c2);
4225                                 es->cursor = c1;
4226                         }
4227                         if (*cmd == 'c') {
4228                                 modified = 1;
4229                                 hnum = hlast;
4230                                 insert = INSERT;
4231                         }
4232                         break;
4233
4234                 case 'p':
4235                         modified = 1;
4236                         hnum = hlast;
4237                         if (es->linelen != 0)
4238                                 es->cursor++;
4239                         while (putbuf(ybuf, yanklen, false) == 0 &&
4240                             --argcnt > 0)
4241                                 ;
4242                         if (es->cursor != 0)
4243                                 es->cursor--;
4244                         if (argcnt != 0)
4245                                 return (-1);
4246                         break;
4247
4248                 case 'P':
4249                         modified = 1;
4250                         hnum = hlast;
4251                         any = 0;
4252                         while (putbuf(ybuf, yanklen, false) == 0 &&
4253                             --argcnt > 0)
4254                                 any = 1;
4255                         if (any && es->cursor != 0)
4256                                 es->cursor--;
4257                         if (argcnt != 0)
4258                                 return (-1);
4259                         break;
4260
4261                 case 'C':
4262                         modified = 1;
4263                         hnum = hlast;
4264                         del_range(es->cursor, es->linelen);
4265                         insert = INSERT;
4266                         break;
4267
4268                 case 'D':
4269                         yank_range(es->cursor, es->linelen);
4270                         del_range(es->cursor, es->linelen);
4271                         if (es->cursor != 0)
4272                                 es->cursor--;
4273                         break;
4274
4275                 case 'g':
4276                         if (!argcnt)
4277                                 argcnt = hlast;
4278                         /* FALLTHROUGH */
4279                 case 'G':
4280                         if (!argcnt)
4281                                 argcnt = 1;
4282                         else
4283                                 argcnt = hlast - (source->line - argcnt);
4284                         if (grabhist(modified, argcnt - 1) < 0)
4285                                 return (-1);
4286                         else {
4287                                 modified = 0;
4288                                 hnum = argcnt - 1;
4289                         }
4290                         break;
4291
4292                 case 'i':
4293                         modified = 1;
4294                         hnum = hlast;
4295                         insert = INSERT;
4296                         break;
4297
4298                 case 'I':
4299                         modified = 1;
4300                         hnum = hlast;
4301                         es->cursor = domove(1, "^", 1);
4302                         insert = INSERT;
4303                         break;
4304
4305                 case 'j':
4306                 case '+':
4307                 case CTRL('n'):
4308                         if (grabhist(modified, hnum + argcnt) < 0)
4309                                 return (-1);
4310                         else {
4311                                 modified = 0;
4312                                 hnum += argcnt;
4313                         }
4314                         break;
4315
4316                 case 'k':
4317                 case '-':
4318                 case CTRL('p'):
4319                         if (grabhist(modified, hnum - argcnt) < 0)
4320                                 return (-1);
4321                         else {
4322                                 modified = 0;
4323                                 hnum -= argcnt;
4324                         }
4325                         break;
4326
4327                 case 'r':
4328                         if (es->linelen == 0)
4329                                 return (-1);
4330                         modified = 1;
4331                         hnum = hlast;
4332                         if (cmd[1] == 0)
4333                                 vi_error();
4334                         else {
4335                                 int n;
4336
4337                                 if (es->cursor + argcnt > es->linelen)
4338                                         return (-1);
4339                                 for (n = 0; n < argcnt; ++n)
4340                                         es->cbuf[es->cursor + n] = cmd[1];
4341                                 es->cursor += n - 1;
4342                         }
4343                         break;
4344
4345                 case 'R':
4346                         modified = 1;
4347                         hnum = hlast;
4348                         insert = REPLACE;
4349                         break;
4350
4351                 case 's':
4352                         if (es->linelen == 0)
4353                                 return (-1);
4354                         modified = 1;
4355                         hnum = hlast;
4356                         if (es->cursor + argcnt > es->linelen)
4357                                 argcnt = es->linelen - es->cursor;
4358                         del_range(es->cursor, es->cursor + argcnt);
4359                         insert = INSERT;
4360                         break;
4361
4362                 case 'v':
4363                         if (!argcnt) {
4364                                 if (es->linelen == 0)
4365                                         return (-1);
4366                                 if (modified) {
4367                                         es->cbuf[es->linelen] = '\0';
4368                                         histsave(&source->line, es->cbuf,
4369                                             HIST_STORE, true);
4370                                 } else
4371                                         argcnt = source->line + 1 -
4372                                             (hlast - hnum);
4373                         }
4374                         if (argcnt)
4375                                 shf_snprintf(es->cbuf, es->cbufsize, "%s %d",
4376                                     "fc -e ${VISUAL:-${EDITOR:-vi}} --",
4377                                     argcnt);
4378                         else
4379                                 strlcpy(es->cbuf,
4380                                     "fc -e ${VISUAL:-${EDITOR:-vi}} --",
4381                                     es->cbufsize);
4382                         es->linelen = strlen(es->cbuf);
4383                         return (2);
4384
4385                 case 'x':
4386                         if (es->linelen == 0)
4387                                 return (-1);
4388                         modified = 1;
4389                         hnum = hlast;
4390                         if (es->cursor + argcnt > es->linelen)
4391                                 argcnt = es->linelen - es->cursor;
4392                         yank_range(es->cursor, es->cursor + argcnt);
4393                         del_range(es->cursor, es->cursor + argcnt);
4394                         break;
4395
4396                 case 'X':
4397                         if (es->cursor > 0) {
4398                                 modified = 1;
4399                                 hnum = hlast;
4400                                 if (es->cursor < argcnt)
4401                                         argcnt = es->cursor;
4402                                 yank_range(es->cursor - argcnt, es->cursor);
4403                                 del_range(es->cursor - argcnt, es->cursor);
4404                                 es->cursor -= argcnt;
4405                         } else
4406                                 return (-1);
4407                         break;
4408
4409                 case 'u':
4410                         t = es;
4411                         es = undo;
4412                         undo = t;
4413                         break;
4414
4415                 case 'U':
4416                         if (!modified)
4417                                 return (-1);
4418                         if (grabhist(modified, ohnum) < 0)
4419                                 return (-1);
4420                         modified = 0;
4421                         hnum = ohnum;
4422                         break;
4423
4424                 case '?':
4425                         if (hnum == hlast)
4426                                 hnum = -1;
4427                         /* ahhh */
4428                 case '/':
4429                         c3 = 1;
4430                         srchlen = 0;
4431                         lastsearch = *cmd;
4432                         /* FALLTHROUGH */
4433                 case 'n':
4434                 case 'N':
4435                         if (lastsearch == ' ')
4436                                 return (-1);
4437                         if (lastsearch == '?')
4438                                 c1 = 1;
4439                         else
4440                                 c1 = 0;
4441                         if (*cmd == 'N')
4442                                 c1 = !c1;
4443                         if ((c2 = grabsearch(modified, hnum,
4444                             c1, srchpat)) < 0) {
4445                                 if (c3) {
4446                                         restore_cbuf();
4447                                         refresh(0);
4448                                 }
4449                                 return (-1);
4450                         } else {
4451                                 modified = 0;
4452                                 hnum = c2;
4453                                 ohnum = hnum;
4454                         }
4455                         if (argcnt >= 2) {
4456                                 /* flag from cursor-up command */
4457                                 es->cursor = argcnt - 2;
4458                                 return (0);
4459                         }
4460                         break;
4461                 case '_':
4462                         {
4463                                 bool inspace;
4464                                 char *p, *sp;
4465
4466                                 if (histnum(-1) < 0)
4467                                         return (-1);
4468                                 p = *histpos();
4469 #define issp(c)         (ksh_isspace(c) || (c) == '\n')
4470                                 if (argcnt) {
4471                                         while (*p && issp(*p))
4472                                                 p++;
4473                                         while (*p && --argcnt) {
4474                                                 while (*p && !issp(*p))
4475                                                         p++;
4476                                                 while (*p && issp(*p))
4477                                                         p++;
4478                                         }
4479                                         if (!*p)
4480                                                 return (-1);
4481                                         sp = p;
4482                                 } else {
4483                                         sp = p;
4484                                         inspace = false;
4485                                         while (*p) {
4486                                                 if (issp(*p))
4487                                                         inspace = true;
4488                                                 else if (inspace) {
4489                                                         inspace = false;
4490                                                         sp = p;
4491                                                 }
4492                                                 p++;
4493                                         }
4494                                         p = sp;
4495                                 }
4496                                 modified = 1;
4497                                 hnum = hlast;
4498                                 if (es->cursor != es->linelen)
4499                                         es->cursor++;
4500                                 while (*p && !issp(*p)) {
4501                                         argcnt++;
4502                                         p++;
4503                                 }
4504                                 if (putbuf(" ", 1, false) != 0 ||
4505                                     putbuf(sp, argcnt, false) != 0) {
4506                                         if (es->cursor != 0)
4507                                                 es->cursor--;
4508                                         return (-1);
4509                                 }
4510                                 insert = INSERT;
4511                         }
4512                         break;
4513
4514                 case '~':
4515                         {
4516                                 char *p;
4517                                 int i;
4518
4519                                 if (es->linelen == 0)
4520                                         return (-1);
4521                                 for (i = 0; i < argcnt; i++) {
4522                                         p = &es->cbuf[es->cursor];
4523                                         if (ksh_islower(*p)) {
4524                                                 modified = 1;
4525                                                 hnum = hlast;
4526                                                 *p = ksh_toupper(*p);
4527                                         } else if (ksh_isupper(*p)) {
4528                                                 modified = 1;
4529                                                 hnum = hlast;
4530                                                 *p = ksh_tolower(*p);
4531                                         }
4532                                         if (es->cursor < es->linelen - 1)
4533                                                 es->cursor++;
4534                                 }
4535                                 break;
4536                         }
4537
4538                 case '#':
4539                         {
4540                                 int ret = x_do_comment(es->cbuf, es->cbufsize,
4541                                     &es->linelen);
4542                                 if (ret >= 0)
4543                                         es->cursor = 0;
4544                                 return (ret);
4545                         }
4546
4547                 /* AT&T ksh */
4548                 case '=':
4549                 /* Nonstandard vi/ksh */
4550                 case CTRL('e'):
4551                         print_expansions(es, 1);
4552                         break;
4553
4554
4555                 /* Nonstandard vi/ksh */
4556                 case CTRL('i'):
4557                         if (!Flag(FVITABCOMPLETE))
4558                                 return (-1);
4559                         complete_word(1, argcnt);
4560                         break;
4561
4562                 /* some annoying AT&T kshs */
4563                 case CTRL('['):
4564                         if (!Flag(FVIESCCOMPLETE))
4565                                 return (-1);
4566                 /* AT&T ksh */
4567                 case '\\':
4568                 /* Nonstandard vi/ksh */
4569                 case CTRL('f'):
4570                         complete_word(1, argcnt);
4571                         break;
4572
4573
4574                 /* AT&T ksh */
4575                 case '*':
4576                 /* Nonstandard vi/ksh */
4577                 case CTRL('x'):
4578                         expand_word(1);
4579                         break;
4580
4581
4582                 /* mksh: cursor movement */
4583                 case '[':
4584                 case 'O':
4585                         state = VPREFIX2;
4586                         if (es->linelen != 0)
4587                                 es->cursor++;
4588                         insert = INSERT;
4589                         return (0);
4590                 }
4591                 if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen)
4592                         es->cursor--;
4593         }
4594         return (0);
4595 }
4596
4597 static int
4598 domove(int argcnt, const char *cmd, int sub)
4599 {
4600         int bcount, i = 0, t;
4601         int ncursor = 0;
4602
4603         switch (*cmd) {
4604         case 'b':
4605                 if (!sub && es->cursor == 0)
4606                         return (-1);
4607                 ncursor = backword(argcnt);
4608                 break;
4609
4610         case 'B':
4611                 if (!sub && es->cursor == 0)
4612                         return (-1);
4613                 ncursor = Backword(argcnt);
4614                 break;
4615
4616         case 'e':
4617                 if (!sub && es->cursor + 1 >= es->linelen)
4618                         return (-1);
4619                 ncursor = endword(argcnt);
4620                 if (sub && ncursor < es->linelen)
4621                         ncursor++;
4622                 break;
4623
4624         case 'E':
4625                 if (!sub && es->cursor + 1 >= es->linelen)
4626                         return (-1);
4627                 ncursor = Endword(argcnt);
4628                 if (sub && ncursor < es->linelen)
4629                         ncursor++;
4630                 break;
4631
4632         case 'f':
4633         case 'F':
4634         case 't':
4635         case 'T':
4636                 fsavecmd = *cmd;
4637                 fsavech = cmd[1];
4638                 /* drop through */
4639
4640         case ',':
4641         case ';':
4642                 if (fsavecmd == ' ')
4643                         return (-1);
4644                 i = fsavecmd == 'f' || fsavecmd == 'F';
4645                 t = fsavecmd > 'a';
4646                 if (*cmd == ',')
4647                         t = !t;
4648                 if ((ncursor = findch(fsavech, argcnt, tobool(t),
4649                     tobool(i))) < 0)
4650                         return (-1);
4651                 if (sub && t)
4652                         ncursor++;
4653                 break;
4654
4655         case 'h':
4656         case CTRL('h'):
4657                 if (!sub && es->cursor == 0)
4658                         return (-1);
4659                 ncursor = es->cursor - argcnt;
4660                 if (ncursor < 0)
4661                         ncursor = 0;
4662                 break;
4663
4664         case ' ':
4665         case 'l':
4666                 if (!sub && es->cursor + 1 >= es->linelen)
4667                         return (-1);
4668                 if (es->linelen != 0) {
4669                         ncursor = es->cursor + argcnt;
4670                         if (ncursor > es->linelen)
4671                                 ncursor = es->linelen;
4672                 }
4673                 break;
4674
4675         case 'w':
4676                 if (!sub && es->cursor + 1 >= es->linelen)
4677                         return (-1);
4678                 ncursor = forwword(argcnt);
4679                 break;
4680
4681         case 'W':
4682                 if (!sub && es->cursor + 1 >= es->linelen)
4683                         return (-1);
4684                 ncursor = Forwword(argcnt);
4685                 break;
4686
4687         case '0':
4688                 ncursor = 0;
4689                 break;
4690
4691         case '^':
4692                 ncursor = 0;
4693                 while (ncursor < es->linelen - 1 &&
4694                     ksh_isspace(es->cbuf[ncursor]))
4695                         ncursor++;
4696                 break;
4697
4698         case '|':
4699                 ncursor = argcnt;
4700                 if (ncursor > es->linelen)
4701                         ncursor = es->linelen;
4702                 if (ncursor)
4703                         ncursor--;
4704                 break;
4705
4706         case '$':
4707                 if (es->linelen != 0)
4708                         ncursor = es->linelen;
4709                 else
4710                         ncursor = 0;
4711                 break;
4712
4713         case '%':
4714                 ncursor = es->cursor;
4715                 while (ncursor < es->linelen &&
4716                     (i = bracktype(es->cbuf[ncursor])) == 0)
4717                         ncursor++;
4718                 if (ncursor == es->linelen)
4719                         return (-1);
4720                 bcount = 1;
4721                 do {
4722                         if (i > 0) {
4723                                 if (++ncursor >= es->linelen)
4724                                         return (-1);
4725                         } else {
4726                                 if (--ncursor < 0)
4727                                         return (-1);
4728                         }
4729                         t = bracktype(es->cbuf[ncursor]);
4730                         if (t == i)
4731                                 bcount++;
4732                         else if (t == -i)
4733                                 bcount--;
4734                 } while (bcount != 0);
4735                 if (sub && i > 0)
4736                         ncursor++;
4737                 break;
4738
4739         default:
4740                 return (-1);
4741         }
4742         return (ncursor);
4743 }
4744
4745 static int
4746 redo_insert(int count)
4747 {
4748         while (count-- > 0)
4749                 if (putbuf(ibuf, inslen, tobool(insert == REPLACE)) != 0)
4750                         return (-1);
4751         if (es->cursor > 0)
4752                 es->cursor--;
4753         insert = 0;
4754         return (0);
4755 }
4756
4757 static void
4758 yank_range(int a, int b)
4759 {
4760         yanklen = b - a;
4761         if (yanklen != 0)
4762                 memmove(ybuf, &es->cbuf[a], yanklen);
4763 }
4764
4765 static int
4766 bracktype(int ch)
4767 {
4768         switch (ch) {
4769
4770         case '(':
4771                 return (1);
4772
4773         case '[':
4774                 return (2);
4775
4776         case '{':
4777                 return (3);
4778
4779         case ')':
4780                 return (-1);
4781
4782         case ']':
4783                 return (-2);
4784
4785         case '}':
4786                 return (-3);
4787
4788         default:
4789                 return (0);
4790         }
4791 }
4792
4793 /*
4794  *      Non user interface editor routines below here
4795  */
4796
4797 static void
4798 save_cbuf(void)
4799 {
4800         memmove(holdbufp, es->cbuf, es->linelen);
4801         holdlen = es->linelen;
4802         holdbufp[holdlen] = '\0';
4803 }
4804
4805 static void
4806 restore_cbuf(void)
4807 {
4808         es->cursor = 0;
4809         es->linelen = holdlen;
4810         memmove(es->cbuf, holdbufp, holdlen);
4811 }
4812
4813 /* return a new edstate */
4814 static struct edstate *
4815 save_edstate(struct edstate *old)
4816 {
4817         struct edstate *news;
4818
4819         news = alloc(sizeof(struct edstate), AEDIT);
4820         news->cbuf = alloc(old->cbufsize, AEDIT);
4821         memcpy(news->cbuf, old->cbuf, old->linelen);
4822         news->cbufsize = old->cbufsize;
4823         news->linelen = old->linelen;
4824         news->cursor = old->cursor;
4825         news->winleft = old->winleft;
4826         return (news);
4827 }
4828
4829 static void
4830 restore_edstate(struct edstate *news, struct edstate *old)
4831 {
4832         memcpy(news->cbuf, old->cbuf, old->linelen);
4833         news->linelen = old->linelen;
4834         news->cursor = old->cursor;
4835         news->winleft = old->winleft;
4836         free_edstate(old);
4837 }
4838
4839 static void
4840 free_edstate(struct edstate *old)
4841 {
4842         afree(old->cbuf, AEDIT);
4843         afree(old, AEDIT);
4844 }
4845
4846 /*
4847  * this is used for calling x_escape() in complete_word()
4848  */
4849 static int
4850 x_vi_putbuf(const char *s, size_t len)
4851 {
4852         return (putbuf(s, len, false));
4853 }
4854
4855 static int
4856 putbuf(const char *buf, ssize_t len, bool repl)
4857 {
4858         if (len == 0)
4859                 return (0);
4860         if (repl) {
4861                 if (es->cursor + len >= es->cbufsize)
4862                         return (-1);
4863                 if (es->cursor + len > es->linelen)
4864                         es->linelen = es->cursor + len;
4865         } else {
4866                 if (es->linelen + len >= es->cbufsize)
4867                         return (-1);
4868                 memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor],
4869                     es->linelen - es->cursor);
4870                 es->linelen += len;
4871         }
4872         memmove(&es->cbuf[es->cursor], buf, len);
4873         es->cursor += len;
4874         return (0);
4875 }
4876
4877 static void
4878 del_range(int a, int b)
4879 {
4880         if (es->linelen != b)
4881                 memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b);
4882         es->linelen -= b - a;
4883 }
4884
4885 static int
4886 findch(int ch, int cnt, bool forw, bool incl)
4887 {
4888         int ncursor;
4889
4890         if (es->linelen == 0)
4891                 return (-1);
4892         ncursor = es->cursor;
4893         while (cnt--) {
4894                 do {
4895                         if (forw) {
4896                                 if (++ncursor == es->linelen)
4897                                         return (-1);
4898                         } else {
4899                                 if (--ncursor < 0)
4900                                         return (-1);
4901                         }
4902                 } while (es->cbuf[ncursor] != ch);
4903         }
4904         if (!incl) {
4905                 if (forw)
4906                         ncursor--;
4907                 else
4908                         ncursor++;
4909         }
4910         return (ncursor);
4911 }
4912
4913 static int
4914 forwword(int argcnt)
4915 {
4916         int ncursor;
4917
4918         ncursor = es->cursor;
4919         while (ncursor < es->linelen && argcnt--) {
4920                 if (ksh_isalnux(es->cbuf[ncursor]))
4921                         while (ksh_isalnux(es->cbuf[ncursor]) &&
4922                             ncursor < es->linelen)
4923                                 ncursor++;
4924                 else if (!ksh_isspace(es->cbuf[ncursor]))
4925                         while (!ksh_isalnux(es->cbuf[ncursor]) &&
4926                             !ksh_isspace(es->cbuf[ncursor]) &&
4927                             ncursor < es->linelen)
4928                                 ncursor++;
4929                 while (ksh_isspace(es->cbuf[ncursor]) &&
4930                     ncursor < es->linelen)
4931                         ncursor++;
4932         }
4933         return (ncursor);
4934 }
4935
4936 static int
4937 backword(int argcnt)
4938 {
4939         int ncursor;
4940
4941         ncursor = es->cursor;
4942         while (ncursor > 0 && argcnt--) {
4943                 while (--ncursor > 0 && ksh_isspace(es->cbuf[ncursor]))
4944                         ;
4945                 if (ncursor > 0) {
4946                         if (ksh_isalnux(es->cbuf[ncursor]))
4947                                 while (--ncursor >= 0 &&
4948                                     ksh_isalnux(es->cbuf[ncursor]))
4949                                         ;
4950                         else
4951                                 while (--ncursor >= 0 &&
4952                                     !ksh_isalnux(es->cbuf[ncursor]) &&
4953                                     !ksh_isspace(es->cbuf[ncursor]))
4954                                         ;
4955                         ncursor++;
4956                 }
4957         }
4958         return (ncursor);
4959 }
4960
4961 static int
4962 endword(int argcnt)
4963 {
4964         int ncursor;
4965
4966         ncursor = es->cursor;
4967         while (ncursor < es->linelen && argcnt--) {
4968                 while (++ncursor < es->linelen - 1 &&
4969                     ksh_isspace(es->cbuf[ncursor]))
4970                         ;
4971                 if (ncursor < es->linelen - 1) {
4972                         if (ksh_isalnux(es->cbuf[ncursor]))
4973                                 while (++ncursor < es->linelen &&
4974                                     ksh_isalnux(es->cbuf[ncursor]))
4975                                         ;
4976                         else
4977                                 while (++ncursor < es->linelen &&
4978                                     !ksh_isalnux(es->cbuf[ncursor]) &&
4979                                     !ksh_isspace(es->cbuf[ncursor]))
4980                                         ;
4981                         ncursor--;
4982                 }
4983         }
4984         return (ncursor);
4985 }
4986
4987 static int
4988 Forwword(int argcnt)
4989 {
4990         int ncursor;
4991
4992         ncursor = es->cursor;
4993         while (ncursor < es->linelen && argcnt--) {
4994                 while (!ksh_isspace(es->cbuf[ncursor]) &&
4995                     ncursor < es->linelen)
4996                         ncursor++;
4997                 while (ksh_isspace(es->cbuf[ncursor]) &&
4998                     ncursor < es->linelen)
4999                         ncursor++;
5000         }
5001         return (ncursor);
5002 }
5003
5004 static int
5005 Backword(int argcnt)
5006 {
5007         int ncursor;
5008
5009         ncursor = es->cursor;
5010         while (ncursor > 0 && argcnt--) {
5011                 while (--ncursor >= 0 && ksh_isspace(es->cbuf[ncursor]))
5012                         ;
5013                 while (ncursor >= 0 && !ksh_isspace(es->cbuf[ncursor]))
5014                         ncursor--;
5015                 ncursor++;
5016         }
5017         return (ncursor);
5018 }
5019
5020 static int
5021 Endword(int argcnt)
5022 {
5023         int ncursor;
5024
5025         ncursor = es->cursor;
5026         while (ncursor < es->linelen - 1 && argcnt--) {
5027                 while (++ncursor < es->linelen - 1 &&
5028                     ksh_isspace(es->cbuf[ncursor]))
5029                         ;
5030                 if (ncursor < es->linelen - 1) {
5031                         while (++ncursor < es->linelen &&
5032                             !ksh_isspace(es->cbuf[ncursor]))
5033                                 ;
5034                         ncursor--;
5035                 }
5036         }
5037         return (ncursor);
5038 }
5039
5040 static int
5041 grabhist(int save, int n)
5042 {
5043         char *hptr;
5044
5045         if (n < 0 || n > hlast)
5046                 return (-1);
5047         if (n == hlast) {
5048                 restore_cbuf();
5049                 ohnum = n;
5050                 return (0);
5051         }
5052         (void)histnum(n);
5053         if ((hptr = *histpos()) == NULL) {
5054                 internal_warningf("%s: %s", "grabhist", "bad history array");
5055                 return (-1);
5056         }
5057         if (save)
5058                 save_cbuf();
5059         if ((es->linelen = strlen(hptr)) >= es->cbufsize)
5060                 es->linelen = es->cbufsize - 1;
5061         memmove(es->cbuf, hptr, es->linelen);
5062         es->cursor = 0;
5063         ohnum = n;
5064         return (0);
5065 }
5066
5067 static int
5068 grabsearch(int save, int start, int fwd, const char *pat)
5069 {
5070         char *hptr;
5071         int hist;
5072         bool anchored;
5073
5074         if ((start == 0 && fwd == 0) || (start >= hlast - 1 && fwd == 1))
5075                 return (-1);
5076         if (fwd)
5077                 start++;
5078         else
5079                 start--;
5080         anchored = *pat == '^' ? (++pat, true) : false;
5081         if ((hist = findhist(start, fwd, pat, anchored)) < 0) {
5082                 /* (start != 0 && fwd && match(holdbufp, pat) >= 0) */
5083                 if (start != 0 && fwd && strcmp(holdbufp, pat) >= 0) {
5084                         restore_cbuf();
5085                         return (0);
5086                 } else
5087                         return (-1);
5088         }
5089         if (save)
5090                 save_cbuf();
5091         histnum(hist);
5092         hptr = *histpos();
5093         if ((es->linelen = strlen(hptr)) >= es->cbufsize)
5094                 es->linelen = es->cbufsize - 1;
5095         memmove(es->cbuf, hptr, es->linelen);
5096         es->cursor = 0;
5097         return (hist);
5098 }
5099
5100 static void
5101 redraw_line(bool newl)
5102 {
5103         if (wbuf_len)
5104                 memset(wbuf[win], ' ', wbuf_len);
5105         if (newl) {
5106                 x_putc('\r');
5107                 x_putc('\n');
5108         }
5109         if (prompt_trunc != -1)
5110                 pprompt(prompt, prompt_trunc);
5111         x_col = pwidth;
5112         morec = ' ';
5113 }
5114
5115 static void
5116 refresh(int leftside)
5117 {
5118         if (leftside < 0)
5119                 leftside = lastref;
5120         else
5121                 lastref = leftside;
5122         if (outofwin())
5123                 rewindow();
5124         display(wbuf[1 - win], wbuf[win], leftside);
5125         win = 1 - win;
5126 }
5127
5128 static int
5129 outofwin(void)
5130 {
5131         int cur, col;
5132
5133         if (es->cursor < es->winleft)
5134                 return (1);
5135         col = 0;
5136         cur = es->winleft;
5137         while (cur < es->cursor)
5138                 col = newcol((unsigned char)es->cbuf[cur++], col);
5139         if (col >= winwidth)
5140                 return (1);
5141         return (0);
5142 }
5143
5144 static void
5145 rewindow(void)
5146 {
5147         int tcur, tcol;
5148         int holdcur1, holdcol1;
5149         int holdcur2, holdcol2;
5150
5151         holdcur1 = holdcur2 = tcur = 0;
5152         holdcol1 = holdcol2 = tcol = 0;
5153         while (tcur < es->cursor) {
5154                 if (tcol - holdcol2 > winwidth / 2) {
5155                         holdcur1 = holdcur2;
5156                         holdcol1 = holdcol2;
5157                         holdcur2 = tcur;
5158                         holdcol2 = tcol;
5159                 }
5160                 tcol = newcol((unsigned char)es->cbuf[tcur++], tcol);
5161         }
5162         while (tcol - holdcol1 > winwidth / 2)
5163                 holdcol1 = newcol((unsigned char)es->cbuf[holdcur1++],
5164                     holdcol1);
5165         es->winleft = holdcur1;
5166 }
5167
5168 static int
5169 newcol(unsigned char ch, int col)
5170 {
5171         if (ch == '\t')
5172                 return ((col | 7) + 1);
5173         return (col + char_len(ch));
5174 }
5175
5176 static void
5177 display(char *wb1, char *wb2, int leftside)
5178 {
5179         unsigned char ch;
5180         char *twb1, *twb2, mc;
5181         int cur, col, cnt;
5182         int ncol = 0;
5183         int moreright;
5184
5185         col = 0;
5186         cur = es->winleft;
5187         moreright = 0;
5188         twb1 = wb1;
5189         while (col < winwidth && cur < es->linelen) {
5190                 if (cur == es->cursor && leftside)
5191                         ncol = col + pwidth;
5192                 if ((ch = es->cbuf[cur]) == '\t')
5193                         do {
5194                                 *twb1++ = ' ';
5195                         } while (++col < winwidth && (col & 7) != 0);
5196                 else if (col < winwidth) {
5197                         if (ISCTRL(ch) && /* but not C1 */ ch < 0x80) {
5198                                 *twb1++ = '^';
5199                                 if (++col < winwidth) {
5200                                         *twb1++ = UNCTRL(ch);
5201                                         col++;
5202                                 }
5203                         } else {
5204                                 *twb1++ = ch;
5205                                 col++;
5206                         }
5207                 }
5208                 if (cur == es->cursor && !leftside)
5209                         ncol = col + pwidth - 1;
5210                 cur++;
5211         }
5212         if (cur == es->cursor)
5213                 ncol = col + pwidth;
5214         if (col < winwidth) {
5215                 while (col < winwidth) {
5216                         *twb1++ = ' ';
5217                         col++;
5218                 }
5219         } else
5220                 moreright++;
5221         *twb1 = ' ';
5222
5223         col = pwidth;
5224         cnt = winwidth;
5225         twb1 = wb1;
5226         twb2 = wb2;
5227         while (cnt--) {
5228                 if (*twb1 != *twb2) {
5229                         if (x_col != col)
5230                                 ed_mov_opt(col, wb1);
5231                         x_putc(*twb1);
5232                         x_col++;
5233                 }
5234                 twb1++;
5235                 twb2++;
5236                 col++;
5237         }
5238         if (es->winleft > 0 && moreright)
5239                 /*
5240                  * POSIX says to use * for this but that is a globbing
5241                  * character and may confuse people; + is more innocuous
5242                  */
5243                 mc = '+';
5244         else if (es->winleft > 0)
5245                 mc = '<';
5246         else if (moreright)
5247                 mc = '>';
5248         else
5249                 mc = ' ';
5250         if (mc != morec) {
5251                 ed_mov_opt(pwidth + winwidth + 1, wb1);
5252                 x_putc(mc);
5253                 x_col++;
5254                 morec = mc;
5255         }
5256         if (x_col != ncol)
5257                 ed_mov_opt(ncol, wb1);
5258 }
5259
5260 static void
5261 ed_mov_opt(int col, char *wb)
5262 {
5263         if (col < x_col) {
5264                 if (col + 1 < x_col - col) {
5265                         x_putc('\r');
5266                         if (prompt_trunc != -1)
5267                                 pprompt(prompt, prompt_trunc);
5268                         x_col = pwidth;
5269                         while (x_col++ < col)
5270                                 x_putcf(*wb++);
5271                 } else {
5272                         while (x_col-- > col)
5273                                 x_putc('\b');
5274                 }
5275         } else {
5276                 wb = &wb[x_col - pwidth];
5277                 while (x_col++ < col)
5278                         x_putcf(*wb++);
5279         }
5280         x_col = col;
5281 }
5282
5283
5284 /* replace word with all expansions (ie, expand word*) */
5285 static int
5286 expand_word(int cmd)
5287 {
5288         static struct edstate *buf;
5289         int rval = 0, nwords, start, end, i;
5290         char **words;
5291
5292         /* Undo previous expansion */
5293         if (cmd == 0 && expanded == EXPAND && buf) {
5294                 restore_edstate(es, buf);
5295                 buf = 0;
5296                 expanded = NONE;
5297                 return (0);
5298         }
5299         if (buf) {
5300                 free_edstate(buf);
5301                 buf = 0;
5302         }
5303
5304         i = XCF_COMMAND_FILE | XCF_FULLPATH;
5305         nwords = x_cf_glob(&i, es->cbuf, es->linelen, es->cursor,
5306             &start, &end, &words);
5307         if (nwords == 0) {
5308                 vi_error();
5309                 return (-1);
5310         }
5311
5312         buf = save_edstate(es);
5313         expanded = EXPAND;
5314         del_range(start, end);
5315         es->cursor = start;
5316         i = 0;
5317         while (i < nwords) {
5318                 if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
5319                         rval = -1;
5320                         break;
5321                 }
5322                 if (++i < nwords && putbuf(" ", 1, false) != 0) {
5323                         rval = -1;
5324                         break;
5325                 }
5326         }
5327         i = buf->cursor - end;
5328         if (rval == 0 && i > 0)
5329                 es->cursor += i;
5330         modified = 1;
5331         hnum = hlast;
5332         insert = INSERT;
5333         lastac = 0;
5334         refresh(0);
5335         return (rval);
5336 }
5337
5338 static int
5339 complete_word(int cmd, int count)
5340 {
5341         static struct edstate *buf;
5342         int rval, nwords, start, end, flags;
5343         size_t match_len;
5344         char **words;
5345         char *match;
5346         bool is_unique;
5347
5348         /* Undo previous completion */
5349         if (cmd == 0 && expanded == COMPLETE && buf) {
5350                 print_expansions(buf, 0);
5351                 expanded = PRINT;
5352                 return (0);
5353         }
5354         if (cmd == 0 && expanded == PRINT && buf) {
5355                 restore_edstate(es, buf);
5356                 buf = 0;
5357                 expanded = NONE;
5358                 return (0);
5359         }
5360         if (buf) {
5361                 free_edstate(buf);
5362                 buf = 0;
5363         }
5364
5365         /*
5366          * XCF_FULLPATH for count 'cause the menu printed by
5367          * print_expansions() was done this way.
5368          */
5369         flags = XCF_COMMAND_FILE;
5370         if (count)
5371                 flags |= XCF_FULLPATH;
5372         nwords = x_cf_glob(&flags, es->cbuf, es->linelen, es->cursor,
5373             &start, &end, &words);
5374         if (nwords == 0) {
5375                 vi_error();
5376                 return (-1);
5377         }
5378         if (count) {
5379                 int i;
5380
5381                 count--;
5382                 if (count >= nwords) {
5383                         vi_error();
5384                         x_print_expansions(nwords, words,
5385                             tobool(flags & XCF_IS_COMMAND));
5386                         x_free_words(nwords, words);
5387                         redraw_line(false);
5388                         return (-1);
5389                 }
5390                 /*
5391                  * Expand the count'th word to its basename
5392                  */
5393                 if (flags & XCF_IS_COMMAND) {
5394                         match = words[count] +
5395                             x_basename(words[count], NULL);
5396                         /* If more than one possible match, use full path */
5397                         for (i = 0; i < nwords; i++)
5398                                 if (i != count &&
5399                                     strcmp(words[i] + x_basename(words[i],
5400                                     NULL), match) == 0) {
5401                                         match = words[count];
5402                                         break;
5403                                 }
5404                 } else
5405                         match = words[count];
5406                 match_len = strlen(match);
5407                 is_unique = true;
5408                 /* expanded = PRINT;    next call undo */
5409         } else {
5410                 match = words[0];
5411                 match_len = x_longest_prefix(nwords, words);
5412                 /* next call will list completions */
5413                 expanded = COMPLETE;
5414                 is_unique = nwords == 1;
5415         }
5416
5417         buf = save_edstate(es);
5418         del_range(start, end);
5419         es->cursor = start;
5420
5421         /*
5422          * escape all shell-sensitive characters and put the result into
5423          * command buffer
5424          */
5425         rval = x_escape(match, match_len, x_vi_putbuf);
5426
5427         if (rval == 0 && is_unique) {
5428                 /*
5429                  * If exact match, don't undo. Allows directory completions
5430                  * to be used (ie, complete the next portion of the path).
5431                  */
5432                 expanded = NONE;
5433
5434                 /*
5435                  * append a space if this is a non-directory match
5436                  * and not a parameter or homedir substitution
5437                  */
5438                 if (match_len > 0 && match[match_len - 1] != '/' &&
5439                     !(flags & XCF_IS_NOSPACE))
5440                         rval = putbuf(" ", 1, false);
5441         }
5442         x_free_words(nwords, words);
5443
5444         modified = 1;
5445         hnum = hlast;
5446         insert = INSERT;
5447         /* prevent this from being redone... */
5448         lastac = 0;
5449         refresh(0);
5450
5451         return (rval);
5452 }
5453
5454 static int
5455 print_expansions(struct edstate *est, int cmd MKSH_A_UNUSED)
5456 {
5457         int start, end, nwords, i;
5458         char **words;
5459
5460         i = XCF_COMMAND_FILE | XCF_FULLPATH;
5461         nwords = x_cf_glob(&i, est->cbuf, est->linelen, est->cursor,
5462             &start, &end, &words);
5463         if (nwords == 0) {
5464                 vi_error();
5465                 return (-1);
5466         }
5467         x_print_expansions(nwords, words, tobool(i & XCF_IS_COMMAND));
5468         x_free_words(nwords, words);
5469         redraw_line(false);
5470         return (0);
5471 }
5472
5473 /* Similar to x_zotc(emacs.c), but no tab weirdness */
5474 static void
5475 x_vi_zotc(int c)
5476 {
5477         if (ISCTRL(c)) {
5478                 x_putc('^');
5479                 c = UNCTRL(c);
5480         }
5481         x_putc(c);
5482 }
5483
5484 static void
5485 vi_error(void)
5486 {
5487         /* Beem out of any macros as soon as an error occurs */
5488         vi_macro_reset();
5489         x_putc(7);
5490         x_flush();
5491 }
5492
5493 static void
5494 vi_macro_reset(void)
5495 {
5496         if (macro.p) {
5497                 afree(macro.buf, AEDIT);
5498                 memset((char *)&macro, 0, sizeof(macro));
5499         }
5500 }
5501 #endif /* !MKSH_S_NOVI */
5502
5503 /* called from main.c */
5504 void
5505 x_init(void)
5506 {
5507         int i, j;
5508
5509         /*
5510          * Set edchars to -2 to force initial binding, except
5511          * we need default values for some deficient systems…
5512          */
5513         edchars.erase = edchars.kill = edchars.intr = edchars.quit =
5514             edchars.eof = -2;
5515         /* ^W */
5516         edchars.werase = 027;
5517
5518         /* command line editing specific memory allocation */
5519         ainit(AEDIT);
5520         holdbufp = alloc(LINE, AEDIT);
5521
5522         /* initialise Emacs command line editing mode */
5523         x_nextcmd = -1;
5524
5525         x_tab = alloc2(X_NTABS, sizeof(*x_tab), AEDIT);
5526         for (j = 0; j < X_TABSZ; j++)
5527                 x_tab[0][j] = XFUNC_insert;
5528         for (i = 1; i < X_NTABS; i++)
5529                 for (j = 0; j < X_TABSZ; j++)
5530                         x_tab[i][j] = XFUNC_error;
5531         for (i = 0; i < (int)NELEM(x_defbindings); i++)
5532                 x_tab[x_defbindings[i].xdb_tab][x_defbindings[i].xdb_char]
5533                     = x_defbindings[i].xdb_func;
5534
5535 #ifndef MKSH_SMALL
5536         x_atab = alloc2(X_NTABS, sizeof(*x_atab), AEDIT);
5537         for (i = 1; i < X_NTABS; i++)
5538                 for (j = 0; j < X_TABSZ; j++)
5539                         x_atab[i][j] = NULL;
5540 #endif
5541 }
5542
5543 #ifdef DEBUG_LEAKS
5544 void
5545 x_done(void)
5546 {
5547         if (x_tab != NULL)
5548                 afreeall(AEDIT);
5549 }
5550 #endif
5551 #endif /* !MKSH_NO_CMDLINE_EDITING */