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