#ifndef SMALL
#include <ctype.h>
#include <err.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ftp_var.h"
static int comparstr(const void *, const void *);
static unsigned char complete_ambiguous(char *, int, StringList *);
static unsigned char complete_command(char *, int);
static unsigned char complete_local(char *, int);
static unsigned char complete_remote(char *, int);
static void ftpvis(char *, size_t, const char *, size_t);
static int
comparstr(const void *a, const void *b)
{
return (strcmp(*(char **)a, *(char **)b));
}
static unsigned char
complete_ambiguous(char *word, int list, StringList *words)
{
char insertstr[PATH_MAX * 2];
char *lastmatch;
int i, j;
size_t matchlen, wordlen;
wordlen = strlen(word);
if (words->sl_cur == 0)
return (CC_ERROR);
if (words->sl_cur == 1) {
char *p = words->sl_str[0] + wordlen;
ftpvis(insertstr, sizeof(insertstr), p, strlen(p));
if (el_insertstr(el, insertstr) == -1)
return (CC_ERROR);
else
return (CC_REFRESH);
}
if (!list) {
lastmatch = words->sl_str[0];
matchlen = strlen(lastmatch);
for (i = 1 ; i < words->sl_cur ; i++) {
for (j = wordlen ; j < strlen(words->sl_str[i]); j++)
if (lastmatch[j] != words->sl_str[i][j])
break;
if (j < matchlen)
matchlen = j;
}
if (matchlen > wordlen) {
ftpvis(insertstr, sizeof(insertstr),
lastmatch + wordlen, matchlen - wordlen);
if (el_insertstr(el, insertstr) == -1)
return (CC_ERROR);
else
return (CC_REFRESH);
}
}
putc('\n', ttyout);
qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr);
list_vertical(words);
return (CC_REDISPLAY);
}
static unsigned char
complete_command(char *word, int list)
{
struct cmd *c;
StringList *words;
size_t wordlen;
unsigned char rv;
words = sl_init();
wordlen = strlen(word);
for (c = cmdtab; c->c_name != NULL; c++) {
if (wordlen > strlen(c->c_name))
continue;
if (strncmp(word, c->c_name, wordlen) == 0)
sl_add(words, c->c_name);
}
rv = complete_ambiguous(word, list, words);
sl_free(words, 0);
return (rv);
}
static unsigned char
complete_local(char *word, int list)
{
StringList *words;
char dir[PATH_MAX];
char *file;
DIR *dd;
struct dirent *dp;
unsigned char rv;
if ((file = strrchr(word, '/')) == NULL) {
dir[0] = '.';
dir[1] = '\0';
file = word;
} else {
if (file == word) {
dir[0] = '/';
dir[1] = '\0';
} else {
(void)strlcpy(dir, word, (size_t)(file - word) + 1);
}
file++;
}
if ((dd = opendir(dir)) == NULL)
return (CC_ERROR);
words = sl_init();
for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
if (strlen(file) > dp->d_namlen)
continue;
if (strncmp(file, dp->d_name, strlen(file)) == 0) {
char *tcp;
tcp = strdup(dp->d_name);
if (tcp == NULL)
errx(1, "Can't allocate memory for local dir");
sl_add(words, tcp);
}
}
closedir(dd);
rv = complete_ambiguous(file, list, words);
sl_free(words, 1);
return (rv);
}
static unsigned char
complete_remote(char *word, int list)
{
static StringList *dirlist;
static char lastdir[PATH_MAX];
StringList *words;
char dir[PATH_MAX];
char *file, *cp;
int i;
unsigned char rv;
char *dummyargv[] = { "complete", dir, NULL };
if ((file = strrchr(word, '/')) == NULL) {
dir[0] = '.';
dir[1] = '\0';
file = word;
} else {
cp = file;
while (*cp == '/' && cp > word)
cp--;
(void)strlcpy(dir, word, (size_t)(cp - word + 2));
file++;
}
if (dirchange || strcmp(dir, lastdir) != 0) {
char *emesg;
sl_free(dirlist, 1);
dirlist = sl_init();
mflag = 1;
emesg = NULL;
if (debug)
(void)putc('\n', ttyout);
while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) {
char *tcp;
if (!mflag)
continue;
if (*cp == '\0') {
mflag = 0;
continue;
}
tcp = strrchr(cp, '/');
if (tcp)
tcp++;
else
tcp = cp;
tcp = strdup(tcp);
if (tcp == NULL)
errx(1, "Can't allocate memory for remote dir");
sl_add(dirlist, tcp);
}
if (emesg != NULL) {
fprintf(ttyout, "\n%s\n", emesg);
return (CC_REDISPLAY);
}
(void)strlcpy(lastdir, dir, sizeof lastdir);
dirchange = 0;
}
words = sl_init();
for (i = 0; i < dirlist->sl_cur; i++) {
cp = dirlist->sl_str[i];
if (strlen(file) > strlen(cp))
continue;
if (strncmp(file, cp, strlen(file)) == 0)
sl_add(words, cp);
}
rv = complete_ambiguous(file, list, words);
sl_free(words, 0);
return (rv);
}
unsigned char
complete(EditLine *el, int ch)
{
static char word[FTPBUFLEN];
static int lastc_argc, lastc_argo;
struct cmd *c;
const LineInfo *lf;
int celems, dolist;
size_t len;
lf = el_line(el);
len = lf->lastchar - lf->buffer;
if (len >= sizeof(line))
return (CC_ERROR);
(void)memcpy(line, lf->buffer, len);
line[len] = '\0';
cursor_pos = line + (lf->cursor - lf->buffer);
lastc_argc = cursor_argc;
lastc_argo = cursor_argo;
makeargv();
if (cursor_argo >= sizeof(word))
return (CC_ERROR);
dolist = 0;
if (lastc_argc == cursor_argc && lastc_argo == cursor_argo
&& strncmp(word, margv[cursor_argc], cursor_argo) == 0)
dolist = 1;
else if (cursor_argo)
memcpy(word, margv[cursor_argc], cursor_argo);
word[cursor_argo] = '\0';
if (cursor_argc == 0)
return (complete_command(word, dolist));
c = getcmd(margv[0]);
if (c == (struct cmd *)-1 || c == 0)
return (CC_ERROR);
celems = strlen(c->c_complete);
if ((cursor_argc > celems) && (celems > 0)
&& isupper((unsigned char)c->c_complete[celems - 1]))
cursor_argc = celems;
if (cursor_argc > celems)
return (CC_ERROR);
switch (c->c_complete[cursor_argc - 1]) {
case 'l':
case 'L':
return (complete_local(word, dolist));
case 'r':
case 'R':
if (connected != -1) {
fputs("\nMust be logged in to complete.\n", ttyout);
return (CC_REDISPLAY);
}
return (complete_remote(word, dolist));
case 'c':
case 'C':
return (complete_command(word, dolist));
case 'n':
return (CC_ERROR);
}
return (CC_ERROR);
}
static void
ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen)
{
size_t di, si;
di = si = 0;
while (di + 1 < dstlen && si < srclen && src[si] != '\0') {
switch (src[si]) {
case '\\':
case ' ':
case '\t':
case '\r':
case '\n':
case '"':
if (di + 3 >= dstlen)
break;
dst[di++] = '\\';
default:
dst[di++] = src[si++];
}
}
if (dstlen != 0)
dst[di] = '\0';
}
#endif