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