filecomplete.c revision 296175
190792Sgshapiro/*	$NetBSD: filecomplete.c,v 1.40 2016/02/17 19:47:49 christos Exp $	*/
2261363Sgshapiro
390792Sgshapiro/*-
490792Sgshapiro * Copyright (c) 1997 The NetBSD Foundation, Inc.
590792Sgshapiro * All rights reserved.
690792Sgshapiro *
790792Sgshapiro * This code is derived from software contributed to The NetBSD Foundation
890792Sgshapiro * by Jaromir Dolecek.
990792Sgshapiro *
1090792Sgshapiro * Redistribution and use in source and binary forms, with or without
1194334Sgshapiro * modification, are permitted provided that the following conditions
12266692Sgshapiro * are met:
1394334Sgshapiro * 1. Redistributions of source code must retain the above copyright
1490792Sgshapiro *    notice, this list of conditions and the following disclaimer.
1590792Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright
1690792Sgshapiro *    notice, this list of conditions and the following disclaimer in the
1790792Sgshapiro *    documentation and/or other materials provided with the distribution.
1890792Sgshapiro *
1990792Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2090792Sgshapiro * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2190792Sgshapiro * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2290792Sgshapiro * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23244833Sgshapiro * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2490792Sgshapiro * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2590792Sgshapiro * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2690792Sgshapiro * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27244833Sgshapiro * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28244833Sgshapiro * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29244833Sgshapiro * POSSIBILITY OF SUCH DAMAGE.
30244833Sgshapiro */
31244833Sgshapiro
32243649Sume#include "config.h"
33243649Sume#if !defined(lint) && !defined(SCCSID)
34243649Sume__RCSID("$NetBSD: filecomplete.c,v 1.40 2016/02/17 19:47:49 christos Exp $");
35243649Sume#endif /* not lint && not SCCSID */
3690792Sgshapiro#include <sys/cdefs.h>
3790792Sgshapiro__FBSDID("$FreeBSD: head/lib/libedit/filecomplete.c 296175 2016-02-29 00:15:25Z pfg $");
3890792Sgshapiro
3998121Sgshapiro#include <sys/types.h>
4090792Sgshapiro#include <sys/stat.h>
4190792Sgshapiro#include <dirent.h>
4290792Sgshapiro#include <errno.h>
4390792Sgshapiro#include <fcntl.h>
4490792Sgshapiro#include <limits.h>
4590792Sgshapiro#include <pwd.h>
4690792Sgshapiro#include <stdio.h>
4790792Sgshapiro#include <stdlib.h>
4890792Sgshapiro#include <string.h>
4990792Sgshapiro#include <unistd.h>
5090792Sgshapiro
5190792Sgshapiro#include "el.h"
5290792Sgshapiro#include "filecomplete.h"
5390792Sgshapiro
5490792Sgshapirostatic const Char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@',
5590792Sgshapiro    '$', '>', '<', '=', ';', '|', '&', '{', '(', '\0' };
5690792Sgshapiro/* Tilde is deliberately omitted here, we treat it specially. */
5790792Sgshapirostatic const Char extra_quote_chars[] = { ')', '}', '*', '?', '[', '$', '\0' };
5890792Sgshapiro
59243649Sume
6090792Sgshapiro/********************************/
6190792Sgshapiro/* completion functions */
6290792Sgshapiro
6390792Sgshapiro/*
6490792Sgshapiro * does tilde expansion of strings of type ``~user/foo''
6590792Sgshapiro * if ``user'' isn't valid user name or ``txt'' doesn't start
6690792Sgshapiro * w/ '~', returns pointer to strdup()ed copy of ``txt''
6790792Sgshapiro *
6890792Sgshapiro * it's the caller's responsibility to free() the returned string
6990792Sgshapiro */
7090792Sgshapirochar *
7190792Sgshapirofn_tilde_expand(const char *txt)
7290792Sgshapiro{
7390792Sgshapiro#if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT)
7490792Sgshapiro	struct passwd pwres;
7590792Sgshapiro	char pwbuf[1024];
7690792Sgshapiro#endif
7790792Sgshapiro	struct passwd *pass;
7890792Sgshapiro	char *temp;
7990792Sgshapiro	size_t len = 0;
80243649Sume
81243649Sume	if (txt[0] != '~')
8290792Sgshapiro		return strdup(txt);
8390792Sgshapiro
8490792Sgshapiro	temp = strchr(txt + 1, '/');
8590792Sgshapiro	if (temp == NULL) {
8690792Sgshapiro		temp = strdup(txt + 1);
8790792Sgshapiro		if (temp == NULL)
8890792Sgshapiro			return NULL;
8990792Sgshapiro	} else {
9090792Sgshapiro		/* text until string after slash */
9190792Sgshapiro		len = (size_t)(temp - txt + 1);
9290792Sgshapiro		temp = el_malloc(len * sizeof(*temp));
9390792Sgshapiro		if (temp == NULL)
9490792Sgshapiro			return NULL;
9590792Sgshapiro		(void)strncpy(temp, txt + 1, len - 2);
9690792Sgshapiro		temp[len - 2] = '\0';
9790792Sgshapiro	}
9890792Sgshapiro	if (temp[0] == 0) {
9990792Sgshapiro#ifdef HAVE_GETPW_R_POSIX
10090792Sgshapiro		if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf),
10190792Sgshapiro		    &pass) != 0)
10290792Sgshapiro			pass = NULL;
10390792Sgshapiro#elif HAVE_GETPW_R_DRAFT
10490792Sgshapiro		pass = getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf));
10590792Sgshapiro#else
10690792Sgshapiro		pass = getpwuid(getuid());
10790792Sgshapiro#endif
108243649Sume	} else {
10990792Sgshapiro#ifdef HAVE_GETPW_R_POSIX
11090792Sgshapiro		if (getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf), &pass) != 0)
11190792Sgshapiro			pass = NULL;
11290792Sgshapiro#elif HAVE_GETPW_R_DRAFT
11390792Sgshapiro		pass = getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf));
11490792Sgshapiro#else
11590792Sgshapiro		pass = getpwnam(temp);
11690792Sgshapiro#endif
11790792Sgshapiro	}
11890792Sgshapiro	el_free(temp);		/* value no more needed */
11990792Sgshapiro	if (pass == NULL)
12090792Sgshapiro		return strdup(txt);
12190792Sgshapiro
12290792Sgshapiro	/* update pointer txt to point at string immedially following */
12390792Sgshapiro	/* first slash */
12490792Sgshapiro	txt += len;
12590792Sgshapiro
12690792Sgshapiro	len = strlen(pass->pw_dir) + 1 + strlen(txt) + 1;
12790792Sgshapiro	temp = el_malloc(len * sizeof(*temp));
12890792Sgshapiro	if (temp == NULL)
12990792Sgshapiro		return NULL;
13090792Sgshapiro	(void)snprintf(temp, len, "%s/%s", pass->pw_dir, txt);
13190792Sgshapiro
13290792Sgshapiro	return temp;
13390792Sgshapiro}
13490792Sgshapiro
13590792Sgshapiro
13690792Sgshapiro/*
13790792Sgshapiro * return first found file name starting by the ``text'' or NULL if no
13890792Sgshapiro * such file can be found
13990792Sgshapiro * value of ``state'' is ignored
14090792Sgshapiro *
14190792Sgshapiro * it's the caller's responsibility to free the returned string
14290792Sgshapiro */
14390792Sgshapirochar *
14490792Sgshapirofn_filename_completion_function(const char *text, int state)
14590792Sgshapiro{
14690792Sgshapiro	static DIR *dir = NULL;
14790792Sgshapiro	static char *filename = NULL, *dirname = NULL, *dirpath = NULL;
14890792Sgshapiro	static size_t filename_len = 0;
14990792Sgshapiro	struct dirent *entry;
15090792Sgshapiro	char *temp;
15190792Sgshapiro	size_t len;
15290792Sgshapiro
15390792Sgshapiro	if (state == 0 || dir == NULL) {
15490792Sgshapiro		temp = strrchr(text, '/');
15590792Sgshapiro		if (temp) {
15690792Sgshapiro			char *nptr;
15790792Sgshapiro			temp++;
15890792Sgshapiro			nptr = el_realloc(filename, (strlen(temp) + 1) *
15990792Sgshapiro			    sizeof(*nptr));
16090792Sgshapiro			if (nptr == NULL) {
16190792Sgshapiro				el_free(filename);
16290792Sgshapiro				filename = NULL;
16390792Sgshapiro				return NULL;
16490792Sgshapiro			}
16590792Sgshapiro			filename = nptr;
16690792Sgshapiro			(void)strcpy(filename, temp);
16790792Sgshapiro			len = (size_t)(temp - text);	/* including last slash */
16890792Sgshapiro
16990792Sgshapiro			nptr = el_realloc(dirname, (len + 1) *
17090792Sgshapiro			    sizeof(*nptr));
17190792Sgshapiro			if (nptr == NULL) {
17290792Sgshapiro				el_free(dirname);
17390792Sgshapiro				dirname = NULL;
17490792Sgshapiro				return NULL;
17590792Sgshapiro			}
17690792Sgshapiro			dirname = nptr;
17790792Sgshapiro			(void)strncpy(dirname, text, len);
17890792Sgshapiro			dirname[len] = '\0';
17990792Sgshapiro		} else {
18090792Sgshapiro			el_free(filename);
18190792Sgshapiro			if (*text == 0)
18290792Sgshapiro				filename = NULL;
18390792Sgshapiro			else {
18490792Sgshapiro				filename = strdup(text);
18590792Sgshapiro				if (filename == NULL)
18690792Sgshapiro					return NULL;
18790792Sgshapiro			}
18890792Sgshapiro			el_free(dirname);
18990792Sgshapiro			dirname = NULL;
19090792Sgshapiro		}
19190792Sgshapiro
19290792Sgshapiro		if (dir != NULL) {
19390792Sgshapiro			(void)closedir(dir);
19490792Sgshapiro			dir = NULL;
19590792Sgshapiro		}
19690792Sgshapiro
19790792Sgshapiro		/* support for ``~user'' syntax */
19890792Sgshapiro
19990792Sgshapiro		el_free(dirpath);
20090792Sgshapiro		dirpath = NULL;
20190792Sgshapiro		if (dirname == NULL) {
20290792Sgshapiro			if ((dirname = strdup("")) == NULL)
20390792Sgshapiro				return NULL;
20490792Sgshapiro			dirpath = strdup("./");
20590792Sgshapiro		} else if (*dirname == '~')
20690792Sgshapiro			dirpath = fn_tilde_expand(dirname);
20790792Sgshapiro		else
20890792Sgshapiro			dirpath = strdup(dirname);
20990792Sgshapiro
21090792Sgshapiro		if (dirpath == NULL)
21190792Sgshapiro			return NULL;
21290792Sgshapiro
21390792Sgshapiro		dir = opendir(dirpath);
21498121Sgshapiro		if (!dir)
21598121Sgshapiro			return NULL;	/* cannot open the directory */
21698121Sgshapiro
217132943Sgshapiro		/* will be used in cycle */
21898121Sgshapiro		filename_len = filename ? strlen(filename) : 0;
21998121Sgshapiro	}
22098121Sgshapiro
22198121Sgshapiro	/* find the match */
22298121Sgshapiro	while ((entry = readdir(dir)) != NULL) {
22398121Sgshapiro		/* skip . and .. */
22498121Sgshapiro		if (entry->d_name[0] == '.' && (!entry->d_name[1]
22598121Sgshapiro		    || (entry->d_name[1] == '.' && !entry->d_name[2])))
22698121Sgshapiro			continue;
22798121Sgshapiro		if (filename_len == 0)
22898121Sgshapiro			break;
22998121Sgshapiro		/* otherwise, get first entry where first */
23098121Sgshapiro		/* filename_len characters are equal	  */
23198121Sgshapiro		if (entry->d_name[0] == filename[0]
23298121Sgshapiro#if HAVE_STRUCT_DIRENT_D_NAMLEN
23398121Sgshapiro		    && entry->d_namlen >= filename_len
23498121Sgshapiro#else
23598121Sgshapiro		    && strlen(entry->d_name) >= filename_len
23698121Sgshapiro#endif
23798121Sgshapiro		    && strncmp(entry->d_name, filename,
23898121Sgshapiro			filename_len) == 0)
23998121Sgshapiro			break;
24098121Sgshapiro	}
24198121Sgshapiro
24298121Sgshapiro	if (entry) {		/* match found */
24398121Sgshapiro
24498121Sgshapiro#if HAVE_STRUCT_DIRENT_D_NAMLEN
24598121Sgshapiro		len = entry->d_namlen;
24698121Sgshapiro#else
24798121Sgshapiro		len = strlen(entry->d_name);
24898121Sgshapiro#endif
24998121Sgshapiro
250141858Sgshapiro		len = strlen(dirname) + len + 1;
251141858Sgshapiro		temp = el_malloc(len * sizeof(*temp));
252141858Sgshapiro		if (temp == NULL)
25398121Sgshapiro			return NULL;
25498121Sgshapiro		(void)snprintf(temp, len, "%s%s", dirname, entry->d_name);
25598121Sgshapiro	} else {
25698121Sgshapiro		(void)closedir(dir);
25798121Sgshapiro		dir = NULL;
25898121Sgshapiro		temp = NULL;
25998121Sgshapiro	}
26098121Sgshapiro
261141858Sgshapiro	return temp;
262141858Sgshapiro}
263141858Sgshapiro
264141858Sgshapiro
265141858Sgshapirostatic const char *
26698121Sgshapiroappend_char_function(const char *name)
267168515Sgshapiro{
26898121Sgshapiro	struct stat stbuf;
26998121Sgshapiro	char *expname = *name == '~' ? fn_tilde_expand(name) : NULL;
27098121Sgshapiro	const char *rs = " ";
27198121Sgshapiro
27298121Sgshapiro	if (stat(expname ? expname : name, &stbuf) == -1)
27398121Sgshapiro		goto out;
27498121Sgshapiro	if (S_ISDIR(stbuf.st_mode))
27598841Sgshapiro		rs = "/";
27698841Sgshapiroout:
27798121Sgshapiro	if (expname)
27898841Sgshapiro		el_free(expname);
27998121Sgshapiro	return rs;
28098121Sgshapiro}
281168515Sgshapiro/*
28298121Sgshapiro * returns list of completions for text given
28398121Sgshapiro * non-static for readline.
28498121Sgshapiro */
28598121Sgshapirochar ** completion_matches(const char *, char *(*)(const char *, int));
28698121Sgshapirochar **
28798121Sgshapirocompletion_matches(const char *text, char *(*genfunc)(const char *, int))
28898121Sgshapiro{
28998121Sgshapiro	char **match_list = NULL, *retstr, *prevstr;
29098121Sgshapiro	size_t match_list_len, max_equal, which, i;
29198121Sgshapiro	size_t matches;
29298121Sgshapiro
29390792Sgshapiro	matches = 0;
294	match_list_len = 1;
295	while ((retstr = (*genfunc) (text, (int)matches)) != NULL) {
296		/* allow for list terminator here */
297		if (matches + 3 >= match_list_len) {
298			char **nmatch_list;
299			while (matches + 3 >= match_list_len)
300				match_list_len <<= 1;
301			nmatch_list = el_realloc(match_list,
302			    match_list_len * sizeof(*nmatch_list));
303			if (nmatch_list == NULL) {
304				el_free(match_list);
305				return NULL;
306			}
307			match_list = nmatch_list;
308
309		}
310		match_list[++matches] = retstr;
311	}
312
313	if (!match_list)
314		return NULL;	/* nothing found */
315
316	/* find least denominator and insert it to match_list[0] */
317	which = 2;
318	prevstr = match_list[1];
319	max_equal = strlen(prevstr);
320	for (; which <= matches; which++) {
321		for (i = 0; i < max_equal &&
322		    prevstr[i] == match_list[which][i]; i++)
323			continue;
324		max_equal = i;
325	}
326
327	retstr = el_malloc((max_equal + 1) * sizeof(*retstr));
328	if (retstr == NULL) {
329		el_free(match_list);
330		return NULL;
331	}
332	(void)strncpy(retstr, match_list[1], max_equal);
333	retstr[max_equal] = '\0';
334	match_list[0] = retstr;
335
336	/* add NULL as last pointer to the array */
337	match_list[matches + 1] = NULL;
338
339	return match_list;
340}
341
342/*
343 * Sort function for qsort(). Just wrapper around strcasecmp().
344 */
345static int
346_fn_qsort_string_compare(const void *i1, const void *i2)
347{
348	const char *s1 = ((const char * const *)i1)[0];
349	const char *s2 = ((const char * const *)i2)[0];
350
351	return strcasecmp(s1, s2);
352}
353
354/*
355 * Display list of strings in columnar format on readline's output stream.
356 * 'matches' is list of strings, 'num' is number of strings in 'matches',
357 * 'width' is maximum length of string in 'matches'.
358 *
359 * matches[0] is not one of the match strings, but it is counted in
360 * num, so the strings are matches[1] *through* matches[num-1].
361 */
362void
363fn_display_match_list (EditLine *el, char **matches, size_t num, size_t width)
364{
365	size_t line, lines, col, cols, thisguy;
366	int screenwidth = el->el_terminal.t_size.h;
367
368	/* Ignore matches[0]. Avoid 1-based array logic below. */
369	matches++;
370	num--;
371
372	/*
373	 * Find out how many entries can be put on one line; count
374	 * with one space between strings the same way it's printed.
375	 */
376	cols = (size_t)screenwidth / (width + 1);
377	if (cols == 0)
378		cols = 1;
379
380	/* how many lines of output, rounded up */
381	lines = (num + cols - 1) / cols;
382
383	/* Sort the items. */
384	qsort(matches, num, sizeof(char *), _fn_qsort_string_compare);
385
386	/*
387	 * On the ith line print elements i, i+lines, i+lines*2, etc.
388	 */
389	for (line = 0; line < lines; line++) {
390		for (col = 0; col < cols; col++) {
391			thisguy = line + col * lines;
392			if (thisguy >= num)
393				break;
394			(void)fprintf(el->el_outfile, "%s%-*s",
395			    col == 0 ? "" : " ", (int)width, matches[thisguy]);
396		}
397		(void)fprintf(el->el_outfile, "\n");
398	}
399}
400
401/*
402 * Complete the word at or before point,
403 * 'what_to_do' says what to do with the completion.
404 * \t   means do standard completion.
405 * `?' means list the possible completions.
406 * `*' means insert all of the possible completions.
407 * `!' means to do standard completion, and list all possible completions if
408 * there is more than one.
409 *
410 * Note: '*' support is not implemented
411 *       '!' could never be invoked
412 */
413int
414fn_complete(EditLine *el,
415	char *(*complet_func)(const char *, int),
416	char **(*attempted_completion_function)(const char *, int, int),
417	const Char *word_break, const Char *special_prefixes,
418	const char *(*app_func)(const char *), size_t query_items,
419	int *completion_type, int *over, int *point, int *end,
420	const Char *(*find_word_start_func)(const Char *, const Char *),
421	Char *(*dequoting_func)(const Char *),
422	char *(*quoting_func)(const char *))
423{
424	const TYPE(LineInfo) *li;
425	Char *temp;
426	Char *dequoted_temp;
427        char **matches;
428	const Char *ctemp;
429	size_t len;
430	int what_to_do = '\t';
431	int retval = CC_NORM;
432
433	if (el->el_state.lastcmd == el->el_state.thiscmd)
434		what_to_do = '?';
435
436	/* readline's rl_complete() has to be told what we did... */
437	if (completion_type != NULL)
438		*completion_type = what_to_do;
439
440	if (!complet_func)
441		complet_func = fn_filename_completion_function;
442	if (!app_func)
443		app_func = append_char_function;
444
445	/* We now look backwards for the start of a filename/variable word */
446	li = FUN(el,line)(el);
447	if (find_word_start_func)
448		ctemp = find_word_start_func(li->buffer, li->cursor);
449	else {
450		ctemp = li->cursor;
451		while (ctemp > li->buffer
452		    && !Strchr(word_break, ctemp[-1])
453		    && (!special_prefixes || !Strchr(special_prefixes, ctemp[-1]) ) )
454			ctemp--;
455	}
456
457	len = (size_t)(li->cursor - ctemp);
458	temp = el_malloc((len + 1) * sizeof(*temp));
459	(void)Strncpy(temp, ctemp, len);
460	temp[len] = '\0';
461
462	if (dequoting_func) {
463		dequoted_temp = dequoting_func(temp);
464		if (dequoted_temp == NULL)
465			return retval;
466	} else
467		dequoted_temp = NULL;
468
469	/* these can be used by function called in completion_matches() */
470	/* or (*attempted_completion_function)() */
471	if (point != 0)
472		*point = (int)(li->cursor - li->buffer);
473	if (end != NULL)
474		*end = (int)(li->lastchar - li->buffer);
475
476	if (attempted_completion_function) {
477		int cur_off = (int)(li->cursor - li->buffer);
478		matches = (*attempted_completion_function)(
479		    ct_encode_string(dequoted_temp ? dequoted_temp : temp,
480		        &el->el_scratch),
481		    cur_off - (int)len, cur_off);
482	} else
483		matches = 0;
484	if (!attempted_completion_function ||
485	    (over != NULL && !*over && !matches))
486		matches = completion_matches(
487		    ct_encode_string(dequoted_temp ? dequoted_temp : temp,
488		        &el->el_scratch), complet_func);
489
490	if (over != NULL)
491		*over = 0;
492
493	if (matches) {
494		int i;
495		size_t matches_num, maxlen, match_len, match_display=1;
496
497		retval = CC_REFRESH;
498		/*
499		 * Only replace the completed string with common part of
500		 * possible matches if there is possible completion.
501		 */
502		if (matches[0][0] != '\0') {
503			char *quoted_match;
504			if (quoting_func) {
505				quoted_match = quoting_func(matches[0]);
506				if (quoted_match == NULL)
507					goto free_matches;
508			} else
509				quoted_match = NULL;
510			el_deletestr(el, (int) len);
511			FUN(el,insertstr)(el,
512			    ct_decode_string(quoted_match ? quoted_match :
513			        matches[0] , &el->el_scratch));
514		}
515
516		if (what_to_do == '?')
517			goto display_matches;
518
519		if (matches[2] == NULL &&
520		    (matches[1] == NULL || strcmp(matches[0], matches[1]) == 0)) {
521			/*
522			 * We found exact match. Add a space after
523			 * it, unless we do filename completion and the
524			 * object is a directory.
525			 */
526			FUN(el,insertstr)(el,
527			    ct_decode_string((*app_func)(matches[0]),
528			    &el->el_scratch));
529		} else if (what_to_do == '!') {
530    display_matches:
531			/*
532			 * More than one match and requested to list possible
533			 * matches.
534			 */
535
536			for(i = 1, maxlen = 0; matches[i]; i++) {
537				match_len = strlen(matches[i]);
538				if (match_len > maxlen)
539					maxlen = match_len;
540			}
541			/* matches[1] through matches[i-1] are available */
542			matches_num = (size_t)(i - 1);
543
544			/* newline to get on next line from command line */
545			(void)fprintf(el->el_outfile, "\n");
546
547			/*
548			 * If there are too many items, ask user for display
549			 * confirmation.
550			 */
551			if (matches_num > query_items) {
552				(void)fprintf(el->el_outfile,
553				    "Display all %zu possibilities? (y or n) ",
554				    matches_num);
555				(void)fflush(el->el_outfile);
556				if (getc(stdin) != 'y')
557					match_display = 0;
558				(void)fprintf(el->el_outfile, "\n");
559			}
560
561			if (match_display) {
562				/*
563				 * Interface of this function requires the
564				 * strings be matches[1..num-1] for compat.
565				 * We have matches_num strings not counting
566				 * the prefix in matches[0], so we need to
567				 * add 1 to matches_num for the call.
568				 */
569				fn_display_match_list(el, matches,
570				    matches_num+1, maxlen);
571			}
572			retval = CC_REDISPLAY;
573		} else if (matches[0][0]) {
574			/*
575			 * There was some common match, but the name was
576			 * not complete enough. Next tab will print possible
577			 * completions.
578			 */
579			el_beep(el);
580		} else {
581			/* lcd is not a valid object - further specification */
582			/* is needed */
583			el_beep(el);
584			retval = CC_NORM;
585		}
586
587free_matches:
588		/* free elements of array and the array itself */
589		for (i = 0; matches[i]; i++)
590			el_free(matches[i]);
591		el_free(matches);
592		matches = NULL;
593	}
594	free(dequoted_temp);
595	el_free(temp);
596	return retval;
597}
598
599/*
600 * el-compatible wrapper around rl_complete; needed for key binding
601 */
602/* ARGSUSED */
603unsigned char
604_el_fn_complete(EditLine *el, int ch __attribute__((__unused__)))
605{
606	return (unsigned char)fn_complete(el, NULL, NULL,
607	    break_chars, NULL, NULL, (size_t)100,
608	    NULL, NULL, NULL, NULL,
609	    NULL, NULL, NULL);
610}
611
612static const Char *
613sh_find_word_start(const Char *buffer, const Char *cursor)
614{
615	const Char *word_start = buffer;
616
617	while (buffer < cursor) {
618		if (*buffer == '\\')
619			buffer++;
620		else if (Strchr(break_chars, *buffer))
621			word_start = buffer + 1;
622		buffer++;
623	}
624	return word_start;
625}
626
627static char *
628sh_quote(const char *str)
629{
630	const char *src;
631	int extra_len = 0;
632	char *quoted_str, *dst;
633
634	 for (src = str; *src != '\0'; src++)
635		if (Strchr(break_chars, *src) ||
636		    Strchr(extra_quote_chars, *src))
637			extra_len++;
638
639	quoted_str = malloc(sizeof(*quoted_str) *
640	     (strlen(str) + extra_len + 1));
641	if (quoted_str == NULL)
642		return NULL;
643
644	dst = quoted_str;
645	for (src = str; *src != '\0'; src++) {
646		if (Strchr(break_chars, *src) ||
647		    Strchr(extra_quote_chars, *src))
648			*dst++ = '\\';
649		*dst++ = *src;
650	}
651	*dst = '\0';
652
653	return quoted_str;
654}
655
656static Char *
657sh_dequote(const Char *str)
658{
659	Char *dequoted_str, *dst;
660
661	/* save extra space to replace \~ with ./~ */
662	dequoted_str = malloc(sizeof(*dequoted_str) * (Strlen(str) + 1 + 1));
663	if (dequoted_str == NULL)
664		return NULL;
665
666	dst = dequoted_str;
667
668	/* dequote \~ at start as ./~ */
669	if (*str == '\\' && str[1] == '~') {
670		str++;
671		*dst++ = '.';
672		*dst++ = '/';
673	}
674
675	while (*str) {
676		if (*str == '\\')
677			str++;
678		if (*str)
679			*dst++ = *str++;
680	}
681	*dst = '\0';
682
683	return dequoted_str;
684}
685
686/*
687 * completion function using sh quoting rules; for key binding
688 */
689/* ARGSUSED */
690unsigned char
691_el_fn_sh_complete(EditLine *el, int ch __attribute__((__unused__)))
692{
693	return (unsigned char)fn_complete(el, NULL, NULL,
694	    break_chars, NULL, NULL, 100,
695	    NULL, NULL, NULL, NULL,
696	    sh_find_word_start, sh_dequote, sh_quote);
697}
698