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