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