1*6478b405Sandvar /* $NetBSD: complete.c,v 1.25 2022/08/06 18:26:43 andvar Exp $ */
243c97412Schristos
343c97412Schristos /*-
48207b28aSchristos * Copyright (c) 1997-2000,2005,2006 The NetBSD Foundation, Inc.
543c97412Schristos * All rights reserved.
643c97412Schristos *
743c97412Schristos * This code is derived from software contributed to The NetBSD Foundation
843c97412Schristos * by Luke Mewburn.
943c97412Schristos *
1043c97412Schristos * Redistribution and use in source and binary forms, with or without
1143c97412Schristos * modification, are permitted provided that the following conditions
1243c97412Schristos * are met:
1343c97412Schristos * 1. Redistributions of source code must retain the above copyright
1443c97412Schristos * notice, this list of conditions and the following disclaimer.
1543c97412Schristos * 2. Redistributions in binary form must reproduce the above copyright
1643c97412Schristos * notice, this list of conditions and the following disclaimer in the
1743c97412Schristos * documentation and/or other materials provided with the distribution.
1843c97412Schristos *
1943c97412Schristos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2043c97412Schristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2143c97412Schristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2243c97412Schristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2343c97412Schristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2443c97412Schristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2543c97412Schristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2643c97412Schristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2743c97412Schristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2843c97412Schristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2943c97412Schristos * POSSIBILITY OF SUCH DAMAGE.
3043c97412Schristos */
3143c97412Schristos
32f3098750Schristos /*
33f3098750Schristos * Most of this is derived or copied from src/usr.bin/ftp/complete.c (1.41).
34f3098750Schristos */
3543c97412Schristos
368207b28aSchristos #ifdef USE_EDITLINE
3743c97412Schristos
3843c97412Schristos #include <sys/cdefs.h>
3943c97412Schristos #ifndef lint
40*6478b405Sandvar __RCSID("$NetBSD: complete.c,v 1.25 2022/08/06 18:26:43 andvar Exp $");
4143c97412Schristos #endif /* not lint */
4243c97412Schristos
4343c97412Schristos /*
4443c97412Schristos * FTP user program - command and file completion routines
4543c97412Schristos */
4643c97412Schristos
47f3098750Schristos #include <assert.h>
4843c97412Schristos #include <ctype.h>
4943c97412Schristos #include <err.h>
5043c97412Schristos #include <dirent.h>
5143c97412Schristos #include <glob.h>
5243c97412Schristos #include <stdio.h>
5343c97412Schristos #include <stdlib.h>
5443c97412Schristos #include <string.h>
5543c97412Schristos #include <stringlist.h>
5626114345Schristos #include <termcap.h>
578207b28aSchristos #include <util.h>
5843c97412Schristos
59f3098750Schristos #include <sys/param.h>
60f3098750Schristos #include <sys/stat.h>
61f3098750Schristos
6243c97412Schristos #include "rcv.h" /* includes "glob.h" */
6343c97412Schristos #include "extern.h"
6443c97412Schristos #include "complete.h"
658207b28aSchristos #ifdef MIME_SUPPORT
668207b28aSchristos #include "mime.h"
678207b28aSchristos #endif
68ca13337dSchristos #include "sig.h"
69f3098750Schristos #ifdef THREAD_SUPPORT
70f3098750Schristos #include "thread.h"
71f3098750Schristos #endif
72f3098750Schristos
73f3098750Schristos #define BELL 0x7
7443c97412Schristos
7543c97412Schristos /*
7643c97412Schristos * Global variables
7743c97412Schristos */
7843c97412Schristos static int doglob = 1; /* glob local file names */
7943c97412Schristos
8043c97412Schristos #define ttyout stdout
8143c97412Schristos #define ttywidth screenwidth /* in "glob.h" */
8243c97412Schristos #define ttyheight screenheight /* in "glob.h" */
8343c97412Schristos
8443c97412Schristos /************************************************************************/
858207b28aSchristos /* from src/usr.bin/ftp/utils.h (1.135) - begin */
8643c97412Schristos
8743c97412Schristos /*
8843c97412Schristos * List words in stringlist, vertically arranged
8943c97412Schristos */
9043c97412Schristos static void
list_vertical(StringList * sl)9143c97412Schristos list_vertical(StringList *sl)
9243c97412Schristos {
93c172e3b9Slukem int k;
94c172e3b9Slukem size_t i, j, columns, lines;
9543c97412Schristos char *p;
9643c97412Schristos size_t w, width;
9743c97412Schristos
9843c97412Schristos width = 0;
9943c97412Schristos
10043c97412Schristos for (i = 0; i < sl->sl_cur; i++) {
10143c97412Schristos w = strlen(sl->sl_str[i]);
10243c97412Schristos if (w > width)
10343c97412Schristos width = w;
10443c97412Schristos }
10543c97412Schristos width = (width + 8) &~ 7;
10643c97412Schristos
10743c97412Schristos columns = ttywidth / width;
10843c97412Schristos if (columns == 0)
10943c97412Schristos columns = 1;
11043c97412Schristos lines = (sl->sl_cur + columns - 1) / columns;
111f3098750Schristos k = 0;
11243c97412Schristos for (i = 0; i < lines; i++) {
11343c97412Schristos for (j = 0; j < columns; j++) {
11443c97412Schristos p = sl->sl_str[j * lines + i];
11543c97412Schristos if (p)
1168207b28aSchristos (void)fputs(p, ttyout);
11743c97412Schristos if (j * lines + i + lines >= sl->sl_cur) {
1188207b28aSchristos (void)putc('\n', ttyout);
11943c97412Schristos break;
12043c97412Schristos }
12143c97412Schristos if (p) {
12243c97412Schristos w = strlen(p);
12343c97412Schristos while (w < width) {
12443c97412Schristos w = (w + 8) &~ 7;
12543c97412Schristos (void)putc('\t', ttyout);
12643c97412Schristos }
12743c97412Schristos }
12843c97412Schristos }
129f3098750Schristos if (ttyheight > 2 && ++k == ttyheight - 2) {
130f3098750Schristos int ch;
131f3098750Schristos k = 0;
132f3098750Schristos (void)fputs("--more--", ttyout);
133f3098750Schristos while ((ch = getchar()) != EOF && ch != ' ' && ch != 'q')
134f3098750Schristos (void)putc(BELL, ttyout);
135f3098750Schristos (void)fputs("\r \r", ttyout);
136f3098750Schristos if (ch == 'q')
137f3098750Schristos break;
138f3098750Schristos }
13943c97412Schristos }
14043c97412Schristos }
14143c97412Schristos
14243c97412Schristos /*
14343c97412Schristos * Copy characters from src into dst, \ quoting characters that require it
14443c97412Schristos */
14543c97412Schristos static void
ftpvis(char * dst,size_t dstlen,const char * src,size_t srclen)14643c97412Schristos ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen)
14743c97412Schristos {
148c172e3b9Slukem size_t di, si;
14943c97412Schristos
15043c97412Schristos for (di = si = 0;
15143c97412Schristos src[si] != '\0' && di < dstlen && si < srclen;
15243c97412Schristos di++, si++) {
15343c97412Schristos switch (src[si]) {
15443c97412Schristos case '\\':
15543c97412Schristos case ' ':
15643c97412Schristos case '\t':
15743c97412Schristos case '\r':
15843c97412Schristos case '\n':
15943c97412Schristos case '"':
16043c97412Schristos dst[di++] = '\\';
16143c97412Schristos if (di >= dstlen)
16243c97412Schristos break;
16343c97412Schristos /* FALLTHROUGH */
16443c97412Schristos default:
16543c97412Schristos dst[di] = src[si];
16643c97412Schristos }
16743c97412Schristos }
16843c97412Schristos dst[di] = '\0';
16943c97412Schristos }
17043c97412Schristos
17143c97412Schristos /*
17243c97412Schristos * sl_init() with inbuilt error checking
17343c97412Schristos */
17443c97412Schristos static StringList *
mail_sl_init(void)1758207b28aSchristos mail_sl_init(void)
17643c97412Schristos {
17743c97412Schristos StringList *p;
17843c97412Schristos
17943c97412Schristos p = sl_init();
18043c97412Schristos if (p == NULL)
181b8afdde7Schristos err(EXIT_FAILURE, "Unable to allocate memory for stringlist");
182f3098750Schristos return p;
18343c97412Schristos }
18443c97412Schristos
18543c97412Schristos
18643c97412Schristos /*
18743c97412Schristos * sl_add() with inbuilt error checking
18843c97412Schristos */
18943c97412Schristos static void
mail_sl_add(StringList * sl,char * i)1908207b28aSchristos mail_sl_add(StringList *sl, char *i)
19143c97412Schristos {
19243c97412Schristos
19343c97412Schristos if (sl_add(sl, i) == -1)
194b8afdde7Schristos err(EXIT_FAILURE, "Unable to add `%s' to stringlist", i);
19543c97412Schristos }
19643c97412Schristos
19743c97412Schristos
19843c97412Schristos /*
19943c97412Schristos * Glob a local file name specification with the expectation of a single
20043c97412Schristos * return value. Can't control multiple values being expanded from the
20143c97412Schristos * expression, we return only the first.
20243c97412Schristos * Returns NULL on error, or a pointer to a buffer containing the filename
203*6478b405Sandvar * that's the caller's responsibility to free(3) when finished with.
20443c97412Schristos */
20543c97412Schristos static char *
globulize(const char * pattern)20643c97412Schristos globulize(const char *pattern)
20743c97412Schristos {
20843c97412Schristos glob_t gl;
20943c97412Schristos int flags;
21043c97412Schristos char *p;
21143c97412Schristos
21243c97412Schristos if (!doglob)
2138207b28aSchristos return estrdup(pattern);
21443c97412Schristos
21543c97412Schristos flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
2168207b28aSchristos (void)memset(&gl, 0, sizeof(gl));
21743c97412Schristos if (glob(pattern, flags, NULL, &gl) || gl.gl_pathc == 0) {
21843c97412Schristos warnx("%s: not found", pattern);
21943c97412Schristos globfree(&gl);
220f3098750Schristos return NULL;
22143c97412Schristos }
2228207b28aSchristos p = estrdup(gl.gl_pathv[0]);
22343c97412Schristos globfree(&gl);
224f3098750Schristos return p;
22543c97412Schristos }
22643c97412Schristos
2278207b28aSchristos /* from src/usr.bin/ftp/utils.h (1.135) - end */
22843c97412Schristos /************************************************************************/
22943c97412Schristos
23043c97412Schristos static int
comparstr(const void * a,const void * b)23143c97412Schristos comparstr(const void *a, const void *b)
23243c97412Schristos {
233f3098750Schristos return strcmp(*(const char * const *)a, *(const char * const *)b);
23443c97412Schristos }
23543c97412Schristos
23643c97412Schristos /*
23743c97412Schristos * Determine if complete is ambiguous. If unique, insert.
23843c97412Schristos * If no choices, error. If unambiguous prefix, insert that.
23943c97412Schristos * Otherwise, list choices. words is assumed to be filtered
24043c97412Schristos * to only contain possible choices.
24143c97412Schristos * Args:
24243c97412Schristos * word word which started the match
2438207b28aSchristos * dolist list by default
24443c97412Schristos * words stringlist containing possible matches
24543c97412Schristos * Returns a result as per el_set(EL_ADDFN, ...)
24643c97412Schristos */
24743c97412Schristos static unsigned char
complete_ambiguous(EditLine * el,char * word,int dolist,StringList * words)2488207b28aSchristos complete_ambiguous(EditLine *el, char *word, int dolist, StringList *words)
24943c97412Schristos {
25043c97412Schristos char insertstr[MAXPATHLEN];
25143c97412Schristos char *lastmatch, *p;
252c172e3b9Slukem size_t i, j, matchlen, wordlen;
25343c97412Schristos
25443c97412Schristos wordlen = strlen(word);
25543c97412Schristos if (words->sl_cur == 0)
256f3098750Schristos return CC_ERROR; /* no choices available */
25743c97412Schristos
25843c97412Schristos if (words->sl_cur == 1) { /* only once choice available */
25943c97412Schristos p = words->sl_str[0] + wordlen;
26043c97412Schristos if (*p == '\0') /* at end of word? */
261f3098750Schristos return CC_REFRESH;
26243c97412Schristos ftpvis(insertstr, sizeof(insertstr), p, strlen(p));
26343c97412Schristos if (el_insertstr(el, insertstr) == -1)
264f3098750Schristos return CC_ERROR;
26543c97412Schristos else
266f3098750Schristos return CC_REFRESH;
26743c97412Schristos }
26843c97412Schristos
2698207b28aSchristos if (!dolist) {
27043c97412Schristos matchlen = 0;
27143c97412Schristos lastmatch = words->sl_str[0];
27243c97412Schristos matchlen = strlen(lastmatch);
27343c97412Schristos for (i = 1; i < words->sl_cur; i++) {
27443c97412Schristos for (j = wordlen; j < strlen(words->sl_str[i]); j++)
27543c97412Schristos if (lastmatch[j] != words->sl_str[i][j])
27643c97412Schristos break;
27743c97412Schristos if (j < matchlen)
27843c97412Schristos matchlen = j;
27943c97412Schristos }
280f3098750Schristos if (matchlen >= wordlen) {
28143c97412Schristos ftpvis(insertstr, sizeof(insertstr),
28243c97412Schristos lastmatch + wordlen, matchlen - wordlen);
28343c97412Schristos if (el_insertstr(el, insertstr) == -1)
284f3098750Schristos return CC_ERROR;
28543c97412Schristos else
286f3098750Schristos return CC_REFRESH_BEEP;
28743c97412Schristos }
28843c97412Schristos }
28943c97412Schristos
2908207b28aSchristos (void)putc('\n', ttyout);
29143c97412Schristos qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr);
292f3098750Schristos
29343c97412Schristos list_vertical(words);
294f3098750Schristos return CC_REDISPLAY;
29543c97412Schristos }
29643c97412Schristos
29743c97412Schristos /*
29843c97412Schristos * Complete a mail command.
29943c97412Schristos */
30043c97412Schristos static unsigned char
complete_command(EditLine * el,char * word,int dolist)3018207b28aSchristos complete_command(EditLine *el, char *word, int dolist)
30243c97412Schristos {
30343c97412Schristos const struct cmd *c;
30443c97412Schristos StringList *words;
30543c97412Schristos size_t wordlen;
30643c97412Schristos unsigned char rv;
30743c97412Schristos
3088207b28aSchristos words = mail_sl_init();
30943c97412Schristos wordlen = strlen(word);
31043c97412Schristos
31143c97412Schristos for (c = cmdtab; c->c_name != NULL; c++) {
31243c97412Schristos if (wordlen > strlen(c->c_name))
31343c97412Schristos continue;
31443c97412Schristos if (strncmp(word, c->c_name, wordlen) == 0)
3158207b28aSchristos mail_sl_add(words, __UNCONST(c->c_name));
31643c97412Schristos }
31743c97412Schristos
3188207b28aSchristos rv = complete_ambiguous(el, word, dolist, words);
31943c97412Schristos if (rv == CC_REFRESH) {
32043c97412Schristos if (el_insertstr(el, " ") == -1)
32143c97412Schristos rv = CC_ERROR;
32243c97412Schristos }
32343c97412Schristos sl_free(words, 0);
324f3098750Schristos return rv;
32543c97412Schristos }
32643c97412Schristos
32743c97412Schristos /*
32843c97412Schristos * Complete a local filename.
32943c97412Schristos */
33043c97412Schristos static unsigned char
complete_filename(EditLine * el,char * word,int dolist)3318207b28aSchristos complete_filename(EditLine *el, char *word, int dolist)
33243c97412Schristos {
33343c97412Schristos StringList *words;
33443c97412Schristos char dir[MAXPATHLEN];
335ddf32695Schristos char *fname, *mf;
33643c97412Schristos DIR *dd;
33743c97412Schristos struct dirent *dp;
33843c97412Schristos unsigned char rv;
33943c97412Schristos size_t len;
34043c97412Schristos
34143c97412Schristos if ((fname = strrchr(word, '/')) == NULL) {
3429ee086efSchristos if (word[0] == '+' && (mf = value(ENAME_FOLDER)) != NULL) {
343ddf32695Schristos if (mf[0] == '/') {
344ddf32695Schristos (void)estrlcpy(dir, mf, sizeof(dir));
345ddf32695Schristos } else {
346ddf32695Schristos dir[0] = '~';
347ddf32695Schristos dir[1] = '/';
348ddf32695Schristos (void)estrlcpy(dir + 2, mf, sizeof(dir) - 2);
349ddf32695Schristos }
350ddf32695Schristos fname = word + 1;
351ddf32695Schristos } else {
35243c97412Schristos dir[0] = '.';
35343c97412Schristos dir[1] = '\0';
35443c97412Schristos fname = word;
355ddf32695Schristos }
35643c97412Schristos } else {
35743c97412Schristos if (fname == word) {
35843c97412Schristos dir[0] = '/';
35943c97412Schristos dir[1] = '\0';
3608207b28aSchristos } else {
3618207b28aSchristos len = fname - word + 1;
3628207b28aSchristos (void)estrlcpy(dir, word, sizeof(dir));
3638207b28aSchristos dir[len] = '\0';
3648207b28aSchristos }
36543c97412Schristos fname++;
36643c97412Schristos }
36743c97412Schristos if (dir[0] == '~') {
36843c97412Schristos char *p;
36943c97412Schristos
37043c97412Schristos if ((p = globulize(dir)) == NULL)
371f3098750Schristos return CC_ERROR;
3728207b28aSchristos (void)estrlcpy(dir, p, sizeof(dir));
37343c97412Schristos free(p);
37443c97412Schristos }
37543c97412Schristos
37643c97412Schristos if ((dd = opendir(dir)) == NULL)
377f3098750Schristos return CC_ERROR;
37843c97412Schristos
3798207b28aSchristos words = mail_sl_init();
38043c97412Schristos len = strlen(fname);
38143c97412Schristos
38243c97412Schristos for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
38343c97412Schristos if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
38443c97412Schristos continue;
38543c97412Schristos
38643c97412Schristos #if defined(DIRENT_MISSING_D_NAMLEN)
38743c97412Schristos if (len > strlen(dp->d_name))
38843c97412Schristos continue;
38943c97412Schristos #else
39043c97412Schristos if (len > dp->d_namlen)
39143c97412Schristos continue;
39243c97412Schristos #endif
39343c97412Schristos if (strncmp(fname, dp->d_name, len) == 0) {
39443c97412Schristos char *tcp;
39543c97412Schristos
3968207b28aSchristos tcp = estrdup(dp->d_name);
3978207b28aSchristos mail_sl_add(words, tcp);
39843c97412Schristos }
39943c97412Schristos }
4008207b28aSchristos (void)closedir(dd);
40143c97412Schristos
4028207b28aSchristos rv = complete_ambiguous(el, fname, dolist, words);
40343c97412Schristos if (rv == CC_REFRESH) {
40443c97412Schristos struct stat sb;
40543c97412Schristos char path[MAXPATHLEN];
40643c97412Schristos
4078207b28aSchristos (void)estrlcpy(path, dir, sizeof(path));
4088207b28aSchristos (void)estrlcat(path, "/", sizeof(path));
4098207b28aSchristos (void)estrlcat(path, words->sl_str[0], sizeof(path));
41043c97412Schristos
41143c97412Schristos if (stat(path, &sb) >= 0) {
41243c97412Schristos char suffix[2] = " ";
41343c97412Schristos
41443c97412Schristos if (S_ISDIR(sb.st_mode))
41543c97412Schristos suffix[0] = '/';
41643c97412Schristos if (el_insertstr(el, suffix) == -1)
41743c97412Schristos rv = CC_ERROR;
41843c97412Schristos }
41943c97412Schristos }
42043c97412Schristos sl_free(words, 1);
421f3098750Schristos return rv;
42243c97412Schristos }
42343c97412Schristos
42443c97412Schristos static int
find_execs(char * word,char * path,StringList * list)42543c97412Schristos find_execs(char *word, char *path, StringList *list)
42643c97412Schristos {
42743c97412Schristos char *sep;
42843c97412Schristos char *dir=path;
42943c97412Schristos DIR *dd;
43043c97412Schristos struct dirent *dp;
4318207b28aSchristos size_t len = strlen(word);
43243c97412Schristos uid_t uid = getuid();
43343c97412Schristos gid_t gid = getgid();
43443c97412Schristos
43543c97412Schristos for (sep = dir; sep; dir = sep + 1) {
43643c97412Schristos if ((sep=strchr(dir, ':')) != NULL) {
43743c97412Schristos *sep=0;
43843c97412Schristos }
43943c97412Schristos
44043c97412Schristos if ((dd = opendir(dir)) == NULL) {
44143c97412Schristos perror("dir");
44243c97412Schristos return -1;
44343c97412Schristos }
44443c97412Schristos
44543c97412Schristos for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
44643c97412Schristos
44743c97412Schristos if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
44843c97412Schristos continue;
44943c97412Schristos
45043c97412Schristos #if defined(DIRENT_MISSING_D_NAMLEN)
45143c97412Schristos if (len > strlen(dp->d_name))
45243c97412Schristos continue;
45343c97412Schristos #else
45443c97412Schristos if (len > dp->d_namlen)
45543c97412Schristos continue;
45643c97412Schristos #endif
45743c97412Schristos
45843c97412Schristos if (strncmp(word, dp->d_name, len) == 0) {
45943c97412Schristos struct stat sb;
46043c97412Schristos char pathname[ MAXPATHLEN ];
46143c97412Schristos unsigned mask;
46243c97412Schristos
4638207b28aSchristos (void)snprintf(pathname, sizeof(pathname),
4648207b28aSchristos "%s/%s", dir, dp->d_name);
46543c97412Schristos if (stat(pathname, &sb) != 0) {
46643c97412Schristos perror(pathname);
46743c97412Schristos continue;
46843c97412Schristos }
46943c97412Schristos
47043c97412Schristos mask = 0001;
47143c97412Schristos if (sb.st_uid == uid) mask |= 0100;
47243c97412Schristos if (sb.st_gid == gid) mask |= 0010;
47343c97412Schristos
47443c97412Schristos if ((sb.st_mode & mask) != 0) {
47543c97412Schristos char *tcp;
4768207b28aSchristos tcp = estrdup(dp->d_name);
4778207b28aSchristos mail_sl_add(list, tcp);
47843c97412Schristos }
47943c97412Schristos }
48043c97412Schristos
48143c97412Schristos }
48243c97412Schristos
4838207b28aSchristos (void)closedir(dd);
48443c97412Schristos }
48543c97412Schristos
48643c97412Schristos return 0;
48743c97412Schristos }
48843c97412Schristos
48943c97412Schristos
49043c97412Schristos /*
49143c97412Schristos * Complete a local executable
49243c97412Schristos */
49343c97412Schristos static unsigned char
complete_executable(EditLine * el,char * word,int dolist)4948207b28aSchristos complete_executable(EditLine *el, char *word, int dolist)
49543c97412Schristos {
49643c97412Schristos StringList *words;
49743c97412Schristos char dir[ MAXPATHLEN ];
49843c97412Schristos char *fname;
49943c97412Schristos unsigned char rv;
5008207b28aSchristos size_t len;
50143c97412Schristos int error;
50243c97412Schristos
50343c97412Schristos if ((fname = strrchr(word, '/')) == NULL) {
50443c97412Schristos dir[0] = '\0'; /* walk the path */
50543c97412Schristos fname = word;
50643c97412Schristos } else {
50743c97412Schristos if (fname == word) {
50843c97412Schristos dir[0] = '/';
50943c97412Schristos dir[1] = '\0';
51043c97412Schristos } else {
5118207b28aSchristos len = fname - word;
5128207b28aSchristos (void)strncpy(dir, word, len);
51343c97412Schristos dir[fname - word] = '\0';
51443c97412Schristos }
51543c97412Schristos fname++;
51643c97412Schristos }
51743c97412Schristos
51843c97412Schristos words = sl_init();
51943c97412Schristos
52043c97412Schristos if (*dir == '\0') { /* walk path */
52143c97412Schristos char *env;
52243c97412Schristos char *path;
52343c97412Schristos env = getenv("PATH");
52443c97412Schristos len = strlen(env);
52543c97412Schristos path = salloc(len + 1);
5268207b28aSchristos (void)strcpy(path, env);
52743c97412Schristos error = find_execs(word, path, words);
52843c97412Schristos }
52943c97412Schristos else { /* check specified dir only */
53043c97412Schristos error = find_execs(word, dir, words);
53143c97412Schristos }
53243c97412Schristos if (error != 0)
53343c97412Schristos return CC_ERROR;
53443c97412Schristos
5358207b28aSchristos rv = complete_ambiguous(el, fname, dolist, words);
536f3098750Schristos if (rv == CC_REFRESH) {
53743c97412Schristos if (el_insertstr(el, " ") == -1)
53843c97412Schristos rv = CC_ERROR;
539f3098750Schristos }
54043c97412Schristos sl_free(words, 1);
54143c97412Schristos
542f3098750Schristos return rv;
54343c97412Schristos }
54443c97412Schristos
54543c97412Schristos
546f3098750Schristos static unsigned char
complete_set(EditLine * el,char * word,int dolist)547f3098750Schristos complete_set(EditLine *el, char *word, int dolist)
54843c97412Schristos {
54943c97412Schristos struct var *vp;
550f3098750Schristos const char **ap;
551f3098750Schristos const char **p;
55243c97412Schristos int h;
55343c97412Schristos int s;
5548207b28aSchristos size_t len = strlen(word);
55543c97412Schristos StringList *words;
55643c97412Schristos unsigned char rv;
55743c97412Schristos
55843c97412Schristos words = sl_init();
55943c97412Schristos
56043c97412Schristos /* allocate space for variables table */
561798fbc60Schristos s = 1;
562798fbc60Schristos for (h = 0; h < HSHSIZE; h++)
56343c97412Schristos for (vp = variables[h]; vp != NULL; vp = vp->v_link)
56443c97412Schristos s++;
5652283d346Schristos ap = salloc(s * sizeof(*ap));
56643c97412Schristos
567798fbc60Schristos /* save the pointers */
56843c97412Schristos for (h = 0, p = ap; h < HSHSIZE; h++)
56943c97412Schristos for (vp = variables[h]; vp != NULL; vp = vp->v_link)
57043c97412Schristos *p++ = vp->v_name;
57143c97412Schristos *p = NULL;
57243c97412Schristos sort(ap);
573798fbc60Schristos for (p = ap; *p != NULL; p++)
574798fbc60Schristos if (len == 0 || strncmp(*p, word, len) == 0)
575798fbc60Schristos mail_sl_add(words, estrdup(*p));
57643c97412Schristos
5778207b28aSchristos rv = complete_ambiguous(el, word, dolist, words);
57843c97412Schristos
57943c97412Schristos sl_free(words, 1);
58043c97412Schristos
581f3098750Schristos return rv;
58243c97412Schristos }
58343c97412Schristos
58443c97412Schristos
58543c97412Schristos static unsigned char
complete_alias(EditLine * el,char * word,int dolist)5868207b28aSchristos complete_alias(EditLine *el, char *word, int dolist)
58743c97412Schristos {
58843c97412Schristos struct grouphead *gh;
589f3098750Schristos const char **ap;
590f3098750Schristos const char **p;
59143c97412Schristos int h;
59243c97412Schristos int s;
5938207b28aSchristos size_t len = strlen(word);
59443c97412Schristos StringList *words;
59543c97412Schristos unsigned char rv;
59643c97412Schristos
59743c97412Schristos words = sl_init();
59843c97412Schristos
59943c97412Schristos /* allocate space for alias table */
600798fbc60Schristos s = 1;
601798fbc60Schristos for (h = 0; h < HSHSIZE; h++)
60243c97412Schristos for (gh = groups[h]; gh != NULL; gh = gh->g_link)
60343c97412Schristos s++;
604f3098750Schristos ap = salloc(s * sizeof(*ap));
60543c97412Schristos
60643c97412Schristos /* save pointers */
607798fbc60Schristos p = ap;
608798fbc60Schristos for (h = 0; h < HSHSIZE; h++)
60943c97412Schristos for (gh = groups[h]; gh != NULL; gh = gh->g_link)
61043c97412Schristos *p++ = gh->g_name;
611f3098750Schristos
61243c97412Schristos *p = NULL;
613f3098750Schristos
61443c97412Schristos sort(ap);
615798fbc60Schristos for (p = ap; *p != NULL; p++)
616798fbc60Schristos if (len == 0 || strncmp(*p, word, len) == 0)
617798fbc60Schristos mail_sl_add(words, estrdup(*p));
61843c97412Schristos
6198207b28aSchristos rv = complete_ambiguous(el, word, dolist, words);
620f3098750Schristos if (rv == CC_REFRESH) {
62143c97412Schristos if (el_insertstr(el, " ") == -1)
62243c97412Schristos rv = CC_ERROR;
623f3098750Schristos }
62443c97412Schristos sl_free(words, 1);
625f3098750Schristos return rv;
62643c97412Schristos }
62743c97412Schristos
62843c97412Schristos
629f3098750Schristos static unsigned char
complete_smopts(EditLine * el,char * word,int dolist)630f3098750Schristos complete_smopts(EditLine *el, char *word, int dolist)
631798fbc60Schristos {
632798fbc60Schristos struct grouphead *gh;
633798fbc60Schristos struct smopts_s *sp;
634f3098750Schristos const char **ap;
635f3098750Schristos const char **p;
636798fbc60Schristos int h;
637798fbc60Schristos int s1;
638798fbc60Schristos int s2;
639798fbc60Schristos size_t len;
640798fbc60Schristos StringList *words;
641798fbc60Schristos unsigned char rv;
642798fbc60Schristos
643798fbc60Schristos len = strlen(word);
644798fbc60Schristos words = sl_init();
645798fbc60Schristos
646798fbc60Schristos /* count the entries in the smoptstbl and groups (alias) tables */
647798fbc60Schristos s1 = 1;
648798fbc60Schristos s2 = 1;
649798fbc60Schristos for (h = 0; h < HSHSIZE; h++) {
650798fbc60Schristos for (sp = smoptstbl[h]; sp != NULL; sp = sp->s_link)
651798fbc60Schristos s1++;
652798fbc60Schristos for (gh = groups[h]; gh != NULL; gh = gh->g_link)
653798fbc60Schristos s2++;
654798fbc60Schristos }
655798fbc60Schristos
656798fbc60Schristos /* allocate sufficient space for the pointers */
6572283d346Schristos ap = salloc(MAX(s1, s2) * sizeof(*ap));
658798fbc60Schristos
659798fbc60Schristos /*
660798fbc60Schristos * First do the smoptstbl pointers. (case _insensitive_)
661798fbc60Schristos */
662798fbc60Schristos p = ap;
663798fbc60Schristos for (h = 0; h < HSHSIZE; h++)
664798fbc60Schristos for (sp = smoptstbl[h]; sp != NULL; sp = sp->s_link)
665798fbc60Schristos *p++ = sp->s_name;
666798fbc60Schristos *p = NULL;
667798fbc60Schristos sort(ap);
668798fbc60Schristos for (p = ap; *p != NULL; p++)
669798fbc60Schristos if (len == 0 || strncasecmp(*p, word, len) == 0)
670798fbc60Schristos mail_sl_add(words, estrdup(*p));
671798fbc60Schristos
672798fbc60Schristos /*
673798fbc60Schristos * Now do the groups (alias) pointers. (case sensitive)
674798fbc60Schristos */
675798fbc60Schristos p = ap;
676798fbc60Schristos for (h = 0; h < HSHSIZE; h++)
677798fbc60Schristos for (gh = groups[h]; gh != NULL; gh = gh->g_link)
678798fbc60Schristos *p++ = gh->g_name;
679798fbc60Schristos *p = NULL;
680798fbc60Schristos sort(ap);
681798fbc60Schristos for (p = ap; *p != NULL; p++)
682798fbc60Schristos if (len == 0 || strncmp(*p, word, len) == 0)
683798fbc60Schristos mail_sl_add(words, estrdup(*p));
684798fbc60Schristos
685798fbc60Schristos rv = complete_ambiguous(el, word, dolist, words);
686798fbc60Schristos
687798fbc60Schristos sl_free(words, 1);
688798fbc60Schristos
689f3098750Schristos return rv;
690798fbc60Schristos }
691798fbc60Schristos
69243c97412Schristos
693f3098750Schristos #ifdef THREAD_SUPPORT
694f3098750Schristos static unsigned char
complete_thread_key(EditLine * el,char * word,int dolist)695f3098750Schristos complete_thread_key(EditLine *el, char *word, int dolist)
69643c97412Schristos {
697f3098750Schristos const char **ap;
698f3098750Schristos const char **p;
699f3098750Schristos const char *name;
700f3098750Schristos size_t len;
701f3098750Schristos StringList *words;
702f3098750Schristos unsigned char rv;
703f3098750Schristos int cnt;
704f3098750Schristos const void *cookie;
705f3098750Schristos
706f3098750Schristos len = strlen(word);
707f3098750Schristos words = sl_init();
708f3098750Schristos
709f3098750Schristos /* count the entries in the table */
710f3098750Schristos /* XXX - have a function return this rather than counting? */
711f3098750Schristos cnt = 1; /* count the NULL terminator */
712f3098750Schristos cookie = NULL;
713f3098750Schristos while (thread_next_key_name(&cookie) != NULL)
714f3098750Schristos cnt++;
715f3098750Schristos
716f3098750Schristos /* allocate sufficient space for the pointers */
7172283d346Schristos ap = salloc(cnt * sizeof(*ap));
718f3098750Schristos
719f3098750Schristos /* load the array */
720f3098750Schristos p = ap;
721f3098750Schristos cookie = NULL;
722f3098750Schristos while ((name = thread_next_key_name(&cookie)) != NULL)
723f3098750Schristos *p++ = name;
724f3098750Schristos *p = NULL;
725f3098750Schristos sort(ap);
726f3098750Schristos for (p = ap; *p != NULL; p++)
727f3098750Schristos if (len == 0 || strncmp(*p, word, len) == 0)
728f3098750Schristos mail_sl_add(words, estrdup(*p));
729f3098750Schristos
730f3098750Schristos rv = complete_ambiguous(el, word, dolist, words);
731f3098750Schristos
732f3098750Schristos sl_free(words, 1);
733f3098750Schristos
734f3098750Schristos return rv;
73543c97412Schristos }
736f3098750Schristos #endif /* THREAD_SUPPORT */
73743c97412Schristos
7388207b28aSchristos /* from /usr/src/usr.bin/ftp/main.c(1.101) - end */
73943c97412Schristos /************************************************************************/
74043c97412Schristos
7418207b28aSchristos /* Some people like to bind file completion to CTRL-D. In emacs mode,
7428207b28aSchristos * CTRL-D is also used to delete the current character, we have to
7438207b28aSchristos * special case this situation.
7448207b28aSchristos */
7458207b28aSchristos #define EMACS_CTRL_D_BINDING_HACK
7468207b28aSchristos
7478207b28aSchristos #ifdef EMACS_CTRL_D_BINDING_HACK
7488207b28aSchristos static int
is_emacs_mode(EditLine * el)7498207b28aSchristos is_emacs_mode(EditLine *el)
7508207b28aSchristos {
7518207b28aSchristos char *mode;
752ca13337dSchristos
7538207b28aSchristos if (el_get(el, EL_EDITOR, &mode) == -1)
7548207b28aSchristos return 0;
7558207b28aSchristos return equal(mode, "emacs");
7568207b28aSchristos }
7578207b28aSchristos
7588207b28aSchristos static int
emacs_ctrl_d(EditLine * el,const LineInfo * lf,int ch)759f3098750Schristos emacs_ctrl_d(EditLine *el, const LineInfo *lf, int ch)
7608207b28aSchristos {
7618207b28aSchristos static char delunder[3] = { CTRL('f'), CTRL('h'), '\0' };
762ca13337dSchristos
7638207b28aSchristos if (ch == CTRL('d') && is_emacs_mode(el)) { /* CTRL-D is special */
764f3098750Schristos if (lf->buffer == lf->lastchar)
765f3098750Schristos return CC_EOF;
766f3098750Schristos if (lf->cursor != lf->lastchar) { /* delete without using ^D */
7678207b28aSchristos el_push(el, delunder); /* ^F^H */
768f3098750Schristos return CC_NORM;
7698207b28aSchristos }
7708207b28aSchristos }
7718207b28aSchristos return -1;
7728207b28aSchristos }
7738207b28aSchristos #endif /* EMACS_CTRL_D_BINDING_HACK */
77443c97412Schristos
77543c97412Schristos /*
776f3098750Schristos * Check if this is the second request made for this line indicating
777f3098750Schristos * the need to list all the completion possibilities.
778f3098750Schristos */
779f3098750Schristos static int
get_dolist(const LineInfo * lf)780f3098750Schristos get_dolist(const LineInfo *lf)
781f3098750Schristos {
782f3098750Schristos static char last_line[LINESIZE];
783f3098750Schristos static char *last_cursor_pos;
784f3098750Schristos char *cursor_pos;
785f3098750Schristos int dolist;
786f3098750Schristos size_t len;
787f3098750Schristos
788f3098750Schristos len = lf->lastchar - lf->buffer;
789f3098750Schristos if (len >= sizeof(last_line) - 1)
790f3098750Schristos return -1;
791f3098750Schristos
792f3098750Schristos cursor_pos = last_line + (lf->cursor - lf->buffer);
793f3098750Schristos dolist =
794f3098750Schristos cursor_pos == last_cursor_pos &&
795f3098750Schristos strncmp(last_line, lf->buffer, len) == 0;
796f3098750Schristos
797f3098750Schristos (void)strlcpy(last_line, lf->buffer, len + 1);
798f3098750Schristos last_cursor_pos = cursor_pos;
799f3098750Schristos
800f3098750Schristos return dolist;
801f3098750Schristos }
802f3098750Schristos
803f3098750Schristos /*
804f3098750Schristos * Take the full line (lf) including the command and split it into a
805f3098750Schristos * sub-line (returned) and a completion context (cmplarray).
806f3098750Schristos */
807f3098750Schristos static LineInfo *
split_line(const char ** cmplarray,const LineInfo * lf)808f3098750Schristos split_line(const char **cmplarray, const LineInfo *lf)
809f3098750Schristos {
810f3098750Schristos static LineInfo li;
811f3098750Schristos const struct cmd *c;
812f3098750Schristos char *cmdname;
813f3098750Schristos char line[LINESIZE];
814f3098750Schristos char *cp;
815f3098750Schristos size_t len;
816f3098750Schristos
817f3098750Schristos len = lf->cursor - lf->buffer;
818f3098750Schristos if (len + 1 > sizeof(line))
819f3098750Schristos return NULL;
820f3098750Schristos
821f3098750Schristos (void)strlcpy(line, lf->buffer, len + 1);
822f3098750Schristos
823f3098750Schristos li.cursor = line + len;
824f3098750Schristos li.lastchar = line + len;
825f3098750Schristos
826d727506fSchristos cp = skip_WSP(line);
827f3098750Schristos cmdname = get_cmdname(cp);
828f3098750Schristos cp += strlen(cmdname);
829f3098750Schristos
830f3098750Schristos if (cp == li.cursor) {
831f3098750Schristos *cmplarray = "c";
832f3098750Schristos li.buffer = cmdname;
833f3098750Schristos return &li;
834f3098750Schristos }
835f3098750Schristos
836f3098750Schristos c = lex(cmdname);
837f3098750Schristos if (c == NULL)
838f3098750Schristos return NULL;
839f3098750Schristos
840f3098750Schristos *cmplarray = c->c_complete;
841f3098750Schristos if (c->c_pipe) {
842f3098750Schristos char *cp2;
843f3098750Schristos if ((cp2 = shellpr(cp)) != NULL) {
844f3098750Schristos cp = cp2;
845d727506fSchristos # define XX(a) ((a) + ((a)[1] == '>' ? 2 : 1))
846f3098750Schristos while ((cp2 = shellpr(XX(cp))) != NULL)
847f3098750Schristos cp = cp2;
848f3098750Schristos
849f3098750Schristos if (*cp == '|') {
850f3098750Schristos *cmplarray = "xF";
851d727506fSchristos cp = skip_WSP(cp + 1);
852f3098750Schristos }
853f3098750Schristos else {
854f3098750Schristos assert(*cp == '>');
855d727506fSchristos cp = skip_WSP(XX(cp));
856f3098750Schristos *cmplarray = "f";
857f3098750Schristos }
858f3098750Schristos # undef XX
859f3098750Schristos }
860f3098750Schristos }
861f3098750Schristos li.buffer = cp;
862f3098750Schristos return &li;
863f3098750Schristos }
864f3098750Schristos
865f3098750Schristos /*
866f3098750Schristos * Split a sub-line and a completion context into a word and a
867f3098750Schristos * completion type. Use the editline tokenizer to handle the quoting
868f3098750Schristos * and splitting.
869f3098750Schristos */
870f3098750Schristos static char *
split_word(int * cmpltype,const char * cmplarray,LineInfo * li)871f3098750Schristos split_word(int *cmpltype, const char *cmplarray, LineInfo *li)
872f3098750Schristos {
873f3098750Schristos static Tokenizer *t = NULL;
874f3098750Schristos const char **argv;
875f3098750Schristos char *word;
876f3098750Schristos int argc;
877f3098750Schristos int cursorc;
878f3098750Schristos int cursoro;
879f3098750Schristos int arraylen;
880f3098750Schristos
881f3098750Schristos if (t != NULL)
882f3098750Schristos tok_reset(t);
883f3098750Schristos else {
884f3098750Schristos if ((t = tok_init(NULL)) == NULL)
885f3098750Schristos err(EXIT_FAILURE, "tok_init");
886f3098750Schristos }
887f3098750Schristos if (tok_line(t, li, &argc, &argv, &cursorc, &cursoro) == -1)
888f3098750Schristos err(EXIT_FAILURE, "tok_line");
889f3098750Schristos
890f3098750Schristos if (cursorc >= argc)
891f3098750Schristos word = __UNCONST("");
892f3098750Schristos else {
893f3098750Schristos word = salloc((size_t)cursoro + 1);
894f3098750Schristos (void)strlcpy(word, argv[cursorc], (size_t)cursoro + 1);
895f3098750Schristos }
896f3098750Schristos
897f3098750Schristos /* check for 'continuation' completes (which are uppercase) */
898ca13337dSchristos arraylen = (int)strlen(cmplarray);
899f3098750Schristos if (cursorc >= arraylen &&
900f3098750Schristos arraylen > 0 &&
901f3098750Schristos isupper((unsigned char)cmplarray[arraylen - 1]))
902f3098750Schristos cursorc = arraylen - 1;
903f3098750Schristos
904f3098750Schristos if (cursorc >= arraylen)
905f3098750Schristos return NULL;
906f3098750Schristos
907f3098750Schristos *cmpltype = cmplarray[cursorc];
908f3098750Schristos return word;
909f3098750Schristos }
910f3098750Schristos
911f3098750Schristos /*
912f3098750Schristos * A generic complete routine for the mail command line.
91343c97412Schristos */
91443c97412Schristos static unsigned char
mail_complete(EditLine * el,int ch)9158207b28aSchristos mail_complete(EditLine *el, int ch)
91643c97412Schristos {
917f3098750Schristos LineInfo *li;
91843c97412Schristos const LineInfo *lf;
919f3098750Schristos const char *cmplarray;
920f3098750Schristos int dolist;
921f3098750Schristos int cmpltype;
922f3098750Schristos char *word;
92343c97412Schristos
92443c97412Schristos lf = el_line(el);
9258207b28aSchristos
9268207b28aSchristos #ifdef EMACS_CTRL_D_BINDING_HACK
9278207b28aSchristos {
9288207b28aSchristos int cc_ret;
929f3098750Schristos if ((cc_ret = emacs_ctrl_d(el, lf, ch)) != -1)
9308207b28aSchristos return cc_ret;
93143c97412Schristos }
9328207b28aSchristos #endif /* EMACS_CTRL_D_BINDING_HACK */
9338207b28aSchristos
934f3098750Schristos if ((dolist = get_dolist(lf)) == -1)
935f3098750Schristos return CC_ERROR;
9368207b28aSchristos
937f3098750Schristos if ((li = split_line(&cmplarray, lf)) == NULL)
938f3098750Schristos return CC_ERROR;
93943c97412Schristos
940f3098750Schristos if ((word = split_word(&cmpltype, cmplarray, li)) == NULL)
941f3098750Schristos return CC_ERROR;
94243c97412Schristos
94343c97412Schristos switch (cmpltype) {
94443c97412Schristos case 'a': /* alias complete */
94543c97412Schristos case 'A':
946f3098750Schristos return complete_alias(el, word, dolist);
94743c97412Schristos
94843c97412Schristos case 'c': /* command complete */
94943c97412Schristos case 'C':
950f3098750Schristos return complete_command(el, word, dolist);
95143c97412Schristos
95243c97412Schristos case 'f': /* filename complete */
95343c97412Schristos case 'F':
954f3098750Schristos return complete_filename(el, word, dolist);
955f3098750Schristos
956798fbc60Schristos case 'm':
957798fbc60Schristos case 'M':
958f3098750Schristos return complete_smopts(el, word, dolist);
959f3098750Schristos
96043c97412Schristos case 'n': /* no complete */
96143c97412Schristos case 'N': /* no complete */
962f3098750Schristos return CC_ERROR;
96343c97412Schristos
96443c97412Schristos case 's':
96543c97412Schristos case 'S':
966f3098750Schristos return complete_set(el, word, dolist);
967f3098750Schristos #ifdef THREAD_SUPPORT
968f3098750Schristos case 't':
969f3098750Schristos case 'T':
970f3098750Schristos return complete_thread_key(el, word, dolist);
971f3098750Schristos #endif
97243c97412Schristos case 'x': /* executable complete */
97343c97412Schristos case 'X':
974f3098750Schristos return complete_executable(el, word, dolist);
97543c97412Schristos
97643c97412Schristos default:
977f3098750Schristos warnx("unknown complete type `%c'", cmpltype);
978f3098750Schristos #if 0
979f3098750Schristos assert(/*CONSTCOND*/0);
980f3098750Schristos #endif
981f3098750Schristos return CC_ERROR;
98243c97412Schristos }
98343c97412Schristos /* NOTREACHED */
98443c97412Schristos }
98543c97412Schristos
98643c97412Schristos
9878207b28aSchristos /*
988f3098750Schristos * A generic file completion routine.
9898207b28aSchristos */
9908207b28aSchristos static unsigned char
file_complete(EditLine * el,int ch)9918207b28aSchristos file_complete(EditLine *el, int ch)
9928207b28aSchristos {
9938207b28aSchristos static char word[LINESIZE];
9948207b28aSchristos const LineInfo *lf;
995f3098750Schristos size_t word_len;
996f3098750Schristos int dolist;
9978207b28aSchristos
9988207b28aSchristos lf = el_line(el);
9998207b28aSchristos
10008207b28aSchristos #ifdef EMACS_CTRL_D_BINDING_HACK
10018207b28aSchristos {
10028207b28aSchristos int cc_ret;
1003f3098750Schristos if ((cc_ret = emacs_ctrl_d(el, lf, ch)) != -1)
10048207b28aSchristos return cc_ret;
10058207b28aSchristos }
10068207b28aSchristos #endif /* EMACS_CTRL_D_BINDING_HACK */
10078207b28aSchristos
10088207b28aSchristos word_len = lf->cursor - lf->buffer;
1009f3098750Schristos if (word_len + 1 > sizeof(word))
1010f3098750Schristos return CC_ERROR;
1011f3098750Schristos
10128207b28aSchristos (void)strlcpy(word, lf->buffer, word_len + 1); /* do not use estrlcpy here! */
1013f3098750Schristos
1014f3098750Schristos if ((dolist = get_dolist(lf)) == -1)
1015f3098750Schristos return CC_ERROR;
1016f3098750Schristos
1017f3098750Schristos return complete_filename(el, word, dolist);
10188207b28aSchristos }
10198207b28aSchristos
10208207b28aSchristos
10218207b28aSchristos #ifdef MIME_SUPPORT
10228207b28aSchristos /*
1023f3098750Schristos * Complete mime_transfer_encoding type.
10248207b28aSchristos */
10258207b28aSchristos static unsigned char
mime_enc_complete(EditLine * el,int ch)10268207b28aSchristos mime_enc_complete(EditLine *el, int ch)
10278207b28aSchristos {
10288207b28aSchristos static char word[LINESIZE];
10298207b28aSchristos StringList *words;
10308207b28aSchristos unsigned char rv;
10318207b28aSchristos const LineInfo *lf;
1032f3098750Schristos size_t word_len;
1033f3098750Schristos int dolist;
10348207b28aSchristos
10358207b28aSchristos lf = el_line(el);
10368207b28aSchristos
10378207b28aSchristos #ifdef EMACS_CTRL_D_BINDING_HACK
10388207b28aSchristos {
10398207b28aSchristos int cc_ret;
1040f3098750Schristos if ((cc_ret = emacs_ctrl_d(el, lf, ch)) != -1)
10418207b28aSchristos return cc_ret;
10428207b28aSchristos }
10438207b28aSchristos #endif /* EMACS_CTRL_D_BINDING_HACK */
10448207b28aSchristos
1045f3098750Schristos word_len = lf->cursor - lf->buffer;
1046f3098750Schristos if (word_len >= sizeof(word) - 1)
1047f3098750Schristos return CC_ERROR;
10488207b28aSchristos
10498207b28aSchristos words = mail_sl_init();
10508207b28aSchristos {
10518207b28aSchristos const char *ename;
10528207b28aSchristos const void *cookie;
10538207b28aSchristos cookie = NULL;
10548207b28aSchristos for (ename = mime_next_encoding_name(&cookie);
10558207b28aSchristos ename;
10568207b28aSchristos ename = mime_next_encoding_name(&cookie))
10578207b28aSchristos if (word_len == 0 ||
10588207b28aSchristos strncmp(lf->buffer, ename, word_len) == 0) {
10598207b28aSchristos char *cp;
10608207b28aSchristos cp = estrdup(ename);
10618207b28aSchristos mail_sl_add(words, cp);
10628207b28aSchristos }
10638207b28aSchristos }
1064f3098750Schristos (void)strlcpy(word, lf->buffer, word_len + 1);
10658207b28aSchristos
1066f3098750Schristos if ((dolist = get_dolist(lf)) == -1)
1067f3098750Schristos return CC_ERROR;
1068f3098750Schristos
1069f3098750Schristos rv = complete_ambiguous(el, word, dolist, words);
10708207b28aSchristos
10718207b28aSchristos sl_free(words, 1);
1072f3098750Schristos return rv;
10738207b28aSchristos }
10748207b28aSchristos #endif /* MIME_SUPPORT */
10758207b28aSchristos
1076f3098750Schristos
10778207b28aSchristos /*************************************************************************
10788207b28aSchristos * Our public interface to el_gets():
10798207b28aSchristos *
10808207b28aSchristos * init_editline()
1081dc965fb3Smsaitoh * Initializes of all editline and completion data structures.
10828207b28aSchristos *
10838207b28aSchristos * my_gets()
1084ca13337dSchristos * Displays prompt, calls el_gets() and deals with history.
1085ca13337dSchristos * Returns the next line of input as a NULL termnated string
1086ca13337dSchristos * without the trailing newline, or NULL if el_gets() sees is an
1087ca13337dSchristos * error or signal.
10888207b28aSchristos */
10898207b28aSchristos
10908207b28aSchristos static const char *el_prompt;
10918207b28aSchristos
10928207b28aSchristos /*ARGSUSED*/
10938207b28aSchristos static const char *
show_prompt(EditLine * e __unused)10948207b28aSchristos show_prompt(EditLine *e __unused)
10958207b28aSchristos {
10968207b28aSchristos return el_prompt;
10978207b28aSchristos }
109843c97412Schristos
1099ca13337dSchristos /*
1100ca13337dSchristos * Write the current INTR character to fp in a friendly form.
1101ca13337dSchristos */
1102ca13337dSchristos static void
echo_INTR(void * p)1103ca13337dSchristos echo_INTR(void *p)
1104ca13337dSchristos {
1105ca13337dSchristos struct termios ttybuf;
1106ca13337dSchristos char buf[5];
1107ca13337dSchristos FILE *fp;
1108ca13337dSchristos
1109ca13337dSchristos fp = p;
1110ca13337dSchristos if (tcgetattr(fileno(stdin), &ttybuf) == -1)
1111ca13337dSchristos warn("tcgetattr");
1112ca13337dSchristos else {
1113ca13337dSchristos (void)vis(buf, ttybuf.c_cc[VINTR], VIS_SAFE | VIS_NOSLASH, 0);
1114ca13337dSchristos (void)fprintf(fp, "%s", buf);
1115ca13337dSchristos (void)fflush(fp);
1116ca13337dSchristos }
1117ca13337dSchristos }
1118ca13337dSchristos
1119ca13337dSchristos static sig_t old_sigint;
1120ca13337dSchristos static void
comp_intr(int signo)1121ca13337dSchristos comp_intr(int signo)
1122ca13337dSchristos {
1123ca13337dSchristos
1124ca13337dSchristos echo_INTR(stdout);
1125ca13337dSchristos old_sigint(signo);
1126ca13337dSchristos }
1127ca13337dSchristos
1128f3098750Schristos PUBLIC char *
my_gets(el_mode_t * em,const char * prompt,char * string)11298207b28aSchristos my_gets(el_mode_t *em, const char *prompt, char *string)
113043c97412Schristos {
1131ca13337dSchristos static char line[LINE_MAX];
11328207b28aSchristos size_t len;
1133ca13337dSchristos int cnt;
113443c97412Schristos const char *buf;
113543c97412Schristos HistEvent ev;
1136ca13337dSchristos
1137ca13337dSchristos sig_check();
113843c97412Schristos
11398207b28aSchristos el_prompt = prompt;
114043c97412Schristos if (string)
11418207b28aSchristos el_push(em->el, string);
114243c97412Schristos
1143ca13337dSchristos /*
1144ca13337dSchristos * Let el_gets() deal with flow control. Also, make sure we
1145ca13337dSchristos * output a ^C when we get a SIGINT as el_gets() doesn't echo
1146ca13337dSchristos * one.
1147ca13337dSchristos */
1148ca13337dSchristos old_sigint = sig_signal(SIGINT, comp_intr);
11498207b28aSchristos buf = el_gets(em->el, &cnt);
1150ca13337dSchristos (void)sig_signal(SIGINT, old_sigint);
11518207b28aSchristos
1152ca13337dSchristos if (buf == NULL) {
1153ca13337dSchristos sig_check();
11548207b28aSchristos return NULL;
1155fbdddce8Schristos }
115643c97412Schristos
1157b8afdde7Schristos if (cnt > 0) {
1158f3098750Schristos if (buf[cnt - 1] == '\n')
11598207b28aSchristos cnt--; /* trash the trailing LF */
1160ca13337dSchristos
1161c172e3b9Slukem len = MIN(sizeof(line) - 1, (size_t)cnt);
11628207b28aSchristos (void)memcpy(line, buf, len);
1163b8afdde7Schristos }
116443c97412Schristos line[cnt] = '\0';
116543c97412Schristos
11668207b28aSchristos /* enter non-empty lines into history */
11678207b28aSchristos if (em->hist) {
11688207b28aSchristos const char *p;
1169ca13337dSchristos
1170d727506fSchristos p = skip_WSP(line);
11718207b28aSchristos if (*p && history(em->hist, &ev, H_ENTER, line) == 0)
11728207b28aSchristos (void)printf("Failed history entry: %s", line);
11738207b28aSchristos }
1174ca13337dSchristos sig_check();
117543c97412Schristos return line;
117643c97412Schristos }
117743c97412Schristos
11788207b28aSchristos static el_mode_t
init_el_mode(const char * el_editor,unsigned char (* completer)(EditLine *,int),struct name * keys,int history_size)11798207b28aSchristos init_el_mode(
11808207b28aSchristos const char *el_editor,
11818207b28aSchristos unsigned char (*completer)(EditLine *, int),
11828207b28aSchristos struct name *keys,
11838207b28aSchristos int history_size)
118443c97412Schristos {
118526114345Schristos FILE *nullfp;
11868207b28aSchristos el_mode_t em;
118726114345Schristos
11888207b28aSchristos (void)memset(&em, 0, sizeof(em));
11898207b28aSchristos
119026114345Schristos if ((nullfp = fopen(_PATH_DEVNULL, "w")) == NULL)
119171735a83Schristos err(EXIT_FAILURE, "Cannot open `%s'", _PATH_DEVNULL);
119226114345Schristos
119326114345Schristos if ((em.el = el_init(getprogname(), stdin, stdout, nullfp)) == NULL) {
1194b01c8fbcSchristos warn("el_init");
1195b01c8fbcSchristos return em;
1196b01c8fbcSchristos }
119726114345Schristos (void)fflush(nullfp);
119826114345Schristos (void)dup2(STDERR_FILENO, fileno(nullfp));
11998207b28aSchristos
12008207b28aSchristos (void)el_set(em.el, EL_PROMPT, show_prompt);
1201b01c8fbcSchristos (void)el_set(em.el, EL_SIGNAL, 1); /* editline handles the signals. */
12028207b28aSchristos
12038207b28aSchristos if (el_editor)
12048207b28aSchristos (void)el_set(em.el, EL_EDITOR, el_editor);
12058207b28aSchristos
12068207b28aSchristos if (completer) {
12078207b28aSchristos struct name *np;
12088207b28aSchristos (void)el_set(em.el, EL_ADDFN, "mail-complete",
12098207b28aSchristos "Context sensitive argument completion", completer);
12108207b28aSchristos for (np = keys; np; np = np->n_flink)
12118207b28aSchristos (void)el_set(em.el, EL_BIND, np->n_name,
12128207b28aSchristos "mail-complete", NULL);
121343c97412Schristos }
121443c97412Schristos
12158207b28aSchristos if (history_size) {
12168207b28aSchristos HistEvent ev;
1217b01c8fbcSchristos if ((em.hist = history_init()) == NULL) {
1218b01c8fbcSchristos warn("history_init");
1219b01c8fbcSchristos return em;
1220b01c8fbcSchristos }
1221b01c8fbcSchristos if (history(em.hist, &ev, H_SETSIZE, history_size) == -1)
12228207b28aSchristos (void)printf("history: %s\n", ev.str);
12238207b28aSchristos (void)el_set(em.el, EL_HIST, history, em.hist);
12248207b28aSchristos }
12258207b28aSchristos
12268207b28aSchristos (void)el_source(em.el, NULL); /* read ~/.editrc */
12278207b28aSchristos
12288207b28aSchristos return em;
12298207b28aSchristos }
12308207b28aSchristos
12318207b28aSchristos
12328207b28aSchristos struct el_modes_s elm = {
12338207b28aSchristos .command = { .el = NULL, .hist = NULL, },
12348207b28aSchristos .string = { .el = NULL, .hist = NULL, },
12358207b28aSchristos .filec = { .el = NULL, .hist = NULL, },
12368207b28aSchristos #ifdef MIME_SUPPORT
12378207b28aSchristos .mime_enc = { .el = NULL, .hist = NULL, },
12388207b28aSchristos #endif
12398207b28aSchristos };
12408207b28aSchristos
1241f3098750Schristos PUBLIC void
init_editline(void)12428207b28aSchristos init_editline(void)
124343c97412Schristos {
12448207b28aSchristos const char *mode;
12458207b28aSchristos int hist_size;
12468207b28aSchristos struct name *keys;
12478207b28aSchristos char *cp;
124843c97412Schristos
12498207b28aSchristos mode = value(ENAME_EL_EDITOR);
125043c97412Schristos
12518207b28aSchristos cp = value(ENAME_EL_HISTORY_SIZE);
12528207b28aSchristos hist_size = cp ? atoi(cp) : 0;
125343c97412Schristos
12548207b28aSchristos cp = value(ENAME_EL_COMPLETION_KEYS);
12558207b28aSchristos keys = cp && *cp ? lexpand(cp, 0) : NULL;
125643c97412Schristos
12578207b28aSchristos elm.command = init_el_mode(mode, mail_complete, keys, hist_size);
12588207b28aSchristos elm.filec = init_el_mode(mode, file_complete, keys, 0);
12598207b28aSchristos elm.string = init_el_mode(mode, NULL, NULL, 0);
12608207b28aSchristos #ifdef MIME_SUPPORT
12618207b28aSchristos elm.mime_enc = init_el_mode(mode, mime_enc_complete, keys, 0);
12628207b28aSchristos #endif
126343c97412Schristos return;
126443c97412Schristos }
126543c97412Schristos
12668207b28aSchristos #endif /* USE_EDITLINE */
1267