sync
[bitrig.git] / bin / ksh / edit.c
blob32d07147fda63f8196ea4b35a0114059c663a7d5
1 /* $OpenBSD: edit.c,v 1.38 2013/06/03 15:41:59 tedu Exp $ */
3 /*
4 * Command line editing - common code
6 */
8 #include "config.h"
9 #ifdef EDIT
11 #include "sh.h"
12 #include "tty.h"
13 #define EXTERN
14 #include "edit.h"
15 #undef EXTERN
16 #include <sys/ioctl.h>
17 #include <ctype.h>
18 #include <libgen.h>
19 #include <sys/stat.h>
22 static void x_sigwinch(int);
23 volatile sig_atomic_t got_sigwinch;
24 static void check_sigwinch(void);
26 static int x_file_glob(int, const char *, int, char ***);
27 static int x_command_glob(int, const char *, int, char ***);
28 static int x_locate_word(const char *, int, int, int *, int *);
31 /* Called from main */
32 void
33 x_init(void)
35 /* set to -2 to force initial binding */
36 edchars.erase = edchars.kill = edchars.intr = edchars.quit =
37 edchars.eof = -2;
38 /* default value for deficient systems */
39 edchars.werase = 027; /* ^W */
41 if (setsig(&sigtraps[SIGWINCH], x_sigwinch, SS_RESTORE_ORIG|SS_SHTRAP))
42 sigtraps[SIGWINCH].flags |= TF_SHELL_USES;
43 got_sigwinch = 1; /* force initial check */
44 check_sigwinch();
46 #ifdef EMACS
47 x_init_emacs();
48 #endif /* EMACS */
51 /* ARGSUSED */
52 static void
53 x_sigwinch(int sig)
55 got_sigwinch = 1;
58 static void
59 check_sigwinch(void)
61 if (got_sigwinch) {
62 struct winsize ws;
64 got_sigwinch = 0;
65 if (procpid == kshpid && ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
66 struct tbl *vp;
68 /* Do NOT export COLUMNS/LINES. Many applications
69 * check COLUMNS/LINES before checking ws.ws_col/row,
70 * so if the app is started with C/L in the environ
71 * and the window is then resized, the app won't
72 * see the change cause the environ doesn't change.
74 if (ws.ws_col) {
75 x_cols = ws.ws_col < MIN_COLS ? MIN_COLS :
76 ws.ws_col;
78 if ((vp = typeset("COLUMNS", 0, 0, 0, 0)))
79 setint(vp, (long) ws.ws_col);
81 if (ws.ws_row && (vp = typeset("LINES", 0, 0, 0, 0)))
82 setint(vp, (long) ws.ws_row);
88 * read an edited command line
90 int
91 x_read(char *buf, size_t len)
93 int i;
95 x_mode(true);
96 #ifdef EMACS
97 if (Flag(FEMACS) || Flag(FGMACS))
98 i = x_emacs(buf, len);
99 else
100 #endif
101 #ifdef VI
102 if (Flag(FVI))
103 i = x_vi(buf, len);
104 else
105 #endif
106 i = -1; /* internal error */
107 x_mode(false);
108 check_sigwinch();
109 return i;
112 /* tty I/O */
115 x_getc(void)
117 char c;
118 int n;
120 while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR)
121 if (trap) {
122 x_mode(false);
123 runtraps(0);
124 x_mode(true);
126 if (n != 1)
127 return -1;
128 return (int) (unsigned char) c;
131 void
132 x_flush(void)
134 shf_flush(shl_out);
137 void
138 x_putc(int c)
140 shf_putc(c, shl_out);
143 void
144 x_puts(const char *s)
146 while (*s != 0)
147 shf_putc(*s++, shl_out);
150 bool
151 x_mode(bool onoff)
153 static bool x_cur_mode;
154 bool prev;
156 if (x_cur_mode == onoff)
157 return x_cur_mode;
158 prev = x_cur_mode;
159 x_cur_mode = onoff;
161 if (onoff) {
162 struct termios cb;
163 X_chars oldchars;
165 oldchars = edchars;
166 cb = tty_state;
168 edchars.erase = cb.c_cc[VERASE];
169 edchars.kill = cb.c_cc[VKILL];
170 edchars.intr = cb.c_cc[VINTR];
171 edchars.quit = cb.c_cc[VQUIT];
172 edchars.eof = cb.c_cc[VEOF];
173 edchars.werase = cb.c_cc[VWERASE];
174 cb.c_iflag &= ~(INLCR|ICRNL);
175 cb.c_lflag &= ~(ISIG|ICANON|ECHO);
176 /* osf/1 processes lnext when ~icanon */
177 cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
178 /* sunos 4.1.x & osf/1 processes discard(flush) when ~icanon */
179 cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
180 cb.c_cc[VTIME] = 0;
181 cb.c_cc[VMIN] = 1;
183 tcsetattr(tty_fd, TCSADRAIN, &cb);
185 /* Convert unset values to internal `unset' value */
186 if (edchars.erase == _POSIX_VDISABLE)
187 edchars.erase = -1;
188 if (edchars.kill == _POSIX_VDISABLE)
189 edchars.kill = -1;
190 if (edchars.intr == _POSIX_VDISABLE)
191 edchars.intr = -1;
192 if (edchars.quit == _POSIX_VDISABLE)
193 edchars.quit = -1;
194 if (edchars.eof == _POSIX_VDISABLE)
195 edchars.eof = -1;
196 if (edchars.werase == _POSIX_VDISABLE)
197 edchars.werase = -1;
198 if (memcmp(&edchars, &oldchars, sizeof(edchars)) != 0) {
199 #ifdef EMACS
200 x_emacs_keys(&edchars);
201 #endif
203 } else {
204 tcsetattr(tty_fd, TCSADRAIN, &tty_state);
207 return prev;
210 void
211 set_editmode(const char *ed)
213 static const enum sh_flag edit_flags[] = {
214 #ifdef EMACS
215 FEMACS, FGMACS,
216 #endif
217 #ifdef VI
218 FVI,
219 #endif
221 char *rcp;
222 int i;
224 if ((rcp = strrchr(ed, '/')))
225 ed = ++rcp;
226 for (i = 0; i < NELEM(edit_flags); i++)
227 if (strstr(ed, options[(int) edit_flags[i]].name)) {
228 change_flag(edit_flags[i], OF_SPECIAL, 1);
229 return;
233 /* ------------------------------------------------------------------------- */
234 /* Misc common code for vi/emacs */
236 /* Handle the commenting/uncommenting of a line.
237 * Returns:
238 * 1 if a carriage return is indicated (comment added)
239 * 0 if no return (comment removed)
240 * -1 if there is an error (not enough room for comment chars)
241 * If successful, *lenp contains the new length. Note: cursor should be
242 * moved to the start of the line after (un)commenting.
245 x_do_comment(char *buf, int bsize, int *lenp)
247 int i, j;
248 int len = *lenp;
250 if (len == 0)
251 return 1; /* somewhat arbitrary - it's what at&t ksh does */
253 /* Already commented? */
254 if (buf[0] == '#') {
255 int saw_nl = 0;
257 for (j = 0, i = 1; i < len; i++) {
258 if (!saw_nl || buf[i] != '#')
259 buf[j++] = buf[i];
260 saw_nl = buf[i] == '\n';
262 *lenp = j;
263 return 0;
264 } else {
265 int n = 1;
267 /* See if there's room for the #'s - 1 per \n */
268 for (i = 0; i < len; i++)
269 if (buf[i] == '\n')
270 n++;
271 if (len + n >= bsize)
272 return -1;
273 /* Now add them... */
274 for (i = len, j = len + n; --i >= 0; ) {
275 if (buf[i] == '\n')
276 buf[--j] = '#';
277 buf[--j] = buf[i];
279 buf[0] = '#';
280 *lenp += n;
281 return 1;
285 /* ------------------------------------------------------------------------- */
286 /* Common file/command completion code for vi/emacs */
289 static char *add_glob(const char *str, int slen);
290 static void glob_table(const char *pat, XPtrV *wp, struct table *tp);
291 static void glob_path(int flags, const char *pat, XPtrV *wp,
292 const char *path);
294 void
295 x_print_expansions(int nwords, char *const *words, int is_command)
297 int use_copy = 0;
298 int prefix_len;
299 XPtrV l;
301 /* Check if all matches are in the same directory (in this
302 * case, we want to omit the directory name)
304 if (!is_command &&
305 (prefix_len = x_longest_prefix(nwords, words)) > 0) {
306 int i;
308 /* Special case for 1 match (prefix is whole word) */
309 if (nwords == 1)
310 prefix_len = x_basename(words[0], (char *) 0);
311 /* Any (non-trailing) slashes in non-common word suffixes? */
312 for (i = 0; i < nwords; i++)
313 if (x_basename(words[i] + prefix_len, (char *) 0) >
314 prefix_len)
315 break;
316 /* All in same directory? */
317 if (i == nwords) {
318 while (prefix_len > 0 && words[0][prefix_len - 1] != '/')
319 prefix_len--;
320 use_copy = 1;
321 XPinit(l, nwords + 1);
322 for (i = 0; i < nwords; i++)
323 XPput(l, words[i] + prefix_len);
324 XPput(l, (char *) 0);
329 * Enumerate expansions
331 x_putc('\r');
332 x_putc('\n');
333 pr_list(use_copy ? (char **) XPptrv(l) : words);
335 if (use_copy)
336 XPfree(l); /* not x_free_words() */
340 * Do file globbing:
341 * - appends * to (copy of) str if no globbing chars found
342 * - does expansion, checks for no match, etc.
343 * - sets *wordsp to array of matching strings
344 * - returns number of matching strings
346 static int
347 x_file_glob(int flags, const char *str, int slen, char ***wordsp)
349 char *toglob;
350 char **words;
351 int nwords;
352 XPtrV w;
353 struct source *s, *sold;
355 if (slen < 0)
356 return 0;
358 toglob = add_glob(str, slen);
361 * Convert "foo*" (toglob) to an array of strings (words)
363 sold = source;
364 s = pushs(SWSTR, ATEMP);
365 s->start = s->str = toglob;
366 source = s;
367 if (yylex(ONEWORD|UNESCAPE) != LWORD) {
368 source = sold;
369 internal_errorf(0, "fileglob: substitute error");
370 return 0;
372 source = sold;
373 XPinit(w, 32);
374 expand(yylval.cp, &w, DOGLOB|DOTILDE|DOMARKDIRS);
375 XPput(w, NULL);
376 words = (char **) XPclose(w);
378 for (nwords = 0; words[nwords]; nwords++)
380 if (nwords == 1) {
381 struct stat statb;
383 /* Check if file exists, also, check for empty
384 * result - happens if we tried to glob something
385 * which evaluated to an empty string (e.g.,
386 * "$FOO" when there is no FOO, etc).
388 if ((lstat(words[0], &statb) < 0) ||
389 words[0][0] == '\0') {
390 x_free_words(nwords, words);
391 words = NULL;
392 nwords = 0;
395 afree(toglob, ATEMP);
397 if (nwords) {
398 *wordsp = words;
399 } else if (words) {
400 x_free_words(nwords, words);
401 *wordsp = NULL;
404 return nwords;
407 /* Data structure used in x_command_glob() */
408 struct path_order_info {
409 char *word;
410 int base;
411 int path_order;
414 static int path_order_cmp(const void *aa, const void *bb);
416 /* Compare routine used in x_command_glob() */
417 static int
418 path_order_cmp(const void *aa, const void *bb)
420 const struct path_order_info *a = (const struct path_order_info *) aa;
421 const struct path_order_info *b = (const struct path_order_info *) bb;
422 int t;
424 t = strcmp(a->word + a->base, b->word + b->base);
425 return t ? t : a->path_order - b->path_order;
428 static int
429 x_command_glob(int flags, const char *str, int slen, char ***wordsp)
431 char *toglob;
432 char *pat;
433 char *fpath;
434 int nwords;
435 XPtrV w;
436 struct block *l;
438 if (slen < 0)
439 return 0;
441 toglob = add_glob(str, slen);
443 /* Convert "foo*" (toglob) to a pattern for future use */
444 pat = evalstr(toglob, DOPAT|DOTILDE);
445 afree(toglob, ATEMP);
447 XPinit(w, 32);
449 glob_table(pat, &w, &keywords);
450 glob_table(pat, &w, &aliases);
451 glob_table(pat, &w, &builtins);
452 for (l = e->loc; l; l = l->next)
453 glob_table(pat, &w, &l->funs);
455 glob_path(flags, pat, &w, path);
456 if ((fpath = str_val(global("FPATH"))) != null)
457 glob_path(flags, pat, &w, fpath);
459 nwords = XPsize(w);
461 if (!nwords) {
462 *wordsp = (char **) 0;
463 XPfree(w);
464 return 0;
467 /* Sort entries */
468 if (flags & XCF_FULLPATH) {
469 /* Sort by basename, then path order */
470 struct path_order_info *info;
471 struct path_order_info *last_info = 0;
472 char **words = (char **) XPptrv(w);
473 int path_order = 0;
474 int i;
476 info = (struct path_order_info *)
477 alloc(sizeof(struct path_order_info) * nwords, ATEMP);
478 for (i = 0; i < nwords; i++) {
479 info[i].word = words[i];
480 info[i].base = x_basename(words[i], (char *) 0);
481 if (!last_info || info[i].base != last_info->base ||
482 strncmp(words[i], last_info->word, info[i].base) != 0) {
483 last_info = &info[i];
484 path_order++;
486 info[i].path_order = path_order;
488 qsort(info, nwords, sizeof(struct path_order_info),
489 path_order_cmp);
490 for (i = 0; i < nwords; i++)
491 words[i] = info[i].word;
492 afree((void *) info, ATEMP);
493 } else {
494 /* Sort and remove duplicate entries */
495 char **words = (char **) XPptrv(w);
496 int i, j;
498 qsortp(XPptrv(w), (size_t) nwords, xstrcmp);
500 for (i = j = 0; i < nwords - 1; i++) {
501 if (strcmp(words[i], words[i + 1]))
502 words[j++] = words[i];
503 else
504 afree(words[i], ATEMP);
506 words[j++] = words[i];
507 nwords = j;
508 w.cur = (void **) &words[j];
511 XPput(w, NULL);
512 *wordsp = (char **) XPclose(w);
514 return nwords;
517 #define IS_WORDC(c) !( ctype(c, C_LEX1) || (c) == '\'' || (c) == '"' || \
518 (c) == '`' || (c) == '=' || (c) == ':' )
520 static int
521 x_locate_word(const char *buf, int buflen, int pos, int *startp,
522 int *is_commandp)
524 int p;
525 int start, end;
527 /* Bad call? Probably should report error */
528 if (pos < 0 || pos > buflen) {
529 *startp = pos;
530 *is_commandp = 0;
531 return 0;
533 /* The case where pos == buflen happens to take care of itself... */
535 start = pos;
536 /* Keep going backwards to start of word (has effect of allowing
537 * one blank after the end of a word)
539 for (; (start > 0 && IS_WORDC(buf[start - 1])) ||
540 (start > 1 && buf[start-2] == '\\'); start--)
542 /* Go forwards to end of word */
543 for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
544 if (buf[end] == '\\' && (end+1) < buflen)
545 end++;
548 if (is_commandp) {
549 int iscmd;
551 /* Figure out if this is a command */
552 for (p = start - 1; p >= 0 && isspace(buf[p]); p--)
554 iscmd = p < 0 || strchr(";|&()`", buf[p]);
555 if (iscmd) {
556 /* If command has a /, path, etc. is not searched;
557 * only current directory is searched, which is just
558 * like file globbing.
560 for (p = start; p < end; p++)
561 if (buf[p] == '/')
562 break;
563 iscmd = p == end;
565 *is_commandp = iscmd;
568 *startp = start;
570 return end - start;
574 x_cf_glob(int flags, const char *buf, int buflen, int pos, int *startp,
575 int *endp, char ***wordsp, int *is_commandp)
577 int len;
578 int nwords;
579 char **words;
580 int is_command;
582 len = x_locate_word(buf, buflen, pos, startp, &is_command);
583 if (!(flags & XCF_COMMAND))
584 is_command = 0;
585 /* Don't do command globing on zero length strings - it takes too
586 * long and isn't very useful. File globs are more likely to be
587 * useful, so allow these.
589 if (len == 0 && is_command)
590 return 0;
592 nwords = (is_command ? x_command_glob : x_file_glob)(flags,
593 buf + *startp, len, &words);
594 if (nwords == 0) {
595 *wordsp = (char **) 0;
596 return 0;
599 if (is_commandp)
600 *is_commandp = is_command;
601 *wordsp = words;
602 *endp = *startp + len;
604 return nwords;
607 /* Given a string, copy it and possibly add a '*' to the end. The
608 * new string is returned.
610 static char *
611 add_glob(const char *str, int slen)
613 char *toglob;
614 char *s;
615 bool saw_slash = false;
617 if (slen < 0)
618 return (char *) 0;
620 toglob = str_nsave(str, slen + 1, ATEMP); /* + 1 for "*" */
621 toglob[slen] = '\0';
624 * If the pathname contains a wildcard (an unquoted '*',
625 * '?', or '[') or parameter expansion ('$'), or a ~username
626 * with no trailing slash, then it is globbed based on that
627 * value (i.e., without the appended '*').
629 for (s = toglob; *s; s++) {
630 if (*s == '\\' && s[1])
631 s++;
632 else if (*s == '*' || *s == '[' || *s == '?' || *s == '$' ||
633 (s[1] == '(' /*)*/ && strchr("+@!", *s)))
634 break;
635 else if (*s == '/')
636 saw_slash = true;
638 if (!*s && (*toglob != '~' || saw_slash)) {
639 toglob[slen] = '*';
640 toglob[slen + 1] = '\0';
643 return toglob;
647 * Find longest common prefix
650 x_longest_prefix(int nwords, char *const *words)
652 int i, j;
653 int prefix_len;
654 char *p;
656 if (nwords <= 0)
657 return 0;
659 prefix_len = strlen(words[0]);
660 for (i = 1; i < nwords; i++)
661 for (j = 0, p = words[i]; j < prefix_len; j++)
662 if (p[j] != words[0][j]) {
663 prefix_len = j;
664 break;
666 return prefix_len;
669 void
670 x_free_words(int nwords, char **words)
672 int i;
674 for (i = 0; i < nwords; i++)
675 if (words[i])
676 afree(words[i], ATEMP);
677 afree(words, ATEMP);
680 /* Return the offset of the basename of string s (which ends at se - need not
681 * be null terminated). Trailing slashes are ignored. If s is just a slash,
682 * then the offset is 0 (actually, length - 1).
683 * s Return
684 * /etc 1
685 * /etc/ 1
686 * /etc// 1
687 * /etc/fo 5
688 * foo 0
689 * /// 2
693 x_basename(const char *s, const char *se)
695 const char *p;
697 if (se == (char *) 0)
698 se = s + strlen(s);
699 if (s == se)
700 return 0;
702 /* Skip trailing slashes */
703 for (p = se - 1; p > s && *p == '/'; p--)
705 for (; p > s && *p != '/'; p--)
707 if (*p == '/' && p + 1 < se)
708 p++;
710 return p - s;
714 * Apply pattern matching to a table: all table entries that match a pattern
715 * are added to wp.
717 static void
718 glob_table(const char *pat, XPtrV *wp, struct table *tp)
720 struct tstate ts;
721 struct tbl *te;
723 for (ktwalk(&ts, tp); (te = ktnext(&ts)); ) {
724 if (gmatch(te->name, pat, false))
725 XPput(*wp, str_save(te->name, ATEMP));
729 static void
730 glob_path(int flags, const char *pat, XPtrV *wp, const char *path)
732 const char *sp, *p;
733 char *xp;
734 int staterr;
735 int pathlen;
736 int patlen;
737 int oldsize, newsize, i, j;
738 char **words;
739 XString xs;
741 patlen = strlen(pat) + 1;
742 sp = path;
743 Xinit(xs, xp, patlen + 128, ATEMP);
744 while (sp) {
745 xp = Xstring(xs, xp);
746 if (!(p = strchr(sp, ':')))
747 p = sp + strlen(sp);
748 pathlen = p - sp;
749 if (pathlen) {
750 /* Copy sp into xp, stuffing any MAGIC characters
751 * on the way
753 const char *s = sp;
755 XcheckN(xs, xp, pathlen * 2);
756 while (s < p) {
757 if (ISMAGIC(*s))
758 *xp++ = MAGIC;
759 *xp++ = *s++;
761 *xp++ = '/';
762 pathlen++;
764 sp = p;
765 XcheckN(xs, xp, patlen);
766 memcpy(xp, pat, patlen);
768 oldsize = XPsize(*wp);
769 glob_str(Xstring(xs, xp), wp, 1); /* mark dirs */
770 newsize = XPsize(*wp);
772 /* Check that each match is executable... */
773 words = (char **) XPptrv(*wp);
774 for (i = j = oldsize; i < newsize; i++) {
775 staterr = 0;
776 if ((search_access(words[i], X_OK, &staterr) >= 0) ||
777 (staterr == EISDIR)) {
778 words[j] = words[i];
779 if (!(flags & XCF_FULLPATH))
780 memmove(words[j], words[j] + pathlen,
781 strlen(words[j] + pathlen) + 1);
782 j++;
783 } else
784 afree(words[i], ATEMP);
786 wp->cur = (void **) &words[j];
788 if (!*sp++)
789 break;
791 Xfree(xs, xp);
795 * if argument string contains any special characters, they will
796 * be escaped and the result will be put into edit buffer by
797 * keybinding-specific function
800 x_escape(const char *s, size_t len, int (*putbuf_func) (const char *, size_t))
802 size_t add, wlen;
803 const char *ifs = str_val(local("IFS", 0));
804 int rval = 0;
806 for (add = 0, wlen = len; wlen - add > 0; add++) {
807 if (strchr("\"#$&'()*:;<=>?[\\]`{|}", s[add]) ||
808 strchr(ifs, s[add])) {
809 if (putbuf_func(s, add) != 0) {
810 rval = -1;
811 break;
814 putbuf_func("\\", 1);
815 putbuf_func(&s[add], 1);
817 add++;
818 wlen -= add;
819 s += add;
820 add = -1; /* after the increment it will go to 0 */
823 if (wlen > 0 && rval == 0)
824 rval = putbuf_func(s, wlen);
826 return (rval);
828 #endif /* EDIT */