Home | History | Annotate | Line # | Download | only in ksh
edit.c revision 1.27
      1 /*	$NetBSD: edit.c,v 1.27 2017/06/22 13:33:39 kamil Exp $	*/
      2 
      3 /*
      4  * Command line editing - common code
      5  *
      6  */
      7 #include <sys/cdefs.h>
      8 
      9 #ifndef lint
     10 __RCSID("$NetBSD: edit.c,v 1.27 2017/06/22 13:33:39 kamil Exp $");
     11 #endif
     12 
     13 
     14 #include "config.h"
     15 #ifdef EDIT
     16 
     17 #include "sh.h"
     18 #include "tty.h"
     19 #define EXTERN
     20 #include "edit.h"
     21 #undef EXTERN
     22 #ifdef OS_SCO	/* SCO Unix 3.2v4.1 */
     23 # include <sys/stream.h>	/* needed for <sys/ptem.h> */
     24 # include <sys/ptem.h>		/* needed for struct winsize */
     25 #endif /* OS_SCO */
     26 #include <sys/ioctl.h>
     27 #include <ctype.h>
     28 #include "ksh_stat.h"
     29 
     30 
     31 #if defined(TIOCGWINSZ)
     32 static RETSIGTYPE x_sigwinch ARGS((int sig));
     33 static int got_sigwinch;
     34 static void check_sigwinch ARGS((void));
     35 #endif /* TIOCGWINSZ */
     36 
     37 static int	x_file_glob ARGS((int flags, const char *str, int slen,
     38 				  char ***wordsp));
     39 static int	x_command_glob ARGS((int flags, const char *str, int slen,
     40 				     char ***wordsp));
     41 static int	x_locate_word ARGS((const char *buf, int buflen, int pos,
     42 				    int *startp, int *is_command));
     43 
     44 static char vdisable_c;
     45 
     46 
     47 /* Called from main */
     48 void
     49 x_init()
     50 {
     51 	/* set to -2 to force initial binding */
     52 	edchars.erase = edchars.kill = edchars.intr = edchars.quit
     53 		= edchars.eof = -2;
     54 	/* default value for deficient systems */
     55 	edchars.werase = 027;	/* ^W */
     56 
     57 #ifdef TIOCGWINSZ
     58 # ifdef SIGWINCH
     59 	if (setsig(&sigtraps[SIGWINCH], x_sigwinch, SS_RESTORE_ORIG|SS_SHTRAP))
     60 		sigtraps[SIGWINCH].flags |= TF_SHELL_USES;
     61 # endif /* SIGWINCH */
     62 	got_sigwinch = 1; /* force initial check */
     63 	check_sigwinch();
     64 #endif /* TIOCGWINSZ */
     65 
     66 #ifdef EMACS
     67 	x_init_emacs();
     68 #endif /* EMACS */
     69 
     70 	/* Bizarreness to figure out how to disable
     71 	 * a struct termios.c_cc[] char
     72 	 */
     73 #ifdef _POSIX_VDISABLE
     74 	if (_POSIX_VDISABLE >= 0)
     75 		vdisable_c = (char) _POSIX_VDISABLE;
     76 	else
     77 		/* `feature not available' */
     78 		vdisable_c = (char) 0377;
     79 #else
     80 # if defined(HAVE_PATHCONF) && defined(_PC_VDISABLE)
     81 	vdisable_c = fpathconf(tty_fd, _PC_VDISABLE);
     82 # else
     83 	vdisable_c = (char) 0377;	/* default to old BSD value */
     84 # endif
     85 #endif /* _POSIX_VDISABLE */
     86 }
     87 
     88 #if defined(TIOCGWINSZ)
     89 static RETSIGTYPE
     90 x_sigwinch(sig)
     91     	int sig;
     92 {
     93 	got_sigwinch = 1;
     94 	return RETSIGVAL;
     95 }
     96 
     97 static void
     98 check_sigwinch ARGS((void))
     99 {
    100 	if (got_sigwinch) {
    101 		struct winsize ws;
    102 
    103 		got_sigwinch = 0;
    104 		if (procpid == kshpid && ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
    105 			struct tbl *vp;
    106 
    107 			/* Do NOT export COLUMNS/LINES.  Many applications
    108 			 * check COLUMNS/LINES before checking ws.ws_col/row,
    109 			 * so if the app is started with C/L in the environ
    110 			 * and the window is then resized, the app won't
    111 			 * see the change cause the environ doesn't change.
    112 			 */
    113 			if (ws.ws_col) {
    114 				x_cols = ws.ws_col < MIN_COLS ? MIN_COLS
    115 						: ws.ws_col;
    116 
    117 				if ((vp = typeset("COLUMNS", 0, 0, 0, 0)))
    118 					setint(vp, (long) ws.ws_col);
    119 			}
    120 			if (ws.ws_row
    121 			    && (vp = typeset("LINES", 0, 0, 0, 0)))
    122 				setint(vp, (long) ws.ws_row);
    123 		}
    124 	}
    125 }
    126 #endif /* TIOCGWINSZ */
    127 
    128 /*
    129  * read an edited command line
    130  */
    131 int
    132 x_read(buf, len)
    133 	char *buf;
    134 	size_t len;
    135 {
    136 	int	i;
    137 
    138 	x_mode(TRUE);
    139 #ifdef EMACS
    140 	if (Flag(FEMACS) || Flag(FGMACS))
    141 		i = x_emacs(buf, len);
    142 	else
    143 #endif
    144 #ifdef VI
    145 	if (Flag(FVI))
    146 		i = x_vi(buf, len);
    147 	else
    148 #endif
    149 		i = -1;		/* internal error */
    150 	x_mode(FALSE);
    151 #if defined(TIOCGWINSZ)
    152 	if (got_sigwinch)
    153 		check_sigwinch();
    154 #endif /* TIOCGWINSZ */
    155 
    156 	return i;
    157 }
    158 
    159 /* tty I/O */
    160 
    161 int
    162 x_getc()
    163 {
    164 	char c;
    165 	int n;
    166 
    167 	while ((n = blocking_read(0, &c, 1)) < 0 && errno == EINTR)
    168 		if (trap) {
    169 			x_mode(FALSE);
    170 			runtraps(0);
    171 			x_mode(TRUE);
    172 		}
    173 	if (n != 1)
    174 		return -1;
    175 	return (int) (unsigned char) c;
    176 }
    177 
    178 void
    179 x_flush()
    180 {
    181 	shf_flush(shl_out);
    182 }
    183 
    184 void
    185 x_putc(c)
    186 	int c;
    187 {
    188 	shf_putc(c, shl_out);
    189 }
    190 
    191 void
    192 x_puts(s)
    193 	const char *s;
    194 {
    195 	while (*s != 0)
    196 		shf_putc(*s++, shl_out);
    197 }
    198 
    199 bool_t
    200 x_mode(onoff)
    201 	bool_t	onoff;
    202 {
    203 	static bool_t	x_cur_mode;
    204 	bool_t		prev;
    205 
    206 	if (x_cur_mode == onoff)
    207 		return x_cur_mode;
    208 	prev = x_cur_mode;
    209 	x_cur_mode = onoff;
    210 
    211 	if (onoff) {
    212 		TTY_state	cb;
    213 		X_chars		oldchars;
    214 
    215 		oldchars = edchars;
    216 		cb = tty_state;
    217 
    218 #if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H)
    219 		edchars.erase = cb.c_cc[VERASE];
    220 		edchars.kill = cb.c_cc[VKILL];
    221 		edchars.intr = cb.c_cc[VINTR];
    222 		edchars.quit = cb.c_cc[VQUIT];
    223 		edchars.eof = cb.c_cc[VEOF];
    224 # ifdef VWERASE
    225 		edchars.werase = cb.c_cc[VWERASE];
    226 # endif
    227 # ifdef _CRAY2		/* brain-damaged terminal handler */
    228 		cb.c_lflag &= ~(ICANON|ECHO);
    229 		/* rely on print routine to map '\n' to CR,LF */
    230 # else
    231 		cb.c_iflag &= ~(INLCR|ICRNL);
    232 #  ifdef _BSD_SYSV	/* need to force CBREAK instead of RAW (need CRMOD on output) */
    233 		cb.c_lflag &= ~(ICANON|ECHO);
    234 #  else
    235 #   ifdef SWTCH	/* need CBREAK to handle swtch char */
    236 		cb.c_lflag &= ~(ICANON|ECHO);
    237 		cb.c_lflag |= ISIG;
    238 		cb.c_cc[VINTR] = vdisable_c;
    239 		cb.c_cc[VQUIT] = vdisable_c;
    240 #   else
    241 		cb.c_lflag &= ~(ISIG|ICANON|ECHO);
    242 #   endif
    243 #  endif
    244 #  ifdef VLNEXT
    245 		/* osf/1 processes lnext when ~icanon */
    246 		cb.c_cc[VLNEXT] = vdisable_c;
    247 #  endif /* VLNEXT */
    248 #  ifdef VDISCARD
    249 		/* sunos 4.1.x & osf/1 processes discard(flush) when ~icanon */
    250 		cb.c_cc[VDISCARD] = vdisable_c;
    251 #  endif /* VDISCARD */
    252 		cb.c_cc[VTIME] = 0;
    253 		cb.c_cc[VMIN] = 1;
    254 # endif	/* _CRAY2 */
    255 #else
    256 	/* Assume BSD tty stuff. */
    257 		edchars.erase = cb.sgttyb.sg_erase;
    258 		edchars.kill = cb.sgttyb.sg_kill;
    259 		cb.sgttyb.sg_flags &= ~ECHO;
    260 		cb.sgttyb.sg_flags |= CBREAK;
    261 #  ifdef TIOCGATC
    262 		edchars.intr = cb.lchars.tc_intrc;
    263 		edchars.quit = cb.lchars.tc_quitc;
    264 		edchars.eof = cb.lchars.tc_eofc;
    265 		edchars.werase = cb.lchars.tc_werasc;
    266 		cb.lchars.tc_suspc = -1;
    267 		cb.lchars.tc_dsuspc = -1;
    268 		cb.lchars.tc_lnextc = -1;
    269 		cb.lchars.tc_statc = -1;
    270 		cb.lchars.tc_intrc = -1;
    271 		cb.lchars.tc_quitc = -1;
    272 		cb.lchars.tc_rprntc = -1;
    273 #  else
    274 		edchars.intr = cb.tchars.t_intrc;
    275 		edchars.quit = cb.tchars.t_quitc;
    276 		edchars.eof = cb.tchars.t_eofc;
    277 		cb.tchars.t_intrc = -1;
    278 		cb.tchars.t_quitc = -1;
    279 #   ifdef TIOCGLTC
    280 		edchars.werase = cb.ltchars.t_werasc;
    281 		cb.ltchars.t_suspc = -1;
    282 		cb.ltchars.t_dsuspc = -1;
    283 		cb.ltchars.t_lnextc = -1;
    284 		cb.ltchars.t_rprntc = -1;
    285 #   endif
    286 #  endif /* TIOCGATC */
    287 #endif /* HAVE_TERMIOS_H || HAVE_TERMIO_H */
    288 
    289 		set_tty(tty_fd, &cb, TF_WAIT);
    290 
    291 		/* Convert unset values to internal `unset' value */
    292 		if (edchars.erase == vdisable_c)
    293 			edchars.erase = -1;
    294 		if (edchars.kill == vdisable_c)
    295 			edchars.kill = -1;
    296 		if (edchars.intr == vdisable_c)
    297 			edchars.intr = -1;
    298 		if (edchars.quit == vdisable_c)
    299 			edchars.quit = -1;
    300 		if (edchars.eof == vdisable_c)
    301 			edchars.eof = -1;
    302 		if (edchars.werase == vdisable_c)
    303 			edchars.werase = -1;
    304 		if (memcmp(&edchars, &oldchars, sizeof(edchars)) != 0) {
    305 #ifdef EMACS
    306 			x_emacs_keys(&edchars);
    307 #endif
    308 		}
    309 	} else {
    310 		/* TF_WAIT doesn't seem to be necessary when leaving xmode */
    311 		set_tty(tty_fd, &tty_state, TF_NONE);
    312 	}
    313 
    314 	return prev;
    315 }
    316 
    317 /* NAME:
    318  *      promptlen - calculate the length of PS1 etc.
    319  *
    320  * DESCRIPTION:
    321  *      This function is based on a fix from guy (at) demon.co.uk
    322  *      It fixes a bug in that if PS1 contains '!', the length
    323  *      given by strlen() is probably wrong.
    324  *
    325  * RETURN VALUE:
    326  *      length
    327  */
    328 int
    329 promptlen(cp, spp)
    330     const char  *cp;
    331     const char **spp;
    332 {
    333     int count = 0;
    334     const char *sp = cp;
    335     char delimiter = 0;
    336     int indelimit = 0;
    337 
    338     /* Undocumented AT&T ksh feature:
    339      * If the second char in the prompt string is \r then the first char
    340      * is taken to be a non-printing delimiter and any chars between two
    341      * instances of the delimiter are not considered to be part of the
    342      * prompt length
    343      */
    344     if (*cp && cp[1] == '\r') {
    345 	delimiter = *cp;
    346 	cp += 2;
    347     }
    348 
    349     for (; *cp; cp++) {
    350 	if (indelimit && *cp != delimiter)
    351 	    ;
    352 	else if (*cp == '\n' || *cp == '\r') {
    353 	    count = 0;
    354 	    sp = cp + 1;
    355 	} else if (*cp == '\t') {
    356 	    count = (count | 7) + 1;
    357 	} else if (*cp == '\b') {
    358 	    if (count > 0)
    359 		count--;
    360 	} else if (*cp == delimiter)
    361 	    indelimit = !indelimit;
    362 	else
    363 	    count++;
    364     }
    365     if (spp)
    366 	*spp = sp;
    367     return count;
    368 }
    369 
    370 void
    371 set_editmode(ed)
    372 	const char *ed;
    373 {
    374 	static const enum sh_flag edit_flags[] = {
    375 #ifdef EMACS
    376 			FEMACS, FGMACS,
    377 #endif
    378 #ifdef VI
    379 			FVI,
    380 #endif
    381 		    };
    382 	char *rcp;
    383 	size_t i;
    384 
    385 	if ((rcp = ksh_strrchr_dirsep(ed)))
    386 		ed = ++rcp;
    387 	for (i = 0; i < NELEM(edit_flags); i++)
    388 		if (strstr(ed, goptions[(int) edit_flags[i]].name)) {
    389 			change_flag(edit_flags[i], OF_SPECIAL, 1);
    390 			return;
    391 		}
    392 }
    393 
    394 /* ------------------------------------------------------------------------- */
    395 /*           Misc common code for vi/emacs				     */
    396 
    397 /* Handle the commenting/uncommenting of a line.
    398  * Returns:
    399  *	1 if a carriage return is indicated (comment added)
    400  *	0 if no return (comment removed)
    401  *	-1 if there is an error (not enough room for comment chars)
    402  * If successful, *lenp contains the new length.  Note: cursor should be
    403  * moved to the start of the line after (un)commenting.
    404  */
    405 int
    406 x_do_comment(buf, bsize, lenp)
    407 	char *buf;
    408 	int bsize;
    409 	int *lenp;
    410 {
    411 	int i, j;
    412 	int len = *lenp;
    413 
    414 	if (len == 0)
    415 		return 1; /* somewhat arbitrary - it's what at&t ksh does */
    416 
    417 	/* Already commented? */
    418 	if (buf[0] == '#') {
    419 		int saw_nl = 0;
    420 
    421 		for (j = 0, i = 1; i < len; i++) {
    422 			if (!saw_nl || buf[i] != '#')
    423 				buf[j++] = buf[i];
    424 			saw_nl = buf[i] == '\n';
    425 		}
    426 		*lenp = j;
    427 		return 0;
    428 	} else {
    429 		int n = 1;
    430 
    431 		/* See if there's room for the #'s - 1 per \n */
    432 		for (i = 0; i < len; i++)
    433 			if (buf[i] == '\n')
    434 				n++;
    435 		if (len + n >= bsize)
    436 			return -1;
    437 		/* Now add them... */
    438 		for (i = len, j = len + n; --i >= 0; ) {
    439 			if (buf[i] == '\n')
    440 				buf[--j] = '#';
    441 			buf[--j] = buf[i];
    442 		}
    443 		buf[0] = '#';
    444 		*lenp += n;
    445 		return 1;
    446 	}
    447 }
    448 
    449 /* ------------------------------------------------------------------------- */
    450 /*           Common file/command completion code for vi/emacs	             */
    451 
    452 
    453 static char	*add_glob ARGS((const char *, int));
    454 static void	glob_table ARGS((const char *, XPtrV *, struct table *));
    455 static void	glob_path ARGS((int, const char *, XPtrV *, const char *));
    456 
    457 #if 0 /* not used... */
    458 int	x_complete_word ARGS((const char *, int, int, int *, char **));
    459 int
    460 x_complete_word(str, slen, is_command, nwordsp, ret)
    461 	const char *str;
    462 	int slen;
    463 	int is_command;
    464 	int *nwordsp;
    465 	char **ret;
    466 {
    467 	int nwords;
    468 	int prefix_len;
    469 	char **words;
    470 
    471 	nwords = (is_command ? x_command_glob : x_file_glob)(XCF_FULLPATH,
    472 				str, slen, &words);
    473 	*nwordsp = nwords;
    474 	if (nwords == 0) {
    475 		*ret = (char *) 0;
    476 		return -1;
    477 	}
    478 
    479 	prefix_len = x_longest_prefix(nwords, words);
    480 	*ret = str_nsave(words[0], prefix_len, ATEMP);
    481 	x_free_words(nwords, words);
    482 	return prefix_len;
    483 }
    484 #endif /* 0 */
    485 
    486 void
    487 x_print_expansions(nwords, words, is_command)
    488 	int nwords;
    489 	char *const *words;
    490 	int is_command;
    491 {
    492 	int use_copy = 0;
    493 	int prefix_len;
    494 	XPtrV l;
    495 
    496 	l.beg = NULL;
    497 
    498 	/* Check if all matches are in the same directory (in this
    499 	 * case, we want to omit the directory name)
    500 	 */
    501 	if (!is_command
    502 	    && (prefix_len = x_longest_prefix(nwords, words)) > 0)
    503 	{
    504 		int i;
    505 
    506 		/* Special case for 1 match (prefix is whole word) */
    507 		if (nwords == 1)
    508 			prefix_len = x_basename(words[0], (char *) 0);
    509 		/* Any (non-trailing) slashes in non-common word suffixes? */
    510 		for (i = 0; i < nwords; i++)
    511 			if (x_basename(words[i] + prefix_len, (char *) 0)
    512 							> prefix_len)
    513 				break;
    514 		/* All in same directory? */
    515 		if (i == nwords) {
    516 			while (prefix_len > 0
    517 			       && !ISDIRSEP(words[0][prefix_len - 1]))
    518 				prefix_len--;
    519 			use_copy = 1;
    520 			XPinit(l, nwords + 1);
    521 			for (i = 0; i < nwords; i++)
    522 				XPput(l, words[i] + prefix_len);
    523 			XPput(l, (char *) 0);
    524 		}
    525 	}
    526 
    527 	/*
    528 	 * Enumerate expansions
    529 	 */
    530 	x_putc('\r');
    531 	x_putc('\n');
    532 	pr_list(use_copy ? (char **) XPptrv(l) : words);
    533 
    534 	if (use_copy)
    535 		XPfree(l); /* not x_free_words() */
    536 }
    537 
    538 /*
    539  *  Do file globbing:
    540  *	- appends * to (copy of) str if no globbing chars found
    541  *	- does expansion, checks for no match, etc.
    542  *	- sets *wordsp to array of matching strings
    543  *	- returns number of matching strings
    544  */
    545 static int
    546 x_file_glob(flags, str, slen, wordsp)
    547 	int flags;
    548 	const char *str;
    549 	int slen;
    550 	char ***wordsp;
    551 {
    552 	char *toglob;
    553 	char **words;
    554 	int nwords, i, idx, escaping;
    555 	XPtrV w;
    556 	struct source *s, *sold;
    557 
    558 	if (slen < 0)
    559 		return 0;
    560 
    561 	toglob = add_glob(str, slen);
    562 
    563 	/* remove all escaping backward slashes */
    564 	escaping = 0;
    565 	for(i = 0, idx = 0; toglob[i]; i++) {
    566 		if (toglob[i] == '\\' && !escaping) {
    567 			escaping = 1;
    568 			continue;
    569 		}
    570 
    571 		toglob[idx] = toglob[i];
    572 		idx++;
    573 		if (escaping) escaping = 0;
    574 	}
    575 	toglob[idx] = '\0';
    576 
    577 	/*
    578 	 * Convert "foo*" (toglob) to an array of strings (words)
    579 	 */
    580 	sold = source;
    581 	s = pushs(SWSTR, ATEMP);
    582 	s->start = s->str = toglob;
    583 	source = s;
    584 	if (yylex(ONEWORD) != LWORD) {
    585 		source = sold;
    586 		internal_errorf(0, "fileglob: substitute error");
    587 		return 0;
    588 	}
    589 	source = sold;
    590 	XPinit(w, 32);
    591 	expand(yylval.cp, &w, DOGLOB|DOTILDE|DOMARKDIRS);
    592 	XPput(w, NULL);
    593 	words = (char **) XPclose(w);
    594 
    595 	for (nwords = 0; words[nwords]; nwords++)
    596 		;
    597 	if (nwords == 1) {
    598 		struct stat statb;
    599 
    600 		/* Check if globbing failed (returned glob pattern),
    601 		 * but be careful (E.g. toglob == "ab*" when the file
    602 		 * "ab*" exists is not an error).
    603 		 * Also, check for empty result - happens if we tried
    604 		 * to glob something which evaluated to an empty
    605 		 * string (e.g., "$FOO" when there is no FOO, etc).
    606 		 */
    607 		if ((strcmp(words[0], toglob) == 0
    608 		     && stat(words[0], &statb) < 0)
    609 		    || words[0][0] == '\0')
    610 		{
    611 			x_free_words(nwords, words);
    612 			words = NULL;
    613 			nwords = 0;
    614 		}
    615 	}
    616 	afree(toglob, ATEMP);
    617 
    618 	if (nwords) {
    619 		*wordsp = words;
    620 	} else if (words) {
    621 		x_free_words(nwords, words);
    622 		*wordsp = NULL;
    623 	}
    624 	return nwords;
    625 }
    626 
    627 /* Data structure used in x_command_glob() */
    628 struct path_order_info {
    629 	char *word;
    630 	int base;
    631 	int path_order;
    632 };
    633 
    634 static int path_order_cmp(const void *aa, const void *bb);
    635 
    636 /* Compare routine used in x_command_glob() */
    637 static int
    638 path_order_cmp(aa, bb)
    639 	const void *aa;
    640 	const void *bb;
    641 {
    642 	const struct path_order_info *a = (const struct path_order_info *) aa;
    643 	const struct path_order_info *b = (const struct path_order_info *) bb;
    644 	int t;
    645 
    646 	t = FILECMP(a->word + a->base, b->word + b->base);
    647 	return t ? t : a->path_order - b->path_order;
    648 }
    649 
    650 static int
    651 x_command_glob(flags, str, slen, wordsp)
    652 	int flags;
    653 	const char *str;
    654 	int slen;
    655 	char ***wordsp;
    656 {
    657 	char *toglob;
    658 	char *pat;
    659 	char *fpath;
    660 	int nwords;
    661 	XPtrV w;
    662 	struct block *l;
    663 
    664 	if (slen < 0)
    665 		return 0;
    666 
    667 	toglob = add_glob(str, slen);
    668 
    669 	/* Convert "foo*" (toglob) to a pattern for future use */
    670 	pat = evalstr(toglob, DOPAT|DOTILDE);
    671 	afree(toglob, ATEMP);
    672 
    673 	XPinit(w, 32);
    674 
    675 	glob_table(pat, &w, &keywords);
    676 	glob_table(pat, &w, &aliases);
    677 	glob_table(pat, &w, &builtins);
    678 	for (l = e->loc; l; l = l->next)
    679 		glob_table(pat, &w, &l->funs);
    680 
    681 	glob_path(flags, pat, &w, path);
    682 	if ((fpath = str_val(global("FPATH"))) != null)
    683 		glob_path(flags, pat, &w, fpath);
    684 
    685 	nwords = XPsize(w);
    686 
    687 	if (!nwords) {
    688 		*wordsp = (char **) 0;
    689 		XPfree(w);
    690 		return 0;
    691 	}
    692 
    693 	/* Sort entries */
    694 	if (flags & XCF_FULLPATH) {
    695 		/* Sort by basename, then path order */
    696 		struct path_order_info *info;
    697 		struct path_order_info *last_info = 0;
    698 		char **words = (char **) XPptrv(w);
    699 		int path_order = 0;
    700 		int i;
    701 
    702 		info = (struct path_order_info *)
    703 			alloc(sizeof(struct path_order_info) * nwords, ATEMP);
    704 		for (i = 0; i < nwords; i++) {
    705 			info[i].word = words[i];
    706 			info[i].base = x_basename(words[i], (char *) 0);
    707 			if (!last_info || info[i].base != last_info->base
    708 			    || FILENCMP(words[i],
    709 					last_info->word, info[i].base) != 0)
    710 			{
    711 				last_info = &info[i];
    712 				path_order++;
    713 			}
    714 			info[i].path_order = path_order;
    715 		}
    716 		qsort(info, nwords, sizeof(struct path_order_info),
    717 			path_order_cmp);
    718 		for (i = 0; i < nwords; i++)
    719 			words[i] = info[i].word;
    720 		afree((void *) info, ATEMP);
    721 	} else {
    722 		/* Sort and remove duplicate entries */
    723 		char **words = (char **) XPptrv(w);
    724 		int i, j;
    725 
    726 		qsortp(XPptrv(w), (size_t) nwords, xstrcmp);
    727 
    728 		for (i = j = 0; i < nwords - 1; i++) {
    729 			if (strcmp(words[i], words[i + 1]))
    730 				words[j++] = words[i];
    731 			else
    732 				afree(words[i], ATEMP);
    733 		}
    734 		words[j++] = words[i];
    735 		nwords = j;
    736 		w.cur = (void **) &words[j];
    737 	}
    738 
    739 	XPput(w, NULL);
    740 	*wordsp = (char **) XPclose(w);
    741 
    742 	return nwords;
    743 }
    744 
    745 #define IS_WORDC(c)	!( ctype(c, C_LEX1) || (c) == '\'' || (c) == '"'  \
    746 			    || (c) == '`' || (c) == '=' || (c) == ':' )
    747 
    748 static int
    749 x_locate_word(buf, buflen, pos, startp, is_commandp)
    750 	const char *buf;
    751 	int buflen;
    752 	int pos;
    753 	int *startp;
    754 	int *is_commandp;
    755 {
    756 	int p;
    757 	int start, end;
    758 
    759 	/* Bad call?  Probably should report error */
    760 	if (pos < 0 || pos > buflen) {
    761 		*startp = pos;
    762 		*is_commandp = 0;
    763 		return 0;
    764 	}
    765 	/* The case where pos == buflen happens to take care of itself... */
    766 
    767 	start = pos;
    768 	/* Keep going backwards to start of word (has effect of allowing
    769 	 * one blank after the end of a word)
    770 	 */
    771 	for (; (start > 0 && IS_WORDC(buf[start - 1]))
    772 		|| (start > 1 && buf[start-2] == '\\'); start--)
    773 		;
    774 	/* Go forwards to end of word */
    775 	for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
    776 		if (buf[end] == '\\' && (end+1) < buflen)
    777 			end++;
    778 	}
    779 
    780 	if (is_commandp) {
    781 		int iscmd;
    782 
    783 		/* Figure out if this is a command */
    784 		for (p = start - 1; p >= 0 && isspace((unsigned char)buf[p]); p--)
    785 			;
    786 		iscmd = p < 0 || strchr(";|&()`", buf[p]);
    787 		if (iscmd) {
    788 			/* If command has a /, path, etc. is not searched;
    789 			 * only current directory is searched, which is just
    790 			 * like file globbing.
    791 			 */
    792 			for (p = start; p < end; p++)
    793 				if (ISDIRSEP(buf[p]))
    794 					break;
    795 			iscmd = p == end;
    796 		}
    797 		*is_commandp = iscmd;
    798 	}
    799 
    800 	*startp = start;
    801 
    802 	return end - start;
    803 }
    804 
    805 int
    806 x_cf_glob(flags, buf, buflen, pos, startp, endp, wordsp, is_commandp)
    807 	int flags;
    808 	const char *buf;
    809 	int buflen;
    810 	int pos;
    811 	int *startp;
    812 	int *endp;
    813 	char ***wordsp;
    814 	int *is_commandp;
    815 {
    816 	int len;
    817 	int nwords;
    818 	char **words;
    819 	int is_command;
    820 
    821 	len = x_locate_word(buf, buflen, pos, startp, &is_command);
    822 	if (!(flags & XCF_COMMAND))
    823 		is_command = 0;
    824 	/* Don't do command globing on zero length strings - it takes too
    825 	 * long and isn't very useful.  File globs are more likely to be
    826 	 * useful, so allow these.
    827 	 */
    828 	if (len == 0 && is_command)
    829 		return 0;
    830 
    831 	nwords = (is_command ? x_command_glob : x_file_glob)(flags,
    832 				    buf + *startp, len, &words);
    833 	if (nwords == 0) {
    834 		*wordsp = (char **) 0;
    835 		return 0;
    836 	}
    837 
    838 	if (is_commandp)
    839 		*is_commandp = is_command;
    840 	*wordsp = words;
    841 	*endp = *startp + len;
    842 
    843 	return nwords;
    844 }
    845 
    846 /* Given a string, copy it and possibly add a '*' to the end.  The
    847  * new string is returned.
    848  */
    849 static char *
    850 add_glob(str, slen)
    851 	const char *str;
    852 	int slen;
    853 {
    854 	char *toglob;
    855 	char *s;
    856 	bool_t saw_slash = FALSE;
    857 
    858 	if (slen < 0)
    859 		return (char *) 0;
    860 
    861 	toglob = str_nsave(str, slen + 1, ATEMP); /* + 1 for "*" */
    862 	toglob[slen] = '\0';
    863 
    864 	/*
    865 	 * If the pathname contains a wildcard (an unquoted '*',
    866 	 * '?', or '['), or a ~username
    867 	 * with no trailing slash, then it is globbed based on that
    868 	 * value (i.e., without the appended '*').
    869 	 */
    870 	for (s = toglob; *s; s++) {
    871 		if (*s == '\\' && s[1])
    872 			s++;
    873 		else if (*s == '*' || *s == '[' || *s == '?'
    874 			 || (s[1] == '(' /*)*/ && strchr("*+?@!", *s)))
    875 			break;
    876 		else if (ISDIRSEP(*s))
    877 			saw_slash = TRUE;
    878 	}
    879 	if (!*s && (*toglob != '~' || saw_slash)) {
    880 		toglob[slen] = '*';
    881 		toglob[slen + 1] = '\0';
    882 	}
    883 
    884 	return toglob;
    885 }
    886 
    887 /*
    888  * Find longest common prefix
    889  */
    890 int
    891 x_longest_prefix(nwords, words)
    892 	int nwords;
    893 	char *const *words;
    894 {
    895 	int i, j;
    896 	int prefix_len;
    897 	char *p;
    898 
    899 	if (nwords <= 0)
    900 		return 0;
    901 
    902 	prefix_len = strlen(words[0]);
    903 	for (i = 1; i < nwords; i++)
    904 		for (j = 0, p = words[i]; j < prefix_len; j++)
    905 			if (FILECHCONV((unsigned char)p[j])
    906 			    != FILECHCONV((unsigned char)words[0][j])) {
    907 				prefix_len = j;
    908 				break;
    909 			}
    910 	return prefix_len;
    911 }
    912 
    913 void
    914 x_free_words(nwords, words)
    915 	int nwords;
    916 	char **words;
    917 {
    918 	int i;
    919 
    920 	for (i = 0; i < nwords; i++)
    921 		if (words[i])
    922 			afree(words[i], ATEMP);
    923 	afree(words, ATEMP);
    924 }
    925 
    926 /* Return the offset of the basename of string s (which ends at se - need not
    927  * be null terminated).  Trailing slashes are ignored.  If s is just a slash,
    928  * then the offset is 0 (actually, length - 1).
    929  *	s		Return
    930  *	/etc		1
    931  *	/etc/		1
    932  *	/etc//		1
    933  *	/etc/fo		5
    934  *	foo		0
    935  *	///		2
    936  *			0
    937  */
    938 int
    939 x_basename(s, se)
    940 	const char *s;
    941 	const char *se;
    942 {
    943 	const char *p;
    944 
    945 	if (se == (char *) 0)
    946 		se = s + strlen(s);
    947 	if (s == se)
    948 		return 0;
    949 
    950 	/* Skip trailing slashes */
    951 	for (p = se - 1; p > s && ISDIRSEP(*p); p--)
    952 		;
    953 	for (; p > s && !ISDIRSEP(*p); p--)
    954 		;
    955 	if (ISDIRSEP(*p) && p + 1 < se)
    956 		p++;
    957 
    958 	return p - s;
    959 }
    960 
    961 /*
    962  *  Apply pattern matching to a table: all table entries that match a pattern
    963  * are added to wp.
    964  */
    965 static void
    966 glob_table(pat, wp, tp)
    967 	const char *pat;
    968 	XPtrV *wp;
    969 	struct table *tp;
    970 {
    971 	struct tstate ts;
    972 	struct tbl *te;
    973 
    974 	for (twalk(&ts, tp); (te = tnext(&ts)); ) {
    975 		if (gmatch(te->name, pat, FALSE))
    976 			XPput(*wp, str_save(te->name, ATEMP));
    977 	}
    978 }
    979 
    980 static void
    981 glob_path(flags, pat, wp, xpath)
    982 	int flags;
    983 	const char *pat;
    984 	XPtrV *wp;
    985 	const char *xpath;
    986 {
    987 	const char *sp, *p;
    988 	char *xp;
    989 	int staterr;
    990 	int pathlen;
    991 	int patlen;
    992 	int oldsize, newsize, i, j;
    993 	char **words;
    994 	XString xs;
    995 
    996 	patlen = strlen(pat) + 1;
    997 	sp = xpath;
    998 	Xinit(xs, xp, patlen + 128, ATEMP);
    999 	while (sp) {
   1000 		xp = Xstring(xs, xp);
   1001 		if (!(p = strchr(sp, PATHSEP)))
   1002 			p = sp + strlen(sp);
   1003 		pathlen = p - sp;
   1004 		if (pathlen) {
   1005 			/* Copy sp into xp, stuffing any MAGIC characters
   1006 			 * on the way
   1007 			 */
   1008 			const char *s = sp;
   1009 
   1010 			XcheckN(xs, xp, pathlen * 2);
   1011 			while (s < p) {
   1012 				if (ISMAGIC(*s))
   1013 					*xp++ = MAGIC;
   1014 				*xp++ = *s++;
   1015 			}
   1016 			*xp++ = DIRSEP;
   1017 			pathlen++;
   1018 		}
   1019 		sp = p;
   1020 		XcheckN(xs, xp, patlen);
   1021 		memcpy(xp, pat, patlen);
   1022 
   1023 		oldsize = XPsize(*wp);
   1024 		glob_str(Xstring(xs, xp), wp, 1); /* mark dirs */
   1025 		newsize = XPsize(*wp);
   1026 
   1027 		/* Check that each match is executable... */
   1028 		words = (char **) XPptrv(*wp);
   1029 		for (i = j = oldsize; i < newsize; i++) {
   1030 			staterr = 0;
   1031 			if ((search_access(words[i], X_OK, &staterr) >= 0)
   1032 			    || (staterr == EISDIR)) {
   1033 				words[j] = words[i];
   1034 				if (!(flags & XCF_FULLPATH))
   1035 					memmove(words[j], words[j] + pathlen,
   1036 						strlen(words[j] + pathlen) + 1);
   1037 				j++;
   1038 			} else
   1039 				afree(words[i], ATEMP);
   1040 		}
   1041 		wp->cur = (void **) &words[j];
   1042 
   1043 		if (!*sp++)
   1044 			break;
   1045 	}
   1046 	Xfree(xs, xp);
   1047 }
   1048 
   1049 /*
   1050  * if argument string contains any special characters, they will
   1051  * be escaped and the result will be put into edit buffer by
   1052  * keybinding-specific function
   1053  */
   1054 int
   1055 x_escape(s, len, putbuf_func)
   1056 	const char *s;
   1057 	size_t len;
   1058 	int (*putbuf_func) ARGS((const char *, size_t));
   1059 {
   1060 	size_t add, wlen;
   1061 	const char *ifs = str_val(local("IFS", 0));
   1062 	int rval=0;
   1063 
   1064 	for (add = 0, wlen = len; wlen - add > 0; add++) {
   1065 		if (strchr("\\$(){}[]?*&;#|<>\"'`", s[add]) || strchr(ifs, s[add])) {
   1066 			if (putbuf_func(s, add) != 0) {
   1067 				rval = -1;
   1068 				break;
   1069 			}
   1070 
   1071 			putbuf_func("\\", 1);
   1072 			putbuf_func(&s[add], 1);
   1073 
   1074 			add++;
   1075 			wlen -= add;
   1076 			s += add;
   1077 			add = -1; /* after the increment it will go to 0 */
   1078 		}
   1079 	}
   1080 	if (wlen > 0 && rval == 0)
   1081 		rval = putbuf_func(s, wlen);
   1082 
   1083 	return (rval);
   1084 }
   1085 #endif /* EDIT */
   1086