OSDN Git Service

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