xref: /src/contrib/libedit/filecomplete.c (revision 136d69caf03bc38de95c4df34c5a683e9ce81bfa)
1b3884193SBaptiste Daroussin /*	$NetBSD: filecomplete.c,v 1.73 2023/04/25 17:51:32 christos Exp $	*/
275d35a37SDavid E. O'Brien 
375d35a37SDavid E. O'Brien /*-
475d35a37SDavid E. O'Brien  * Copyright (c) 1997 The NetBSD Foundation, Inc.
575d35a37SDavid E. O'Brien  * All rights reserved.
675d35a37SDavid E. O'Brien  *
775d35a37SDavid E. O'Brien  * This code is derived from software contributed to The NetBSD Foundation
875d35a37SDavid E. O'Brien  * by Jaromir Dolecek.
975d35a37SDavid E. O'Brien  *
1075d35a37SDavid E. O'Brien  * Redistribution and use in source and binary forms, with or without
1175d35a37SDavid E. O'Brien  * modification, are permitted provided that the following conditions
1275d35a37SDavid E. O'Brien  * are met:
1375d35a37SDavid E. O'Brien  * 1. Redistributions of source code must retain the above copyright
1475d35a37SDavid E. O'Brien  *    notice, this list of conditions and the following disclaimer.
1575d35a37SDavid E. O'Brien  * 2. Redistributions in binary form must reproduce the above copyright
1675d35a37SDavid E. O'Brien  *    notice, this list of conditions and the following disclaimer in the
1775d35a37SDavid E. O'Brien  *    documentation and/or other materials provided with the distribution.
1875d35a37SDavid E. O'Brien  *
1975d35a37SDavid E. O'Brien  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2075d35a37SDavid E. O'Brien  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2175d35a37SDavid E. O'Brien  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2275d35a37SDavid E. O'Brien  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2375d35a37SDavid E. O'Brien  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2475d35a37SDavid E. O'Brien  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2575d35a37SDavid E. O'Brien  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2675d35a37SDavid E. O'Brien  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2775d35a37SDavid E. O'Brien  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2875d35a37SDavid E. O'Brien  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2975d35a37SDavid E. O'Brien  * POSSIBILITY OF SUCH DAMAGE.
3075d35a37SDavid E. O'Brien  */
3175d35a37SDavid E. O'Brien 
3275d35a37SDavid E. O'Brien #include "config.h"
3375d35a37SDavid E. O'Brien #if !defined(lint) && !defined(SCCSID)
34b3884193SBaptiste Daroussin __RCSID("$NetBSD: filecomplete.c,v 1.73 2023/04/25 17:51:32 christos Exp $");
3575d35a37SDavid E. O'Brien #endif /* not lint && not SCCSID */
3675d35a37SDavid E. O'Brien 
3775d35a37SDavid E. O'Brien #include <sys/types.h>
3875d35a37SDavid E. O'Brien #include <sys/stat.h>
3975d35a37SDavid E. O'Brien #include <dirent.h>
4075d35a37SDavid E. O'Brien #include <errno.h>
4175d35a37SDavid E. O'Brien #include <fcntl.h>
424593a7cdSPedro F. Giffuni #include <limits.h>
434593a7cdSPedro F. Giffuni #include <pwd.h>
444593a7cdSPedro F. Giffuni #include <stdio.h>
454593a7cdSPedro F. Giffuni #include <stdlib.h>
464593a7cdSPedro F. Giffuni #include <string.h>
474593a7cdSPedro F. Giffuni #include <unistd.h>
4849350233SEd Schouten 
4975d35a37SDavid E. O'Brien #include "el.h"
5075d35a37SDavid E. O'Brien #include "filecomplete.h"
5175d35a37SDavid E. O'Brien 
528c36b043SPedro F. Giffuni static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{(";
5375d35a37SDavid E. O'Brien 
5475d35a37SDavid E. O'Brien /********************************/
5575d35a37SDavid E. O'Brien /* completion functions */
5675d35a37SDavid E. O'Brien 
5775d35a37SDavid E. O'Brien /*
5875d35a37SDavid E. O'Brien  * does tilde expansion of strings of type ``~user/foo''
5975d35a37SDavid E. O'Brien  * if ``user'' isn't valid user name or ``txt'' doesn't start
6075d35a37SDavid E. O'Brien  * w/ '~', returns pointer to strdup()ed copy of ``txt''
6175d35a37SDavid E. O'Brien  *
625b6fd65dSBaptiste Daroussin  * it's the caller's responsibility to free() the returned string
6375d35a37SDavid E. O'Brien  */
6475d35a37SDavid E. O'Brien char *
fn_tilde_expand(const char * txt)6575d35a37SDavid E. O'Brien fn_tilde_expand(const char *txt)
6675d35a37SDavid E. O'Brien {
6749350233SEd Schouten #if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT)
6849350233SEd Schouten 	struct passwd pwres;
6949350233SEd Schouten 	char pwbuf[1024];
7049350233SEd Schouten #endif
7149350233SEd Schouten 	struct passwd *pass;
726da940f2SBaptiste Daroussin 	const char *pos;
7375d35a37SDavid E. O'Brien 	char *temp;
7475d35a37SDavid E. O'Brien 	size_t len = 0;
7575d35a37SDavid E. O'Brien 
7675d35a37SDavid E. O'Brien 	if (txt[0] != '~')
7749350233SEd Schouten 		return strdup(txt);
7875d35a37SDavid E. O'Brien 
796da940f2SBaptiste Daroussin 	pos = strchr(txt + 1, '/');
806da940f2SBaptiste Daroussin 	if (pos == NULL) {
8175d35a37SDavid E. O'Brien 		temp = strdup(txt + 1);
8275d35a37SDavid E. O'Brien 		if (temp == NULL)
8375d35a37SDavid E. O'Brien 			return NULL;
8475d35a37SDavid E. O'Brien 	} else {
8549350233SEd Schouten 		/* text until string after slash */
866da940f2SBaptiste Daroussin 		len = (size_t)(pos - txt + 1);
8731506252SBaptiste Daroussin 		temp = el_calloc(len, sizeof(*temp));
8875d35a37SDavid E. O'Brien 		if (temp == NULL)
8975d35a37SDavid E. O'Brien 			return NULL;
90881fcf9cSBaptiste Daroussin 		(void)strlcpy(temp, txt + 1, len - 1);
9175d35a37SDavid E. O'Brien 	}
9275d35a37SDavid E. O'Brien 	if (temp[0] == 0) {
9349350233SEd Schouten #ifdef HAVE_GETPW_R_POSIX
9449350233SEd Schouten 		if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf),
9549350233SEd Schouten 		    &pass) != 0)
9675d35a37SDavid E. O'Brien 			pass = NULL;
9749350233SEd Schouten #elif HAVE_GETPW_R_DRAFT
9849350233SEd Schouten 		pass = getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf));
9949350233SEd Schouten #else
10049350233SEd Schouten 		pass = getpwuid(getuid());
10149350233SEd Schouten #endif
10275d35a37SDavid E. O'Brien 	} else {
10349350233SEd Schouten #ifdef HAVE_GETPW_R_POSIX
10475d35a37SDavid E. O'Brien 		if (getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf), &pass) != 0)
10575d35a37SDavid E. O'Brien 			pass = NULL;
10649350233SEd Schouten #elif HAVE_GETPW_R_DRAFT
10749350233SEd Schouten 		pass = getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf));
10849350233SEd Schouten #else
10949350233SEd Schouten 		pass = getpwnam(temp);
11049350233SEd Schouten #endif
11175d35a37SDavid E. O'Brien 	}
11249350233SEd Schouten 	el_free(temp);		/* value no more needed */
11375d35a37SDavid E. O'Brien 	if (pass == NULL)
11449350233SEd Schouten 		return strdup(txt);
11575d35a37SDavid E. O'Brien 
11675d35a37SDavid E. O'Brien 	/* update pointer txt to point at string immedially following */
11775d35a37SDavid E. O'Brien 	/* first slash */
11875d35a37SDavid E. O'Brien 	txt += len;
11975d35a37SDavid E. O'Brien 
12049350233SEd Schouten 	len = strlen(pass->pw_dir) + 1 + strlen(txt) + 1;
12131506252SBaptiste Daroussin 	temp = el_calloc(len, sizeof(*temp));
12275d35a37SDavid E. O'Brien 	if (temp == NULL)
12375d35a37SDavid E. O'Brien 		return NULL;
12449350233SEd Schouten 	(void)snprintf(temp, len, "%s/%s", pass->pw_dir, txt);
12575d35a37SDavid E. O'Brien 
12649350233SEd Schouten 	return temp;
12775d35a37SDavid E. O'Brien }
12875d35a37SDavid E. O'Brien 
12931506252SBaptiste Daroussin static int
needs_escaping(wchar_t c)1306da940f2SBaptiste Daroussin needs_escaping(wchar_t c)
13131506252SBaptiste Daroussin {
13231506252SBaptiste Daroussin 	switch (c) {
13331506252SBaptiste Daroussin 	case '\'':
13431506252SBaptiste Daroussin 	case '"':
13531506252SBaptiste Daroussin 	case '(':
13631506252SBaptiste Daroussin 	case ')':
13731506252SBaptiste Daroussin 	case '\\':
13831506252SBaptiste Daroussin 	case '<':
13931506252SBaptiste Daroussin 	case '>':
14031506252SBaptiste Daroussin 	case '$':
14131506252SBaptiste Daroussin 	case '#':
14231506252SBaptiste Daroussin 	case ' ':
14331506252SBaptiste Daroussin 	case '\n':
14431506252SBaptiste Daroussin 	case '\t':
14531506252SBaptiste Daroussin 	case '?':
14631506252SBaptiste Daroussin 	case ';':
14731506252SBaptiste Daroussin 	case '`':
14831506252SBaptiste Daroussin 	case '@':
14931506252SBaptiste Daroussin 	case '=':
15031506252SBaptiste Daroussin 	case '|':
15131506252SBaptiste Daroussin 	case '{':
15231506252SBaptiste Daroussin 	case '}':
15331506252SBaptiste Daroussin 	case '&':
15431506252SBaptiste Daroussin 	case '*':
15531506252SBaptiste Daroussin 	case '[':
15631506252SBaptiste Daroussin 		return 1;
15731506252SBaptiste Daroussin 	default:
15831506252SBaptiste Daroussin 		return 0;
15931506252SBaptiste Daroussin 	}
16031506252SBaptiste Daroussin }
16131506252SBaptiste Daroussin 
16231506252SBaptiste Daroussin static int
needs_dquote_escaping(char c)16331506252SBaptiste Daroussin needs_dquote_escaping(char c)
16431506252SBaptiste Daroussin {
16531506252SBaptiste Daroussin 	switch (c) {
16631506252SBaptiste Daroussin 	case '"':
16731506252SBaptiste Daroussin 	case '\\':
16831506252SBaptiste Daroussin 	case '`':
16931506252SBaptiste Daroussin 	case '$':
17031506252SBaptiste Daroussin 		return 1;
17131506252SBaptiste Daroussin 	default:
17231506252SBaptiste Daroussin 		return 0;
17331506252SBaptiste Daroussin 	}
17431506252SBaptiste Daroussin }
17531506252SBaptiste Daroussin 
17631506252SBaptiste Daroussin 
17731506252SBaptiste Daroussin static wchar_t *
unescape_string(const wchar_t * string,size_t length)17831506252SBaptiste Daroussin unescape_string(const wchar_t *string, size_t length)
17931506252SBaptiste Daroussin {
18031506252SBaptiste Daroussin 	size_t i;
18131506252SBaptiste Daroussin 	size_t j = 0;
18231506252SBaptiste Daroussin 	wchar_t *unescaped = el_calloc(length + 1, sizeof(*string));
18331506252SBaptiste Daroussin 	if (unescaped == NULL)
18431506252SBaptiste Daroussin 		return NULL;
18531506252SBaptiste Daroussin 	for (i = 0; i < length ; i++) {
18631506252SBaptiste Daroussin 		if (string[i] == '\\')
18731506252SBaptiste Daroussin 			continue;
18831506252SBaptiste Daroussin 		unescaped[j++] = string[i];
18931506252SBaptiste Daroussin 	}
19031506252SBaptiste Daroussin 	unescaped[j] = 0;
19131506252SBaptiste Daroussin 	return unescaped;
19231506252SBaptiste Daroussin }
19331506252SBaptiste Daroussin 
19431506252SBaptiste Daroussin static char *
escape_filename(EditLine * el,const char * filename,int single_match,const char * (* app_func)(const char *))19531506252SBaptiste Daroussin escape_filename(EditLine * el, const char *filename, int single_match,
19631506252SBaptiste Daroussin 		const char *(*app_func)(const char *))
19731506252SBaptiste Daroussin {
19831506252SBaptiste Daroussin 	size_t original_len = 0;
19931506252SBaptiste Daroussin 	size_t escaped_character_count = 0;
20031506252SBaptiste Daroussin 	size_t offset = 0;
20131506252SBaptiste Daroussin 	size_t newlen;
20231506252SBaptiste Daroussin 	const char *s;
20331506252SBaptiste Daroussin 	char c;
20431506252SBaptiste Daroussin 	size_t s_quoted = 0;	/* does the input contain a single quote */
20531506252SBaptiste Daroussin 	size_t d_quoted = 0;	/* does the input contain a double quote */
20631506252SBaptiste Daroussin 	char *escaped_str;
20731506252SBaptiste Daroussin 	wchar_t *temp = el->el_line.buffer;
20831506252SBaptiste Daroussin 	const char *append_char = NULL;
20931506252SBaptiste Daroussin 
21031506252SBaptiste Daroussin 	if (filename == NULL)
21131506252SBaptiste Daroussin 		return NULL;
21231506252SBaptiste Daroussin 
21331506252SBaptiste Daroussin 	while (temp != el->el_line.cursor) {
21431506252SBaptiste Daroussin 		/*
21531506252SBaptiste Daroussin 		 * If we see a single quote but have not seen a double quote
2166da940f2SBaptiste Daroussin 		 * so far set/unset s_quote, unless it is already quoted
21731506252SBaptiste Daroussin 		 */
2186da940f2SBaptiste Daroussin 		if (temp[0] == '\'' && !d_quoted &&
2196da940f2SBaptiste Daroussin 		    (temp == el->el_line.buffer || temp[-1] != '\\'))
22031506252SBaptiste Daroussin 			s_quoted = !s_quoted;
22131506252SBaptiste Daroussin 		/*
22231506252SBaptiste Daroussin 		 * vice versa to the above condition
22331506252SBaptiste Daroussin 		 */
22431506252SBaptiste Daroussin 		else if (temp[0] == '"' && !s_quoted)
22531506252SBaptiste Daroussin 			d_quoted = !d_quoted;
22631506252SBaptiste Daroussin 		temp++;
22731506252SBaptiste Daroussin 	}
22831506252SBaptiste Daroussin 
22931506252SBaptiste Daroussin 	/* Count number of special characters so that we can calculate
23031506252SBaptiste Daroussin 	 * number of extra bytes needed in the new string
23131506252SBaptiste Daroussin 	 */
23231506252SBaptiste Daroussin 	for (s = filename; *s; s++, original_len++) {
23331506252SBaptiste Daroussin 		c = *s;
23431506252SBaptiste Daroussin 		/* Inside a single quote only single quotes need escaping */
23531506252SBaptiste Daroussin 		if (s_quoted && c == '\'') {
23631506252SBaptiste Daroussin 			escaped_character_count += 3;
23731506252SBaptiste Daroussin 			continue;
23831506252SBaptiste Daroussin 		}
23931506252SBaptiste Daroussin 		/* Inside double quotes only ", \, ` and $ need escaping */
24031506252SBaptiste Daroussin 		if (d_quoted && needs_dquote_escaping(c)) {
24131506252SBaptiste Daroussin 			escaped_character_count++;
24231506252SBaptiste Daroussin 			continue;
24331506252SBaptiste Daroussin 		}
24431506252SBaptiste Daroussin 		if (!s_quoted && !d_quoted && needs_escaping(c))
24531506252SBaptiste Daroussin 			escaped_character_count++;
24631506252SBaptiste Daroussin 	}
24731506252SBaptiste Daroussin 
24831506252SBaptiste Daroussin 	newlen = original_len + escaped_character_count + 1;
24931506252SBaptiste Daroussin 	if (s_quoted || d_quoted)
25031506252SBaptiste Daroussin 		newlen++;
25131506252SBaptiste Daroussin 
25231506252SBaptiste Daroussin 	if (single_match && app_func)
25331506252SBaptiste Daroussin 		newlen++;
25431506252SBaptiste Daroussin 
25531506252SBaptiste Daroussin 	if ((escaped_str = el_malloc(newlen)) == NULL)
25631506252SBaptiste Daroussin 		return NULL;
25731506252SBaptiste Daroussin 
25831506252SBaptiste Daroussin 	for (s = filename; *s; s++) {
25931506252SBaptiste Daroussin 		c = *s;
26031506252SBaptiste Daroussin 		if (!needs_escaping(c)) {
26131506252SBaptiste Daroussin 			/* no escaping is required continue as usual */
26231506252SBaptiste Daroussin 			escaped_str[offset++] = c;
26331506252SBaptiste Daroussin 			continue;
26431506252SBaptiste Daroussin 		}
26531506252SBaptiste Daroussin 
26631506252SBaptiste Daroussin 		/* single quotes inside single quotes require special handling */
26731506252SBaptiste Daroussin 		if (c == '\'' && s_quoted) {
26831506252SBaptiste Daroussin 			escaped_str[offset++] = '\'';
26931506252SBaptiste Daroussin 			escaped_str[offset++] = '\\';
27031506252SBaptiste Daroussin 			escaped_str[offset++] = '\'';
27131506252SBaptiste Daroussin 			escaped_str[offset++] = '\'';
27231506252SBaptiste Daroussin 			continue;
27331506252SBaptiste Daroussin 		}
27431506252SBaptiste Daroussin 
27531506252SBaptiste Daroussin 		/* Otherwise no escaping needed inside single quotes */
27631506252SBaptiste Daroussin 		if (s_quoted) {
27731506252SBaptiste Daroussin 			escaped_str[offset++] = c;
27831506252SBaptiste Daroussin 			continue;
27931506252SBaptiste Daroussin 		}
28031506252SBaptiste Daroussin 
28131506252SBaptiste Daroussin 		/* No escaping needed inside a double quoted string either
28231506252SBaptiste Daroussin 		 * unless we see a '$', '\', '`', or '"' (itself)
28331506252SBaptiste Daroussin 		 */
28431506252SBaptiste Daroussin 		if (d_quoted && !needs_dquote_escaping(c)) {
28531506252SBaptiste Daroussin 			escaped_str[offset++] = c;
28631506252SBaptiste Daroussin 			continue;
28731506252SBaptiste Daroussin 		}
28831506252SBaptiste Daroussin 
28931506252SBaptiste Daroussin 		/* If we reach here that means escaping is actually needed */
29031506252SBaptiste Daroussin 		escaped_str[offset++] = '\\';
29131506252SBaptiste Daroussin 		escaped_str[offset++] = c;
29231506252SBaptiste Daroussin 	}
29331506252SBaptiste Daroussin 
29431506252SBaptiste Daroussin 	if (single_match && app_func) {
29531506252SBaptiste Daroussin 		escaped_str[offset] = 0;
29631595a7cSBaptiste Daroussin 		append_char = app_func(filename);
29731506252SBaptiste Daroussin 		/* we want to append space only if we are not inside quotes */
29831506252SBaptiste Daroussin 		if (append_char[0] == ' ') {
29931506252SBaptiste Daroussin 			if (!s_quoted && !d_quoted)
30031506252SBaptiste Daroussin 				escaped_str[offset++] = append_char[0];
30131506252SBaptiste Daroussin 		} else
30231506252SBaptiste Daroussin 			escaped_str[offset++] = append_char[0];
30331506252SBaptiste Daroussin 	}
30431506252SBaptiste Daroussin 
30531506252SBaptiste Daroussin 	/* close the quotes if single match and the match is not a directory */
30631506252SBaptiste Daroussin 	if (single_match && (append_char && append_char[0] == ' ')) {
30731506252SBaptiste Daroussin 		if (s_quoted)
30831506252SBaptiste Daroussin 			escaped_str[offset++] = '\'';
30931506252SBaptiste Daroussin 		else if (d_quoted)
31031506252SBaptiste Daroussin 			escaped_str[offset++] = '"';
31131506252SBaptiste Daroussin 	}
31231506252SBaptiste Daroussin 
31331506252SBaptiste Daroussin 	escaped_str[offset] = 0;
31431506252SBaptiste Daroussin 	return escaped_str;
31531506252SBaptiste Daroussin }
31675d35a37SDavid E. O'Brien 
31775d35a37SDavid E. O'Brien /*
31875d35a37SDavid E. O'Brien  * return first found file name starting by the ``text'' or NULL if no
31975d35a37SDavid E. O'Brien  * such file can be found
32075d35a37SDavid E. O'Brien  * value of ``state'' is ignored
32175d35a37SDavid E. O'Brien  *
3225b6fd65dSBaptiste Daroussin  * it's the caller's responsibility to free the returned string
32375d35a37SDavid E. O'Brien  */
32475d35a37SDavid E. O'Brien char *
fn_filename_completion_function(const char * text,int state)32575d35a37SDavid E. O'Brien fn_filename_completion_function(const char *text, int state)
32675d35a37SDavid E. O'Brien {
32775d35a37SDavid E. O'Brien 	static DIR *dir = NULL;
32875d35a37SDavid E. O'Brien 	static char *filename = NULL, *dirname = NULL, *dirpath = NULL;
32975d35a37SDavid E. O'Brien 	static size_t filename_len = 0;
33075d35a37SDavid E. O'Brien 	struct dirent *entry;
33175d35a37SDavid E. O'Brien 	char *temp;
3326da940f2SBaptiste Daroussin 	const char *pos;
33375d35a37SDavid E. O'Brien 	size_t len;
33475d35a37SDavid E. O'Brien 
33575d35a37SDavid E. O'Brien 	if (state == 0 || dir == NULL) {
3366da940f2SBaptiste Daroussin 		pos = strrchr(text, '/');
3376da940f2SBaptiste Daroussin 		if (pos) {
33875d35a37SDavid E. O'Brien 			char *nptr;
3396da940f2SBaptiste Daroussin 			pos++;
3406da940f2SBaptiste Daroussin 			nptr = el_realloc(filename, (strlen(pos) + 1) *
34149350233SEd Schouten 			    sizeof(*nptr));
34275d35a37SDavid E. O'Brien 			if (nptr == NULL) {
34349350233SEd Schouten 				el_free(filename);
344703cb613SDavid E. O'Brien 				filename = NULL;
34575d35a37SDavid E. O'Brien 				return NULL;
34675d35a37SDavid E. O'Brien 			}
34775d35a37SDavid E. O'Brien 			filename = nptr;
3486da940f2SBaptiste Daroussin 			(void)strcpy(filename, pos);
3496da940f2SBaptiste Daroussin 			len = (size_t)(pos - text);	/* including last slash */
350703cb613SDavid E. O'Brien 
35149350233SEd Schouten 			nptr = el_realloc(dirname, (len + 1) *
35249350233SEd Schouten 			    sizeof(*nptr));
35375d35a37SDavid E. O'Brien 			if (nptr == NULL) {
35449350233SEd Schouten 				el_free(dirname);
355703cb613SDavid E. O'Brien 				dirname = NULL;
35675d35a37SDavid E. O'Brien 				return NULL;
35775d35a37SDavid E. O'Brien 			}
35875d35a37SDavid E. O'Brien 			dirname = nptr;
359881fcf9cSBaptiste Daroussin 			(void)strlcpy(dirname, text, len + 1);
36075d35a37SDavid E. O'Brien 		} else {
36149350233SEd Schouten 			el_free(filename);
36275d35a37SDavid E. O'Brien 			if (*text == 0)
36375d35a37SDavid E. O'Brien 				filename = NULL;
36475d35a37SDavid E. O'Brien 			else {
36575d35a37SDavid E. O'Brien 				filename = strdup(text);
36675d35a37SDavid E. O'Brien 				if (filename == NULL)
36775d35a37SDavid E. O'Brien 					return NULL;
36875d35a37SDavid E. O'Brien 			}
36949350233SEd Schouten 			el_free(dirname);
37075d35a37SDavid E. O'Brien 			dirname = NULL;
37175d35a37SDavid E. O'Brien 		}
37275d35a37SDavid E. O'Brien 
37375d35a37SDavid E. O'Brien 		if (dir != NULL) {
37475d35a37SDavid E. O'Brien 			(void)closedir(dir);
37575d35a37SDavid E. O'Brien 			dir = NULL;
37675d35a37SDavid E. O'Brien 		}
37775d35a37SDavid E. O'Brien 
37875d35a37SDavid E. O'Brien 		/* support for ``~user'' syntax */
379703cb613SDavid E. O'Brien 
38049350233SEd Schouten 		el_free(dirpath);
381703cb613SDavid E. O'Brien 		dirpath = NULL;
382703cb613SDavid E. O'Brien 		if (dirname == NULL) {
383703cb613SDavid E. O'Brien 			if ((dirname = strdup("")) == NULL)
38475d35a37SDavid E. O'Brien 				return NULL;
385703cb613SDavid E. O'Brien 			dirpath = strdup("./");
386703cb613SDavid E. O'Brien 		} else if (*dirname == '~')
38775d35a37SDavid E. O'Brien 			dirpath = fn_tilde_expand(dirname);
38875d35a37SDavid E. O'Brien 		else
38975d35a37SDavid E. O'Brien 			dirpath = strdup(dirname);
39075d35a37SDavid E. O'Brien 
39175d35a37SDavid E. O'Brien 		if (dirpath == NULL)
39275d35a37SDavid E. O'Brien 			return NULL;
39375d35a37SDavid E. O'Brien 
39475d35a37SDavid E. O'Brien 		dir = opendir(dirpath);
39575d35a37SDavid E. O'Brien 		if (!dir)
39649350233SEd Schouten 			return NULL;	/* cannot open the directory */
39775d35a37SDavid E. O'Brien 
39875d35a37SDavid E. O'Brien 		/* will be used in cycle */
39975d35a37SDavid E. O'Brien 		filename_len = filename ? strlen(filename) : 0;
40075d35a37SDavid E. O'Brien 	}
40175d35a37SDavid E. O'Brien 
40275d35a37SDavid E. O'Brien 	/* find the match */
40375d35a37SDavid E. O'Brien 	while ((entry = readdir(dir)) != NULL) {
40475d35a37SDavid E. O'Brien 		/* skip . and .. */
40575d35a37SDavid E. O'Brien 		if (entry->d_name[0] == '.' && (!entry->d_name[1]
40675d35a37SDavid E. O'Brien 		    || (entry->d_name[1] == '.' && !entry->d_name[2])))
40775d35a37SDavid E. O'Brien 			continue;
40875d35a37SDavid E. O'Brien 		if (filename_len == 0)
40975d35a37SDavid E. O'Brien 			break;
41075d35a37SDavid E. O'Brien 		/* otherwise, get first entry where first */
41175d35a37SDavid E. O'Brien 		/* filename_len characters are equal	  */
41275d35a37SDavid E. O'Brien 		if (entry->d_name[0] == filename[0]
413703cb613SDavid E. O'Brien #if HAVE_STRUCT_DIRENT_D_NAMLEN
41475d35a37SDavid E. O'Brien 		    && entry->d_namlen >= filename_len
415703cb613SDavid E. O'Brien #else
416703cb613SDavid E. O'Brien 		    && strlen(entry->d_name) >= filename_len
41775d35a37SDavid E. O'Brien #endif
41875d35a37SDavid E. O'Brien 		    && strncmp(entry->d_name, filename,
41975d35a37SDavid E. O'Brien 			filename_len) == 0)
42075d35a37SDavid E. O'Brien 			break;
42175d35a37SDavid E. O'Brien 	}
42275d35a37SDavid E. O'Brien 
42375d35a37SDavid E. O'Brien 	if (entry) {		/* match found */
42475d35a37SDavid E. O'Brien 
425703cb613SDavid E. O'Brien #if HAVE_STRUCT_DIRENT_D_NAMLEN
42675d35a37SDavid E. O'Brien 		len = entry->d_namlen;
427703cb613SDavid E. O'Brien #else
428703cb613SDavid E. O'Brien 		len = strlen(entry->d_name);
42975d35a37SDavid E. O'Brien #endif
43075d35a37SDavid E. O'Brien 
43149350233SEd Schouten 		len = strlen(dirname) + len + 1;
43231506252SBaptiste Daroussin 		temp = el_calloc(len, sizeof(*temp));
43375d35a37SDavid E. O'Brien 		if (temp == NULL)
43475d35a37SDavid E. O'Brien 			return NULL;
43549350233SEd Schouten 		(void)snprintf(temp, len, "%s%s", dirname, entry->d_name);
43675d35a37SDavid E. O'Brien 	} else {
43775d35a37SDavid E. O'Brien 		(void)closedir(dir);
43875d35a37SDavid E. O'Brien 		dir = NULL;
43975d35a37SDavid E. O'Brien 		temp = NULL;
44075d35a37SDavid E. O'Brien 	}
44175d35a37SDavid E. O'Brien 
44249350233SEd Schouten 	return temp;
44375d35a37SDavid E. O'Brien }
44475d35a37SDavid E. O'Brien 
44575d35a37SDavid E. O'Brien 
44675d35a37SDavid E. O'Brien static const char *
append_char_function(const char * name)44775d35a37SDavid E. O'Brien append_char_function(const char *name)
44875d35a37SDavid E. O'Brien {
44975d35a37SDavid E. O'Brien 	struct stat stbuf;
45075d35a37SDavid E. O'Brien 	char *expname = *name == '~' ? fn_tilde_expand(name) : NULL;
45175d35a37SDavid E. O'Brien 	const char *rs = " ";
45275d35a37SDavid E. O'Brien 
45375d35a37SDavid E. O'Brien 	if (stat(expname ? expname : name, &stbuf) == -1)
45475d35a37SDavid E. O'Brien 		goto out;
45575d35a37SDavid E. O'Brien 	if (S_ISDIR(stbuf.st_mode))
45675d35a37SDavid E. O'Brien 		rs = "/";
45775d35a37SDavid E. O'Brien out:
45875d35a37SDavid E. O'Brien 	if (expname)
45949350233SEd Schouten 		el_free(expname);
46075d35a37SDavid E. O'Brien 	return rs;
46175d35a37SDavid E. O'Brien }
462b3884193SBaptiste Daroussin 
46375d35a37SDavid E. O'Brien /*
46475d35a37SDavid E. O'Brien  * returns list of completions for text given
46575d35a37SDavid E. O'Brien  * non-static for readline.
46675d35a37SDavid E. O'Brien  */
46775d35a37SDavid E. O'Brien char **
completion_matches(const char * text,char * (* genfunc)(const char *,int))46875d35a37SDavid E. O'Brien completion_matches(const char *text, char *(*genfunc)(const char *, int))
46975d35a37SDavid E. O'Brien {
47075d35a37SDavid E. O'Brien 	char **match_list = NULL, *retstr, *prevstr;
47175d35a37SDavid E. O'Brien 	size_t match_list_len, max_equal, which, i;
47275d35a37SDavid E. O'Brien 	size_t matches;
47375d35a37SDavid E. O'Brien 
47475d35a37SDavid E. O'Brien 	matches = 0;
47575d35a37SDavid E. O'Brien 	match_list_len = 1;
47675d35a37SDavid E. O'Brien 	while ((retstr = (*genfunc) (text, (int)matches)) != NULL) {
47775d35a37SDavid E. O'Brien 		/* allow for list terminator here */
47875d35a37SDavid E. O'Brien 		if (matches + 3 >= match_list_len) {
47975d35a37SDavid E. O'Brien 			char **nmatch_list;
48075d35a37SDavid E. O'Brien 			while (matches + 3 >= match_list_len)
48175d35a37SDavid E. O'Brien 				match_list_len <<= 1;
48249350233SEd Schouten 			nmatch_list = el_realloc(match_list,
48349350233SEd Schouten 			    match_list_len * sizeof(*nmatch_list));
48475d35a37SDavid E. O'Brien 			if (nmatch_list == NULL) {
48549350233SEd Schouten 				el_free(match_list);
48675d35a37SDavid E. O'Brien 				return NULL;
48775d35a37SDavid E. O'Brien 			}
48875d35a37SDavid E. O'Brien 			match_list = nmatch_list;
48975d35a37SDavid E. O'Brien 
49075d35a37SDavid E. O'Brien 		}
49175d35a37SDavid E. O'Brien 		match_list[++matches] = retstr;
49275d35a37SDavid E. O'Brien 	}
49375d35a37SDavid E. O'Brien 
49475d35a37SDavid E. O'Brien 	if (!match_list)
49575d35a37SDavid E. O'Brien 		return NULL;	/* nothing found */
49675d35a37SDavid E. O'Brien 
49775d35a37SDavid E. O'Brien 	/* find least denominator and insert it to match_list[0] */
49875d35a37SDavid E. O'Brien 	which = 2;
49975d35a37SDavid E. O'Brien 	prevstr = match_list[1];
50075d35a37SDavid E. O'Brien 	max_equal = strlen(prevstr);
50175d35a37SDavid E. O'Brien 	for (; which <= matches; which++) {
50275d35a37SDavid E. O'Brien 		for (i = 0; i < max_equal &&
50375d35a37SDavid E. O'Brien 		    prevstr[i] == match_list[which][i]; i++)
50475d35a37SDavid E. O'Brien 			continue;
50575d35a37SDavid E. O'Brien 		max_equal = i;
50675d35a37SDavid E. O'Brien 	}
50775d35a37SDavid E. O'Brien 
50831506252SBaptiste Daroussin 	retstr = el_calloc(max_equal + 1, sizeof(*retstr));
50975d35a37SDavid E. O'Brien 	if (retstr == NULL) {
51049350233SEd Schouten 		el_free(match_list);
51175d35a37SDavid E. O'Brien 		return NULL;
51275d35a37SDavid E. O'Brien 	}
513881fcf9cSBaptiste Daroussin 	(void)strlcpy(retstr, match_list[1], max_equal + 1);
51475d35a37SDavid E. O'Brien 	match_list[0] = retstr;
51575d35a37SDavid E. O'Brien 
51675d35a37SDavid E. O'Brien 	/* add NULL as last pointer to the array */
51749350233SEd Schouten 	match_list[matches + 1] = NULL;
51875d35a37SDavid E. O'Brien 
51949350233SEd Schouten 	return match_list;
52075d35a37SDavid E. O'Brien }
52175d35a37SDavid E. O'Brien 
52275d35a37SDavid E. O'Brien /*
52375d35a37SDavid E. O'Brien  * Sort function for qsort(). Just wrapper around strcasecmp().
52475d35a37SDavid E. O'Brien  */
52575d35a37SDavid E. O'Brien static int
_fn_qsort_string_compare(const void * i1,const void * i2)52675d35a37SDavid E. O'Brien _fn_qsort_string_compare(const void *i1, const void *i2)
52775d35a37SDavid E. O'Brien {
52875d35a37SDavid E. O'Brien 	const char *s1 = ((const char * const *)i1)[0];
52975d35a37SDavid E. O'Brien 	const char *s2 = ((const char * const *)i2)[0];
53075d35a37SDavid E. O'Brien 
53175d35a37SDavid E. O'Brien 	return strcasecmp(s1, s2);
53275d35a37SDavid E. O'Brien }
53375d35a37SDavid E. O'Brien 
53475d35a37SDavid E. O'Brien /*
53575d35a37SDavid E. O'Brien  * Display list of strings in columnar format on readline's output stream.
53649350233SEd Schouten  * 'matches' is list of strings, 'num' is number of strings in 'matches',
53749350233SEd Schouten  * 'width' is maximum length of string in 'matches'.
53849350233SEd Schouten  *
53949350233SEd Schouten  * matches[0] is not one of the match strings, but it is counted in
54049350233SEd Schouten  * num, so the strings are matches[1] *through* matches[num-1].
54175d35a37SDavid E. O'Brien  */
54275d35a37SDavid E. O'Brien void
fn_display_match_list(EditLine * el,char ** matches,size_t num,size_t width,const char * (* app_func)(const char *))5438c36b043SPedro F. Giffuni fn_display_match_list(EditLine * el, char **matches, size_t num, size_t width,
5448c36b043SPedro F. Giffuni     const char *(*app_func) (const char *))
54575d35a37SDavid E. O'Brien {
54649350233SEd Schouten 	size_t line, lines, col, cols, thisguy;
54749350233SEd Schouten 	int screenwidth = el->el_terminal.t_size.h;
5488c36b043SPedro F. Giffuni 	if (app_func == NULL)
5498c36b043SPedro F. Giffuni 		app_func = append_char_function;
55049350233SEd Schouten 
55149350233SEd Schouten 	/* Ignore matches[0]. Avoid 1-based array logic below. */
55249350233SEd Schouten 	matches++;
55349350233SEd Schouten 	num--;
55475d35a37SDavid E. O'Brien 
55575d35a37SDavid E. O'Brien 	/*
55649350233SEd Schouten 	 * Find out how many entries can be put on one line; count
55749350233SEd Schouten 	 * with one space between strings the same way it's printed.
55875d35a37SDavid E. O'Brien 	 */
55931506252SBaptiste Daroussin 	cols = (size_t)screenwidth / (width + 2);
56049350233SEd Schouten 	if (cols == 0)
56149350233SEd Schouten 		cols = 1;
56275d35a37SDavid E. O'Brien 
56349350233SEd Schouten 	/* how many lines of output, rounded up */
56449350233SEd Schouten 	lines = (num + cols - 1) / cols;
56575d35a37SDavid E. O'Brien 
56649350233SEd Schouten 	/* Sort the items. */
56749350233SEd Schouten 	qsort(matches, num, sizeof(char *), _fn_qsort_string_compare);
56875d35a37SDavid E. O'Brien 
56949350233SEd Schouten 	/*
57049350233SEd Schouten 	 * On the ith line print elements i, i+lines, i+lines*2, etc.
57149350233SEd Schouten 	 */
57249350233SEd Schouten 	for (line = 0; line < lines; line++) {
57349350233SEd Schouten 		for (col = 0; col < cols; col++) {
57449350233SEd Schouten 			thisguy = line + col * lines;
57549350233SEd Schouten 			if (thisguy >= num)
57649350233SEd Schouten 				break;
5778c36b043SPedro F. Giffuni 			(void)fprintf(el->el_outfile, "%s%s%s",
5788c36b043SPedro F. Giffuni 			    col == 0 ? "" : " ", matches[thisguy],
57931506252SBaptiste Daroussin 				(*app_func)(matches[thisguy]));
5808c36b043SPedro F. Giffuni 			(void)fprintf(el->el_outfile, "%-*s",
5818c36b043SPedro F. Giffuni 				(int) (width - strlen(matches[thisguy])), "");
582703cb613SDavid E. O'Brien 		}
58375d35a37SDavid E. O'Brien 		(void)fprintf(el->el_outfile, "\n");
58475d35a37SDavid E. O'Brien 	}
58575d35a37SDavid E. O'Brien }
58675d35a37SDavid E. O'Brien 
58731506252SBaptiste Daroussin static wchar_t *
find_word_to_complete(const wchar_t * cursor,const wchar_t * buffer,const wchar_t * word_break,const wchar_t * special_prefixes,size_t * length,int do_unescape)58831506252SBaptiste Daroussin find_word_to_complete(const wchar_t * cursor, const wchar_t * buffer,
589881fcf9cSBaptiste Daroussin     const wchar_t * word_break, const wchar_t * special_prefixes, size_t * length,
590881fcf9cSBaptiste Daroussin 	int do_unescape)
59131506252SBaptiste Daroussin {
59231506252SBaptiste Daroussin 	/* We now look backwards for the start of a filename/variable word */
59331506252SBaptiste Daroussin 	const wchar_t *ctemp = cursor;
594881fcf9cSBaptiste Daroussin 	wchar_t *temp;
59531506252SBaptiste Daroussin 	size_t len;
59631506252SBaptiste Daroussin 
59731506252SBaptiste Daroussin 	/* if the cursor is placed at a slash or a quote, we need to find the
59831506252SBaptiste Daroussin 	 * word before it
59931506252SBaptiste Daroussin 	 */
60031506252SBaptiste Daroussin 	if (ctemp > buffer) {
60131506252SBaptiste Daroussin 		switch (ctemp[-1]) {
60231506252SBaptiste Daroussin 		case '\\':
60331506252SBaptiste Daroussin 		case '\'':
60431506252SBaptiste Daroussin 		case '"':
60531506252SBaptiste Daroussin 			ctemp--;
60631506252SBaptiste Daroussin 			break;
60731506252SBaptiste Daroussin 		default:
60831506252SBaptiste Daroussin 			break;
60931506252SBaptiste Daroussin 		}
61031506252SBaptiste Daroussin 	}
61131506252SBaptiste Daroussin 
61231506252SBaptiste Daroussin 	for (;;) {
61331506252SBaptiste Daroussin 		if (ctemp <= buffer)
61431506252SBaptiste Daroussin 			break;
6156da940f2SBaptiste Daroussin 		if (ctemp - buffer >= 2 && ctemp[-2] == '\\' &&
6166da940f2SBaptiste Daroussin 		    needs_escaping(ctemp[-1])) {
61731506252SBaptiste Daroussin 			ctemp -= 2;
61831506252SBaptiste Daroussin 			continue;
619881fcf9cSBaptiste Daroussin 		}
6206da940f2SBaptiste Daroussin 		if (wcschr(word_break, ctemp[-1]))
62131506252SBaptiste Daroussin 			break;
62231506252SBaptiste Daroussin 		if (special_prefixes && wcschr(special_prefixes, ctemp[-1]))
62331506252SBaptiste Daroussin 			break;
62431506252SBaptiste Daroussin 		ctemp--;
62531506252SBaptiste Daroussin 	}
62631506252SBaptiste Daroussin 
62731506252SBaptiste Daroussin 	len = (size_t) (cursor - ctemp);
62831506252SBaptiste Daroussin 	if (len == 1 && (ctemp[0] == '\'' || ctemp[0] == '"')) {
62931506252SBaptiste Daroussin 		len = 0;
63031506252SBaptiste Daroussin 		ctemp++;
63131506252SBaptiste Daroussin 	}
63231506252SBaptiste Daroussin 	*length = len;
633881fcf9cSBaptiste Daroussin 	if (do_unescape) {
63431506252SBaptiste Daroussin 		wchar_t *unescaped_word = unescape_string(ctemp, len);
63531506252SBaptiste Daroussin 		if (unescaped_word == NULL)
63631506252SBaptiste Daroussin 			return NULL;
63731506252SBaptiste Daroussin 		return unescaped_word;
63831506252SBaptiste Daroussin 	}
639881fcf9cSBaptiste Daroussin 	temp = el_malloc((len + 1) * sizeof(*temp));
6405a36c826SBaptiste Daroussin 	if (temp == NULL)
6415a36c826SBaptiste Daroussin 		return NULL;
642881fcf9cSBaptiste Daroussin 	(void) wcsncpy(temp, ctemp, len);
643881fcf9cSBaptiste Daroussin 	temp[len] = '\0';
644881fcf9cSBaptiste Daroussin 	return temp;
645881fcf9cSBaptiste Daroussin }
64631506252SBaptiste Daroussin 
64775d35a37SDavid E. O'Brien /*
64875d35a37SDavid E. O'Brien  * Complete the word at or before point,
64975d35a37SDavid E. O'Brien  * 'what_to_do' says what to do with the completion.
65075d35a37SDavid E. O'Brien  * \t   means do standard completion.
65175d35a37SDavid E. O'Brien  * `?' means list the possible completions.
65275d35a37SDavid E. O'Brien  * `*' means insert all of the possible completions.
65375d35a37SDavid E. O'Brien  * `!' means to do standard completion, and list all possible completions if
65475d35a37SDavid E. O'Brien  * there is more than one.
65575d35a37SDavid E. O'Brien  *
65675d35a37SDavid E. O'Brien  * Note: '*' support is not implemented
65775d35a37SDavid E. O'Brien  *       '!' could never be invoked
65875d35a37SDavid E. O'Brien  */
65975d35a37SDavid E. O'Brien int
fn_complete2(EditLine * el,char * (* complete_func)(const char *,int),char ** (* attempted_completion_function)(const char *,int,int),const wchar_t * word_break,const wchar_t * special_prefixes,const char * (* app_func)(const char *),size_t query_items,int * completion_type,int * over,int * point,int * end,unsigned int flags)66031595a7cSBaptiste Daroussin fn_complete2(EditLine *el,
66131595a7cSBaptiste Daroussin     char *(*complete_func)(const char *, int),
66275d35a37SDavid E. O'Brien     char **(*attempted_completion_function)(const char *, int, int),
6638c36b043SPedro F. Giffuni     const wchar_t *word_break, const wchar_t *special_prefixes,
664703cb613SDavid E. O'Brien     const char *(*app_func)(const char *), size_t query_items,
66531595a7cSBaptiste Daroussin     int *completion_type, int *over, int *point, int *end,
66631595a7cSBaptiste Daroussin     unsigned int flags)
66775d35a37SDavid E. O'Brien {
6688c36b043SPedro F. Giffuni 	const LineInfoW *li;
6698c36b043SPedro F. Giffuni 	wchar_t *temp;
670703cb613SDavid E. O'Brien 	char **matches;
67131506252SBaptiste Daroussin 	char *completion;
67275d35a37SDavid E. O'Brien 	size_t len;
67375d35a37SDavid E. O'Brien 	int what_to_do = '\t';
6748dfe73baSDavid E. O'Brien 	int retval = CC_NORM;
67531595a7cSBaptiste Daroussin 	int do_unescape = flags & FN_QUOTE_MATCH;
67675d35a37SDavid E. O'Brien 
67775d35a37SDavid E. O'Brien 	if (el->el_state.lastcmd == el->el_state.thiscmd)
67875d35a37SDavid E. O'Brien 		what_to_do = '?';
67975d35a37SDavid E. O'Brien 
68075d35a37SDavid E. O'Brien 	/* readline's rl_complete() has to be told what we did... */
68175d35a37SDavid E. O'Brien 	if (completion_type != NULL)
68275d35a37SDavid E. O'Brien 		*completion_type = what_to_do;
68375d35a37SDavid E. O'Brien 
68431595a7cSBaptiste Daroussin 	if (!complete_func)
68531595a7cSBaptiste Daroussin 		complete_func = fn_filename_completion_function;
68675d35a37SDavid E. O'Brien 	if (!app_func)
68775d35a37SDavid E. O'Brien 		app_func = append_char_function;
68875d35a37SDavid E. O'Brien 
6898c36b043SPedro F. Giffuni 	li = el_wline(el);
69031506252SBaptiste Daroussin 	temp = find_word_to_complete(li->cursor,
691881fcf9cSBaptiste Daroussin 	    li->buffer, word_break, special_prefixes, &len, do_unescape);
69231506252SBaptiste Daroussin 	if (temp == NULL)
69331506252SBaptiste Daroussin 		goto out;
69475d35a37SDavid E. O'Brien 
69575d35a37SDavid E. O'Brien 	/* these can be used by function called in completion_matches() */
69675d35a37SDavid E. O'Brien 	/* or (*attempted_completion_function)() */
6978c36b043SPedro F. Giffuni 	if (point != NULL)
698703cb613SDavid E. O'Brien 		*point = (int)(li->cursor - li->buffer);
69975d35a37SDavid E. O'Brien 	if (end != NULL)
700703cb613SDavid E. O'Brien 		*end = (int)(li->lastchar - li->buffer);
70175d35a37SDavid E. O'Brien 
70275d35a37SDavid E. O'Brien 	if (attempted_completion_function) {
703703cb613SDavid E. O'Brien 		int cur_off = (int)(li->cursor - li->buffer);
70449350233SEd Schouten 		matches = (*attempted_completion_function)(
70549350233SEd Schouten 		    ct_encode_string(temp, &el->el_scratch),
70649350233SEd Schouten 		    cur_off - (int)len, cur_off);
70775d35a37SDavid E. O'Brien 	} else
7088c36b043SPedro F. Giffuni 		matches = NULL;
70975d35a37SDavid E. O'Brien 	if (!attempted_completion_function ||
71004f67c69SDavid E. O'Brien 	    (over != NULL && !*over && !matches))
71149350233SEd Schouten 		matches = completion_matches(
71231595a7cSBaptiste Daroussin 		    ct_encode_string(temp, &el->el_scratch), complete_func);
71375d35a37SDavid E. O'Brien 
71475d35a37SDavid E. O'Brien 	if (over != NULL)
71575d35a37SDavid E. O'Brien 		*over = 0;
71675d35a37SDavid E. O'Brien 
717881fcf9cSBaptiste Daroussin 	if (matches == NULL) {
718881fcf9cSBaptiste Daroussin 		goto out;
719881fcf9cSBaptiste Daroussin 	}
7208dfe73baSDavid E. O'Brien 	int i;
721703cb613SDavid E. O'Brien 	size_t matches_num, maxlen, match_len, match_display=1;
72231506252SBaptiste Daroussin 	int single_match = matches[2] == NULL &&
72331506252SBaptiste Daroussin 		(matches[1] == NULL || strcmp(matches[0], matches[1]) == 0);
72475d35a37SDavid E. O'Brien 
7258dfe73baSDavid E. O'Brien 	retval = CC_REFRESH;
72631506252SBaptiste Daroussin 
72775d35a37SDavid E. O'Brien 	if (matches[0][0] != '\0') {
72875d35a37SDavid E. O'Brien 		el_deletestr(el, (int)len);
72931595a7cSBaptiste Daroussin 		if (flags & FN_QUOTE_MATCH)
73031506252SBaptiste Daroussin 			completion = escape_filename(el, matches[0],
73131506252SBaptiste Daroussin 			    single_match, app_func);
73231506252SBaptiste Daroussin 		else
73331506252SBaptiste Daroussin 			completion = strdup(matches[0]);
73431506252SBaptiste Daroussin 		if (completion == NULL)
73530f4a9aeSBaptiste Daroussin 			goto out2;
736881fcf9cSBaptiste Daroussin 
737881fcf9cSBaptiste Daroussin 		/*
738881fcf9cSBaptiste Daroussin 		 * Replace the completed string with the common part of
739881fcf9cSBaptiste Daroussin 		 * all possible matches if there is a possible completion.
740881fcf9cSBaptiste Daroussin 		 */
741881fcf9cSBaptiste Daroussin 		el_winsertstr(el,
742881fcf9cSBaptiste Daroussin 		    ct_decode_string(completion, &el->el_scratch));
743881fcf9cSBaptiste Daroussin 
74431595a7cSBaptiste Daroussin 		if (single_match && attempted_completion_function &&
74531595a7cSBaptiste Daroussin 		    !(flags & FN_QUOTE_MATCH))
74631595a7cSBaptiste Daroussin 		{
747881fcf9cSBaptiste Daroussin 			/*
748881fcf9cSBaptiste Daroussin 			 * We found an exact match. Add a space after
749881fcf9cSBaptiste Daroussin 			 * it, unless we do filename completion and the
75031506252SBaptiste Daroussin 			 * object is a directory. Also do necessary
75131506252SBaptiste Daroussin 			 * escape quoting
75231506252SBaptiste Daroussin 			 */
753881fcf9cSBaptiste Daroussin 			el_winsertstr(el, ct_decode_string(
754881fcf9cSBaptiste Daroussin 			    (*app_func)(completion), &el->el_scratch));
75531506252SBaptiste Daroussin 		}
75631506252SBaptiste Daroussin 		free(completion);
75775d35a37SDavid E. O'Brien 	}
75875d35a37SDavid E. O'Brien 
75975d35a37SDavid E. O'Brien 
76031506252SBaptiste Daroussin 	if (!single_match && (what_to_do == '!' || what_to_do == '?')) {
76175d35a37SDavid E. O'Brien 		/*
76275d35a37SDavid E. O'Brien 		 * More than one match and requested to list possible
76375d35a37SDavid E. O'Brien 		 * matches.
76475d35a37SDavid E. O'Brien 		 */
76575d35a37SDavid E. O'Brien 
76675d35a37SDavid E. O'Brien 		for(i = 1, maxlen = 0; matches[i]; i++) {
76775d35a37SDavid E. O'Brien 			match_len = strlen(matches[i]);
76875d35a37SDavid E. O'Brien 			if (match_len > maxlen)
76975d35a37SDavid E. O'Brien 				maxlen = match_len;
77075d35a37SDavid E. O'Brien 		}
77149350233SEd Schouten 		/* matches[1] through matches[i-1] are available */
77249350233SEd Schouten 		matches_num = (size_t)(i - 1);
77375d35a37SDavid E. O'Brien 
77475d35a37SDavid E. O'Brien 		/* newline to get on next line from command line */
77575d35a37SDavid E. O'Brien 		(void)fprintf(el->el_outfile, "\n");
77675d35a37SDavid E. O'Brien 
77775d35a37SDavid E. O'Brien 		/*
77875d35a37SDavid E. O'Brien 		 * If there are too many items, ask user for display
77975d35a37SDavid E. O'Brien 		 * confirmation.
78075d35a37SDavid E. O'Brien 		 */
78175d35a37SDavid E. O'Brien 		if (matches_num > query_items) {
78275d35a37SDavid E. O'Brien 			(void)fprintf(el->el_outfile,
783703cb613SDavid E. O'Brien 			    "Display all %zu possibilities? (y or n) ",
78475d35a37SDavid E. O'Brien 			    matches_num);
78575d35a37SDavid E. O'Brien 			(void)fflush(el->el_outfile);
78675d35a37SDavid E. O'Brien 			if (getc(stdin) != 'y')
78775d35a37SDavid E. O'Brien 				match_display = 0;
78875d35a37SDavid E. O'Brien 			(void)fprintf(el->el_outfile, "\n");
78975d35a37SDavid E. O'Brien 		}
79075d35a37SDavid E. O'Brien 
79149350233SEd Schouten 		if (match_display) {
79249350233SEd Schouten 			/*
79349350233SEd Schouten 			 * Interface of this function requires the
79449350233SEd Schouten 			 * strings be matches[1..num-1] for compat.
79549350233SEd Schouten 			 * We have matches_num strings not counting
79649350233SEd Schouten 			 * the prefix in matches[0], so we need to
79749350233SEd Schouten 			 * add 1 to matches_num for the call.
79849350233SEd Schouten 			 */
79949350233SEd Schouten 			fn_display_match_list(el, matches,
8008c36b043SPedro F. Giffuni 			    matches_num+1, maxlen, app_func);
80149350233SEd Schouten 		}
80275d35a37SDavid E. O'Brien 		retval = CC_REDISPLAY;
80375d35a37SDavid E. O'Brien 	} else if (matches[0][0]) {
80475d35a37SDavid E. O'Brien 		/*
80575d35a37SDavid E. O'Brien 		 * There was some common match, but the name was
80675d35a37SDavid E. O'Brien 		 * not complete enough. Next tab will print possible
80775d35a37SDavid E. O'Brien 		 * completions.
80875d35a37SDavid E. O'Brien 		 */
80975d35a37SDavid E. O'Brien 		el_beep(el);
81075d35a37SDavid E. O'Brien 	} else {
81175d35a37SDavid E. O'Brien 		/* lcd is not a valid object - further specification */
81275d35a37SDavid E. O'Brien 		/* is needed */
81375d35a37SDavid E. O'Brien 		el_beep(el);
81475d35a37SDavid E. O'Brien 		retval = CC_NORM;
81575d35a37SDavid E. O'Brien 	}
81675d35a37SDavid E. O'Brien 
81775d35a37SDavid E. O'Brien 	/* free elements of array and the array itself */
81830f4a9aeSBaptiste Daroussin out2:
81975d35a37SDavid E. O'Brien 	for (i = 0; matches[i]; i++)
82049350233SEd Schouten 		el_free(matches[i]);
82149350233SEd Schouten 	el_free(matches);
8228dfe73baSDavid E. O'Brien 	matches = NULL;
82331506252SBaptiste Daroussin 
82431506252SBaptiste Daroussin out:
82549350233SEd Schouten 	el_free(temp);
8268dfe73baSDavid E. O'Brien 	return retval;
82775d35a37SDavid E. O'Brien }
82875d35a37SDavid E. O'Brien 
82931595a7cSBaptiste Daroussin int
fn_complete(EditLine * el,char * (* complete_func)(const char *,int),char ** (* attempted_completion_function)(const char *,int,int),const wchar_t * word_break,const wchar_t * special_prefixes,const char * (* app_func)(const char *),size_t query_items,int * completion_type,int * over,int * point,int * end)83031595a7cSBaptiste Daroussin fn_complete(EditLine *el,
83131595a7cSBaptiste Daroussin     char *(*complete_func)(const char *, int),
83231595a7cSBaptiste Daroussin     char **(*attempted_completion_function)(const char *, int, int),
83331595a7cSBaptiste Daroussin     const wchar_t *word_break, const wchar_t *special_prefixes,
83431595a7cSBaptiste Daroussin     const char *(*app_func)(const char *), size_t query_items,
83531595a7cSBaptiste Daroussin     int *completion_type, int *over, int *point, int *end)
83631595a7cSBaptiste Daroussin {
83731595a7cSBaptiste Daroussin 	return fn_complete2(el, complete_func, attempted_completion_function,
83831595a7cSBaptiste Daroussin 	    word_break, special_prefixes, app_func, query_items,
83931595a7cSBaptiste Daroussin 	    completion_type, over, point, end,
84031595a7cSBaptiste Daroussin 	    attempted_completion_function ? 0 : FN_QUOTE_MATCH);
84131595a7cSBaptiste Daroussin }
84231595a7cSBaptiste Daroussin 
84375d35a37SDavid E. O'Brien /*
84475d35a37SDavid E. O'Brien  * el-compatible wrapper around rl_complete; needed for key binding
84575d35a37SDavid E. O'Brien  */
84675d35a37SDavid E. O'Brien /* ARGSUSED */
84775d35a37SDavid E. O'Brien unsigned char
_el_fn_complete(EditLine * el,int ch)84875d35a37SDavid E. O'Brien _el_fn_complete(EditLine *el, int ch __attribute__((__unused__)))
84975d35a37SDavid E. O'Brien {
85075d35a37SDavid E. O'Brien 	return (unsigned char)fn_complete(el, NULL, NULL,
85149350233SEd Schouten 	    break_chars, NULL, NULL, (size_t)100,
85275d35a37SDavid E. O'Brien 	    NULL, NULL, NULL, NULL);
85375d35a37SDavid E. O'Brien }
8545a36c826SBaptiste Daroussin 
8555a36c826SBaptiste Daroussin /*
8565a36c826SBaptiste Daroussin  * el-compatible wrapper around rl_complete; needed for key binding
8575a36c826SBaptiste Daroussin  */
8585a36c826SBaptiste Daroussin /* ARGSUSED */
8595a36c826SBaptiste Daroussin unsigned char
_el_fn_sh_complete(EditLine * el,int ch)8605a36c826SBaptiste Daroussin _el_fn_sh_complete(EditLine *el, int ch)
8615a36c826SBaptiste Daroussin {
8625a36c826SBaptiste Daroussin 	return _el_fn_complete(el, ch);
8635a36c826SBaptiste Daroussin }
864