"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "libedit/filecomplete.c" between
tnftp-20151004.tar.gz and tnftp-20200705.tar.gz

About: tnftp is an enhanced ftp client (prior name "lukemftp").

filecomplete.c  (tnftp-20151004):filecomplete.c  (tnftp-20200705)
/* $NetBSD: filecomplete.c,v 1.3 2008/04/29 08:13:38 martin Exp $ */ /* $NetBSD: filecomplete.c,v 1.6 2020/07/04 13:43:21 lukem Exp $ */
/* from NetBSD: filecomplete.c,v 1.5 2005/05/18 22:34:41 christos Exp * /* from NetBSD: filecomplete.c,v 1.64 2020/01/05 07:12:05 abhinav Exp *
/ /
/*- /*-
* Copyright (c) 1997 The NetBSD Foundation, Inc. * Copyright (c) 1997 The NetBSD Foundation, Inc.
* All rights reserved. * All rights reserved.
* *
* This code is derived from software contributed to The NetBSD Foundation * This code is derived from software contributed to The NetBSD Foundation
* by Jaromir Dolecek. * by Jaromir Dolecek.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
skipping to change at line 33 skipping to change at line 33
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#if 0 /* tnftp */
#if !defined(lint) && !defined(SCCSID)
__RCSID(" NetBSD: filecomplete.c,v 1.64 2020/01/05 07:12:05 abhinav Exp ");
#endif /* not lint && not SCCSID */
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <stdio.h>
#include <dirent.h> #include <dirent.h>
#include <string.h> #include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h> #include <pwd.h>
#include <ctype.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include <limits.h> #endif /* tnftp */
#include <errno.h>
#include <fcntl.h>
#ifdef HAVE_VIS_H
#include <vis.h>
#else
#include "np/vis.h"
#endif
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include "el.h" #include "el.h"
#include "fcns.h" /* for EL_NUM_FCNS */
#include "histedit.h"
#include "filecomplete.h" #include "filecomplete.h"
static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{(";
'>', '<', '=', ';', '|', '&', '{', '(', '\0' };
/********************************/ /********************************/
/* completion functions */ /* completion functions */
/* /*
* does tilde expansion of strings of type ``~user/foo'' * does tilde expansion of strings of type ``~user/foo''
* if ``user'' isn't valid user name or ``txt'' doesn't start * if ``user'' isn't valid user name or ``txt'' doesn't start
* w/ '~', returns pointer to strdup()ed copy of ``txt'' * w/ '~', returns pointer to strdup()ed copy of ``txt''
* *
* it's callers's responsibility to free() returned string * it's the caller's responsibility to free() the returned string
*/ */
char * char *
tilde_expand(char *txt) fn_tilde_expand(const char *txt)
{ {
struct passwd pwres, *pass; #if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT)
struct passwd pwres;
char pwbuf[1024];
#endif
struct passwd *pass;
char *temp; char *temp;
size_t len = 0; size_t len = 0;
char pwbuf[1024];
if (txt[0] != '~') if (txt[0] != '~')
return (strdup(txt)); return strdup(txt);
temp = strchr(txt + 1, '/'); temp = strchr(txt + 1, '/');
if (temp == NULL) { if (temp == NULL) {
temp = strdup(txt + 1); temp = strdup(txt + 1);
if (temp == NULL) if (temp == NULL)
return NULL; return NULL;
} else { } else {
len = temp - txt + 1; /* text until string after slash */ /* text until string after slash */
temp = malloc(len); len = (size_t)(temp - txt + 1);
temp = el_calloc(len, sizeof(*temp));
if (temp == NULL) if (temp == NULL)
return NULL; return NULL;
(void)strncpy(temp, txt + 1, len - 2); (void)strlcpy(temp, txt + 1, len - 1);
temp[len - 2] = '\0';
} }
if (temp[0] == 0) { if (temp[0] == 0) {
if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf), &pass) != #ifdef HAVE_GETPW_R_POSIX
0) if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf),
&pass) != 0)
pass = NULL; pass = NULL;
#elif HAVE_GETPW_R_DRAFT
pass = getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf));
#else
pass = getpwuid(getuid());
#endif
} else { } else {
#ifdef HAVE_GETPW_R_POSIX
if (getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf), &pass) != 0) if (getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf), &pass) != 0)
pass = NULL; pass = NULL;
#elif HAVE_GETPW_R_DRAFT
pass = getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf));
#else
pass = getpwnam(temp);
#endif
} }
free(temp); /* value no more needed */ el_free(temp); /* value no more needed */
if (pass == NULL) if (pass == NULL)
return (strdup(txt)); return strdup(txt);
/* update pointer txt to point at string immedially following */ /* update pointer txt to point at string immedially following */
/* first slash */ /* first slash */
txt += len; txt += len;
temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1); len = strlen(pass->pw_dir) + 1 + strlen(txt) + 1;
temp = el_calloc(len, sizeof(*temp));
if (temp == NULL) if (temp == NULL)
return NULL; return NULL;
(void)sprintf(temp, "%s/%s", pass->pw_dir, txt); (void)snprintf(temp, len, "%s/%s", pass->pw_dir, txt);
return (temp); return temp;
}
static int
needs_escaping(char c)
{
switch (c) {
case '\'':
case '"':
case '(':
case ')':
case '\\':
case '<':
case '>':
case '$':
case '#':
case ' ':
case '\n':
case '\t':
case '?':
case ';':
case '`':
case '@':
case '=':
case '|':
case '{':
case '}':
case '&':
case '*':
case '[':
return 1;
default:
return 0;
}
}
static int
needs_dquote_escaping(char c)
{
switch (c) {
case '"':
case '\\':
case '`':
case '$':
return 1;
default:
return 0;
}
}
static wchar_t *
unescape_string(const wchar_t *string, size_t length)
{
size_t i;
size_t j = 0;
wchar_t *unescaped = el_calloc(length + 1, sizeof(*string));
if (unescaped == NULL)
return NULL;
for (i = 0; i < length ; i++) {
if (string[i] == '\\')
continue;
unescaped[j++] = string[i];
}
unescaped[j] = 0;
return unescaped;
}
static char *
escape_filename(EditLine * el, const char *filename, int single_match,
const char *(*app_func)(const char *))
{
size_t original_len = 0;
size_t escaped_character_count = 0;
size_t offset = 0;
size_t newlen;
const char *s;
char c;
size_t s_quoted = 0; /* does the input contain a single quote */
size_t d_quoted = 0; /* does the input contain a double quote */
char *escaped_str;
wchar_t *temp = el->el_line.buffer;
const char *append_char = NULL;
if (filename == NULL)
return NULL;
while (temp != el->el_line.cursor) {
/*
* If we see a single quote but have not seen a double quote
* so far set/unset s_quote
*/
if (temp[0] == '\'' && !d_quoted)
s_quoted = !s_quoted;
/*
* vice versa to the above condition
*/
else if (temp[0] == '"' && !s_quoted)
d_quoted = !d_quoted;
temp++;
}
/* Count number of special characters so that we can calculate
* number of extra bytes needed in the new string
*/
for (s = filename; *s; s++, original_len++) {
c = *s;
/* Inside a single quote only single quotes need escaping */
if (s_quoted && c == '\'') {
escaped_character_count += 3;
continue;
}
/* Inside double quotes only ", \, ` and $ need escaping */
if (d_quoted && needs_dquote_escaping(c)) {
escaped_character_count++;
continue;
}
if (!s_quoted && !d_quoted && needs_escaping(c))
escaped_character_count++;
}
newlen = original_len + escaped_character_count + 1;
if (s_quoted || d_quoted)
newlen++;
if (single_match && app_func)
newlen++;
if ((escaped_str = el_malloc(newlen)) == NULL)
return NULL;
for (s = filename; *s; s++) {
c = *s;
if (!needs_escaping(c)) {
/* no escaping is required continue as usual */
escaped_str[offset++] = c;
continue;
}
/* single quotes inside single quotes require special handling */
if (c == '\'' && s_quoted) {
escaped_str[offset++] = '\'';
escaped_str[offset++] = '\\';
escaped_str[offset++] = '\'';
escaped_str[offset++] = '\'';
continue;
}
/* Otherwise no escaping needed inside single quotes */
if (s_quoted) {
escaped_str[offset++] = c;
continue;
}
/* No escaping needed inside a double quoted string either
* unless we see a '$', '\', '`', or '"' (itself)
*/
if (d_quoted && !needs_dquote_escaping(c)) {
escaped_str[offset++] = c;
continue;
}
/* If we reach here that means escaping is actually needed */
escaped_str[offset++] = '\\';
escaped_str[offset++] = c;
}
if (single_match && app_func) {
escaped_str[offset] = 0;
append_char = app_func(escaped_str);
/* we want to append space only if we are not inside quotes */
if (append_char[0] == ' ') {
if (!s_quoted && !d_quoted)
escaped_str[offset++] = append_char[0];
} else
escaped_str[offset++] = append_char[0];
}
/* close the quotes if single match and the match is not a directory */
if (single_match && (append_char && append_char[0] == ' ')) {
if (s_quoted)
escaped_str[offset++] = '\'';
else if (d_quoted)
escaped_str[offset++] = '"';
}
escaped_str[offset] = 0;
return escaped_str;
} }
/* /*
* return first found file name starting by the ``text'' or NULL if no * return first found file name starting by the ``text'' or NULL if no
* such file can be found * such file can be found
* value of ``state'' is ignored * value of ``state'' is ignored
* *
* it's caller's responsibility to free returned string * it's the caller's responsibility to free the returned string
*/ */
char * char *
filename_completion_function(const char *text, int state) fn_filename_completion_function(const char *text, int state)
{ {
static DIR *dir = NULL; static DIR *dir = NULL;
static char *filename = NULL, *dirname = NULL, *dirpath = NULL; static char *filename = NULL, *dirname = NULL, *dirpath = NULL;
static size_t filename_len = 0; static size_t filename_len = 0;
struct dirent *entry; struct dirent *entry;
char *temp; char *temp;
size_t len; size_t len;
if (state == 0 || dir == NULL) { if (state == 0 || dir == NULL) {
temp = strrchr(text, '/'); temp = strrchr(text, '/');
if (temp) { if (temp) {
char *nptr; char *nptr;
temp++; temp++;
nptr = realloc(filename, strlen(temp) + 1); nptr = el_realloc(filename, (strlen(temp) + 1) *
sizeof(*nptr));
if (nptr == NULL) { if (nptr == NULL) {
free(filename); el_free(filename);
filename = NULL;
return NULL; return NULL;
} }
filename = nptr; filename = nptr;
(void)strcpy(filename, temp); (void)strcpy(filename, temp);
len = temp - text; /* including last slash */ len = (size_t)(temp - text); /* including last slash *
nptr = realloc(dirname, len + 1); /
nptr = el_realloc(dirname, (len + 1) *
sizeof(*nptr));
if (nptr == NULL) { if (nptr == NULL) {
free(filename); el_free(dirname);
dirname = NULL;
return NULL; return NULL;
} }
dirname = nptr; dirname = nptr;
(void)strncpy(dirname, text, len); (void)strlcpy(dirname, text, len + 1);
dirname[len] = '\0';
} else { } else {
el_free(filename);
if (*text == 0) if (*text == 0)
filename = NULL; filename = NULL;
else { else {
filename = strdup(text); filename = strdup(text);
if (filename == NULL) if (filename == NULL)
return NULL; return NULL;
} }
el_free(dirname);
dirname = NULL; dirname = NULL;
} }
if (dir != NULL) { if (dir != NULL) {
(void)closedir(dir); (void)closedir(dir);
dir = NULL; dir = NULL;
} }
/* support for ``~user'' syntax */ /* support for ``~user'' syntax */
free(dirpath);
if (dirname == NULL && (dirname = strdup("./")) == NULL) el_free(dirpath);
return NULL; dirpath = NULL;
if (dirname == NULL) {
if (*dirname == '~') if ((dirname = strdup("")) == NULL)
dirpath = tilde_expand(dirname); return NULL;
dirpath = strdup("./");
} else if (*dirname == '~')
dirpath = fn_tilde_expand(dirname);
else else
dirpath = strdup(dirname); dirpath = strdup(dirname);
if (dirpath == NULL) if (dirpath == NULL)
return NULL; return NULL;
dir = opendir(dirpath); dir = opendir(dirpath);
if (!dir) if (!dir)
return (NULL); /* cannot open the directory */ return NULL; /* cannot open the directory */
/* will be used in cycle */ /* will be used in cycle */
filename_len = filename ? strlen(filename) : 0; filename_len = filename ? strlen(filename) : 0;
} }
/* find the match */ /* find the match */
while ((entry = readdir(dir)) != NULL) { while ((entry = readdir(dir)) != NULL) {
/* skip . and .. */ /* skip . and .. */
if (entry->d_name[0] == '.' && (!entry->d_name[1] if (entry->d_name[0] == '.' && (!entry->d_name[1]
|| (entry->d_name[1] == '.' && !entry->d_name[2]))) || (entry->d_name[1] == '.' && !entry->d_name[2])))
continue; continue;
if (filename_len == 0) if (filename_len == 0)
break; break;
/* otherwise, get first entry where first */ /* otherwise, get first entry where first */
/* filename_len characters are equal */ /* filename_len characters are equal */
if (entry->d_name[0] == filename[0] if (entry->d_name[0] == filename[0]
#if defined(__SVR4) || defined(__linux__) #if HAVE_STRUCT_DIRENT_D_NAMLEN
&& strlen(entry->d_name) >= filename_len
#else
&& entry->d_namlen >= filename_len && entry->d_namlen >= filename_len
#else
&& strlen(entry->d_name) >= filename_len
#endif #endif
&& strncmp(entry->d_name, filename, && strncmp(entry->d_name, filename,
filename_len) == 0) filename_len) == 0)
break; break;
} }
if (entry) { /* match found */ if (entry) { /* match found */
struct stat stbuf;
const char *isdir = "";
#if defined(__SVR4) || defined(__linux__) #if HAVE_STRUCT_DIRENT_D_NAMLEN
len = strlen(entry->d_name);
#else
len = entry->d_namlen; len = entry->d_namlen;
#else
len = strlen(entry->d_name);
#endif #endif
temp = malloc(strlen(dirpath) + len + 1);
if (temp == NULL)
return NULL;
(void)sprintf(temp, "%s%s", dirpath, entry->d_name); /* safe */
/* test, if it's directory */ len = strlen(dirname) + len + 1;
if (stat(temp, &stbuf) == 0 && S_ISDIR(stbuf.st_mode)) temp = el_calloc(len, sizeof(*temp));
isdir = "/";
free(temp);
temp = malloc(strlen(dirname) + len + 1 + 1);
if (temp == NULL) if (temp == NULL)
return NULL; return NULL;
(void)sprintf(temp, "%s%s%s", dirname, entry->d_name, isdir); (void)snprintf(temp, len, "%s%s", dirname, entry->d_name);
} else { } else {
(void)closedir(dir); (void)closedir(dir);
dir = NULL; dir = NULL;
temp = NULL; temp = NULL;
} }
return (temp); return temp;
} }
static const char *
append_char_function(const char *name)
{
struct stat stbuf;
char *expname = *name == '~' ? fn_tilde_expand(name) : NULL;
const char *rs = " ";
if (stat(expname ? expname : name, &stbuf) == -1)
goto out;
if (S_ISDIR(stbuf.st_mode))
rs = "/";
out:
if (expname)
el_free(expname);
return rs;
}
/* /*
* returns list of completions for text given * returns list of completions for text given
* non-static for readline. * non-static for readline.
*/ */
char ** completion_matches(const char *, char *(*)(const char *, int)); char ** completion_matches(const char *, char *(*)(const char *, int));
char ** char **
completion_matches(const char *text, char *(*genfunc)(const char *, int)) completion_matches(const char *text, char *(*genfunc)(const char *, int))
{ {
char **match_list = NULL, *retstr, *prevstr; char **match_list = NULL, *retstr, *prevstr;
size_t match_list_len, max_equal, which, i; size_t match_list_len, max_equal, which, i;
size_t matches; size_t matches;
matches = 0; matches = 0;
match_list_len = 1; match_list_len = 1;
while ((retstr = (*genfunc) (text, (int)matches)) != NULL) { while ((retstr = (*genfunc) (text, (int)matches)) != NULL) {
/* allow for list terminator here */ /* allow for list terminator here */
if (matches + 3 >= match_list_len) { if (matches + 3 >= match_list_len) {
char **nmatch_list; char **nmatch_list;
while (matches + 3 >= match_list_len) while (matches + 3 >= match_list_len)
match_list_len <<= 1; match_list_len <<= 1;
nmatch_list = realloc(match_list, nmatch_list = el_realloc(match_list,
match_list_len * sizeof(char *)); match_list_len * sizeof(*nmatch_list));
if (nmatch_list == NULL) { if (nmatch_list == NULL) {
free(match_list); el_free(match_list);
return NULL; return NULL;
} }
match_list = nmatch_list; match_list = nmatch_list;
} }
match_list[++matches] = retstr; match_list[++matches] = retstr;
} }
if (!match_list) if (!match_list)
return NULL; /* nothing found */ return NULL; /* nothing found */
skipping to change at line 292 skipping to change at line 507
which = 2; which = 2;
prevstr = match_list[1]; prevstr = match_list[1];
max_equal = strlen(prevstr); max_equal = strlen(prevstr);
for (; which <= matches; which++) { for (; which <= matches; which++) {
for (i = 0; i < max_equal && for (i = 0; i < max_equal &&
prevstr[i] == match_list[which][i]; i++) prevstr[i] == match_list[which][i]; i++)
continue; continue;
max_equal = i; max_equal = i;
} }
retstr = malloc(max_equal + 1); retstr = el_calloc(max_equal + 1, sizeof(*retstr));
if (retstr == NULL) { if (retstr == NULL) {
free(match_list); el_free(match_list);
return NULL; return NULL;
} }
(void)strncpy(retstr, match_list[1], max_equal); (void)strlcpy(retstr, match_list[1], max_equal + 1);
retstr[max_equal] = '\0';
match_list[0] = retstr; match_list[0] = retstr;
/* add NULL as last pointer to the array */ /* add NULL as last pointer to the array */
match_list[matches + 1] = (char *) NULL; match_list[matches + 1] = NULL;
return (match_list); return match_list;
} }
/* /*
* Sort function for qsort(). Just wrapper around strcasecmp(). * Sort function for qsort(). Just wrapper around strcasecmp().
*/ */
static int static int
_fn_qsort_string_compare(const void *i1, const void *i2) _fn_qsort_string_compare(const void *i1, const void *i2)
{ {
const char *s1 = ((const char * const *)i1)[0]; const char *s1 = ((const char * const *)i1)[0];
const char *s2 = ((const char * const *)i2)[0]; const char *s2 = ((const char * const *)i2)[0];
return strcasecmp(s1, s2); return strcasecmp(s1, s2);
} }
/* /*
* Display list of strings in columnar format on readline's output stream. * Display list of strings in columnar format on readline's output stream.
* 'matches' is list of strings, 'len' is number of strings in 'matches', * 'matches' is list of strings, 'num' is number of strings in 'matches',
* 'max' is maximum length of string in 'matches'. * 'width' is maximum length of string in 'matches'.
*
* matches[0] is not one of the match strings, but it is counted in
* num, so the strings are matches[1] *through* matches[num-1].
*/ */
void void
fn_display_match_list (EditLine *el, char **matches, int len, int max) fn_display_match_list(EditLine * el, char **matches, size_t num, size_t width,
const char *(*app_func) (const char *))
{ {
int i, idx, limit, count; size_t line, lines, col, cols, thisguy;
int screenwidth = el->el_term.t_size.h; int screenwidth = el->el_terminal.t_size.h;
if (app_func == NULL)
app_func = append_char_function;
/* Ignore matches[0]. Avoid 1-based array logic below. */
matches++;
num--;
/*
* Find out how many entries can be put on one line; count
* with one space between strings the same way it's printed.
*/
cols = (size_t)screenwidth / (width + 2);
if (cols == 0)
cols = 1;
/* how many lines of output, rounded up */
lines = (num + cols - 1) / cols;
/* Sort the items. */
qsort(matches, num, sizeof(char *), _fn_qsort_string_compare);
/* /*
* Find out how many entries can be put on one line, count * On the ith line print elements i, i+lines, i+lines*2, etc.
* with two spaces between strings.
*/ */
limit = screenwidth / (max + 2); for (line = 0; line < lines; line++) {
if (limit == 0) for (col = 0; col < cols; col++) {
limit = 1; thisguy = line + col * lines;
if (thisguy >= num)
/* how many lines of output */ break;
count = len / limit; (void)fprintf(el->el_outfile, "%s%s%s",
if (count * limit < len) col == 0 ? "" : " ", matches[thisguy],
count++; (*app_func)(matches[thisguy]));
(void)fprintf(el->el_outfile, "%-*s",
/* Sort the items if they are not already sorted. */ (int) (width - strlen(matches[thisguy])), "");
qsort(&matches[1], (size_t)(len - 1), sizeof(char *), }
_fn_qsort_string_compare);
idx = 1;
for(; count > 0; count--) {
for(i = 0; i < limit && matches[idx]; i++, idx++)
(void)fprintf(el->el_outfile, "%-*s ", max,
matches[idx]);
(void)fprintf(el->el_outfile, "\n"); (void)fprintf(el->el_outfile, "\n");
} }
} }
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 * lengt
h,
int do_unescape)
{
/* We now look backwards for the start of a filename/variable word */
const wchar_t *ctemp = cursor;
wchar_t *temp;
size_t len;
/* if the cursor is placed at a slash or a quote, we need to find the
* word before it
*/
if (ctemp > buffer) {
switch (ctemp[-1]) {
case '\\':
case '\'':
case '"':
ctemp--;
break;
default:
break;
}
}
for (;;) {
if (ctemp <= buffer)
break;
if (wcschr(word_break, ctemp[-1])) {
if (ctemp - buffer >= 2 && ctemp[-2] == '\\') {
ctemp -= 2;
continue;
}
break;
}
if (special_prefixes && wcschr(special_prefixes, ctemp[-1]))
break;
ctemp--;
}
len = (size_t) (cursor - ctemp);
if (len == 1 && (ctemp[0] == '\'' || ctemp[0] == '"')) {
len = 0;
ctemp++;
}
*length = len;
if (do_unescape) {
wchar_t *unescaped_word = unescape_string(ctemp, len);
if (unescaped_word == NULL)
return NULL;
return unescaped_word;
}
temp = el_malloc((len + 1) * sizeof(*temp));
(void) wcsncpy(temp, ctemp, len);
temp[len] = '\0';
return temp;
}
/* /*
* Complete the word at or before point, * Complete the word at or before point,
* 'what_to_do' says what to do with the completion. * 'what_to_do' says what to do with the completion.
* \t means do standard completion. * \t means do standard completion.
* `?' means list the possible completions. * `?' means list the possible completions.
* `*' means insert all of the possible completions. * `*' means insert all of the possible completions.
* `!' means to do standard completion, and list all possible completions if * `!' means to do standard completion, and list all possible completions if
* there is more than one. * there is more than one.
* *
* Note: '*' support is not implemented * Note: '*' support is not implemented
* '!' could never be invoked * '!' could never be invoked
*/ */
int int
fn_complete(EditLine *el, fn_complete(EditLine *el,
char *(*complet_func)(const char *, int), char *(*complet_func)(const char *, int),
char **(*attempted_completion_function)(const char *, int, int), char **(*attempted_completion_function)(const char *, int, int),
const char *word_break, const char *special_prefixes, const wchar_t *word_break, const wchar_t *special_prefixes,
char append_character, int query_items, const char *(*app_func)(const char *), size_t query_items,
int *completion_type, int *over, int *point, int *end) int *completion_type, int *over, int *point, int *end)
{ {
const LineInfo *li; const LineInfoW *li;
char *temp, **matches; wchar_t *temp;
const char *ctemp; char **matches;
char *completion;
size_t len; size_t len;
int what_to_do = '\t'; int what_to_do = '\t';
int retval = CC_NORM;
int do_unescape = attempted_completion_function == NULL? 1: 0;
if (el->el_state.lastcmd == el->el_state.thiscmd) if (el->el_state.lastcmd == el->el_state.thiscmd)
what_to_do = '?'; what_to_do = '?';
/* readline's rl_complete() has to be told what we did... */ /* readline's rl_complete() has to be told what we did... */
if (completion_type != NULL) if (completion_type != NULL)
*completion_type = what_to_do; *completion_type = what_to_do;
if (!complet_func) if (!complet_func)
complet_func = filename_completion_function; complet_func = fn_filename_completion_function;
if (!app_func)
/* We now look backwards for the start of a filename/variable word */ app_func = append_char_function;
li = el_line(el);
ctemp = (const char *) li->cursor; li = el_wline(el);
while (ctemp > li->buffer temp = find_word_to_complete(li->cursor,
&& !strchr(word_break, ctemp[-1]) li->buffer, word_break, special_prefixes, &len, do_unescape);
&& (!special_prefixes || !strchr(special_prefixes, ctemp[-1]) ) ) if (temp == NULL)
ctemp--; goto out;
len = li->cursor - ctemp;
temp = alloca(len + 1);
(void)strncpy(temp, ctemp, len);
temp[len] = '\0';
/* these can be used by function called in completion_matches() */ /* these can be used by function called in completion_matches() */
/* or (*attempted_completion_function)() */ /* or (*attempted_completion_function)() */
if (point != 0) if (point != NULL)
*point = li->cursor - li->buffer; *point = (int)(li->cursor - li->buffer);
if (end != NULL) if (end != NULL)
*end = li->lastchar - li->buffer; *end = (int)(li->lastchar - li->buffer);
if (attempted_completion_function) { if (attempted_completion_function) {
int cur_off = li->cursor - li->buffer; int cur_off = (int)(li->cursor - li->buffer);
matches = (*attempted_completion_function) (temp, matches = (*attempted_completion_function)(
(int)(cur_off - len), cur_off); ct_encode_string(temp, &el->el_scratch),
cur_off - (int)len, cur_off);
} else } else
matches = 0; matches = NULL;
if (!attempted_completion_function || if (!attempted_completion_function ||
(over != NULL && *over && !matches)) (over != NULL && !*over && !matches))
matches = completion_matches(temp, complet_func); matches = completion_matches(
ct_encode_string(temp, &el->el_scratch), complet_func);
if (over != NULL) if (over != NULL)
*over = 0; *over = 0;
if (matches) { if (matches == NULL) {
int i, retval = CC_REFRESH; goto out;
int matches_num, maxlen, match_len, match_display=1; }
int i;
size_t matches_num, maxlen, match_len, match_display=1;
int single_match = matches[2] == NULL &&
(matches[1] == NULL || strcmp(matches[0], matches[1]) == 0);
retval = CC_REFRESH;
if (matches[0][0] != '\0') {
el_deletestr(el, (int)len);
if (!attempted_completion_function)
completion = escape_filename(el, matches[0],
single_match, app_func);
else
completion = strdup(matches[0]);
if (completion == NULL)
goto out;
/* /*
* Only replace the completed string with common part of * Replace the completed string with the common part of
* possible matches if there is possible completion. * all possible matches if there is a possible completion.
*/ */
if (matches[0][0] != '\0') { el_winsertstr(el,
el_deletestr(el, (int) len); ct_decode_string(completion, &el->el_scratch));
el_insertstr(el, matches[0]);
}
if (what_to_do == '?') if (single_match && attempted_completion_function) {
goto display_matches;
if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) {
/* /*
* We found exact match. Add a space after * We found an exact match. Add a space after
* it, unless we do filename completion and the * it, unless we do filename completion and the
* object is a directory. * object is a directory. Also do necessary
*/ * escape quoting
size_t alen = strlen(matches[0]);
if ((complet_func != filename_completion_function
|| (alen > 0 && (matches[0])[alen - 1] != '/'))
&& append_character) {
char buf[2];
buf[0] = append_character;
buf[1] = '\0';
el_insertstr(el, buf);
}
} else if (what_to_do == '!') {
display_matches:
/*
* More than one match and requested to list possible
* matches.
*/ */
el_winsertstr(el, ct_decode_string(
(*app_func)(completion), &el->el_scratch));
}
free(completion);
}
for(i=1, maxlen=0; matches[i]; i++) { if (!single_match && (what_to_do == '!' || what_to_do == '?')) {
match_len = strlen(matches[i]); /*
if (match_len > maxlen) * More than one match and requested to list possible
maxlen = match_len; * matches.
} */
matches_num = i - 1;
/* newline to get on next line from command line */ for(i = 1, maxlen = 0; matches[i]; i++) {
(void)fprintf(el->el_outfile, "\n"); match_len = strlen(matches[i]);
if (match_len > maxlen)
maxlen = match_len;
}
/* matches[1] through matches[i-1] are available */
matches_num = (size_t)(i - 1);
/* /* newline to get on next line from command line */
* If there are too many items, ask user for display (void)fprintf(el->el_outfile, "\n");
* confirmation.
*/ /*
if (matches_num > query_items) { * If there are too many items, ask user for display
(void)fprintf(el->el_outfile, * confirmation.
"Display all %d possibilities? (y or n) ", */
matches_num); if (matches_num > query_items) {
(void)fflush(el->el_outfile); (void)fprintf(el->el_outfile,
if (getc(stdin) != 'y') "Display all %zu possibilities? (y or n) ",
match_display = 0; matches_num);
(void)fprintf(el->el_outfile, "\n"); (void)fflush(el->el_outfile);
} if (getc(stdin) != 'y')
match_display = 0;
(void)fprintf(el->el_outfile, "\n");
}
if (match_display) if (match_display) {
fn_display_match_list(el, matches, matches_num,
maxlen);
retval = CC_REDISPLAY;
} else if (matches[0][0]) {
/* /*
* There was some common match, but the name was * Interface of this function requires the
* not complete enough. Next tab will print possible * strings be matches[1..num-1] for compat.
* completions. * We have matches_num strings not counting
* the prefix in matches[0], so we need to
* add 1 to matches_num for the call.
*/ */
el_beep(el); fn_display_match_list(el, matches,
} else { matches_num+1, maxlen, app_func);
/* lcd is not a valid object - further specification */
/* is needed */
el_beep(el);
retval = CC_NORM;
} }
retval = CC_REDISPLAY;
/* free elements of array and the array itself */ } else if (matches[0][0]) {
for (i = 0; matches[i]; i++) /*
free(matches[i]); * There was some common match, but the name was
free(matches), matches = NULL; * not complete enough. Next tab will print possible
* completions.
return (retval); */
el_beep(el);
} else {
/* lcd is not a valid object - further specification */
/* is needed */
el_beep(el);
retval = CC_NORM;
} }
return (CC_NORM);
/* free elements of array and the array itself */
for (i = 0; matches[i]; i++)
el_free(matches[i]);
el_free(matches);
matches = NULL;
out:
el_free(temp);
return retval;
} }
/* /*
* el-compatible wrapper around rl_complete; needed for key binding * el-compatible wrapper around rl_complete; needed for key binding
*/ */
/* ARGSUSED */ /* ARGSUSED */
unsigned char unsigned char
_el_fn_complete(EditLine *el, int ch __attribute__((__unused__))) _el_fn_complete(EditLine *el, int ch __attribute__((__unused__)))
{ {
return (unsigned char)fn_complete(el, NULL, NULL, return (unsigned char)fn_complete(el, NULL, NULL,
break_chars, NULL, ' ', 100, break_chars, NULL, NULL, (size_t)100,
NULL, NULL, NULL, NULL); NULL, NULL, NULL, NULL);
} }
 End of changes. 85 change blocks. 
208 lines changed or deleted 515 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)