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