Annotation of src/usr.bin/ftp/complete.c, Revision 1.30
1.30 ! lukem 1: /* $NetBSD: complete.c,v 1.29 1999/09/24 14:28:14 lukem Exp $ */
1.1 lukem 2:
3: /*-
1.21 lukem 4: * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
1.1 lukem 5: * All rights reserved.
6: *
7: * This code is derived from software contributed to The NetBSD Foundation
8: * by Luke Mewburn.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by the NetBSD
21: * Foundation, Inc. and its contributors.
22: * 4. Neither the name of The NetBSD Foundation nor the names of its
23: * contributors may be used to endorse or promote products derived
24: * from this software without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1.10 lukem 29: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1.1 lukem 31: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36: * POSSIBILITY OF SUCH DAMAGE.
37: */
38:
1.9 lukem 39: #include <sys/cdefs.h>
1.1 lukem 40: #ifndef lint
1.30 ! lukem 41: __RCSID("$NetBSD: complete.c,v 1.29 1999/09/24 14:28:14 lukem Exp $");
1.1 lukem 42: #endif /* not lint */
43:
44: /*
45: * FTP user program - command and file completion routines
46: */
47:
1.13 lukem 48: #include <sys/stat.h>
1.25 christos 49: #include <signal.h>
1.13 lukem 50:
1.1 lukem 51: #include <ctype.h>
52: #include <err.h>
53: #include <dirent.h>
54: #include <stdio.h>
55: #include <stdlib.h>
56: #include <string.h>
57:
58: #include "ftp_var.h"
59:
1.24 cgd 60: #ifndef NO_EDITCOMPLETE
61:
1.9 lukem 62: static int comparstr __P((const void *, const void *));
63: static unsigned char complete_ambiguous __P((char *, int, StringList *));
64: static unsigned char complete_command __P((char *, int));
65: static unsigned char complete_local __P((char *, int));
66: static unsigned char complete_remote __P((char *, int));
67:
1.1 lukem 68: static int
69: comparstr(a, b)
70: const void *a, *b;
71: {
1.26 christos 72: return (strcmp(*(const char **)a, *(const char **)b));
1.1 lukem 73: }
74:
75: /*
76: * Determine if complete is ambiguous. If unique, insert.
77: * If no choices, error. If unambiguous prefix, insert that.
78: * Otherwise, list choices. words is assumed to be filtered
79: * to only contain possible choices.
80: * Args:
81: * word word which started the match
82: * list list by default
83: * words stringlist containing possible matches
1.13 lukem 84: * Returns a result as per el_set(EL_ADDFN, ...)
1.1 lukem 85: */
86: static unsigned char
87: complete_ambiguous(word, list, words)
88: char *word;
89: int list;
90: StringList *words;
91: {
1.3 lukem 92: char insertstr[MAXPATHLEN];
1.1 lukem 93: char *lastmatch;
1.11 lukem 94: int i, j;
95: size_t matchlen, wordlen;
1.1 lukem 96:
97: wordlen = strlen(word);
98: if (words->sl_cur == 0)
1.6 lukem 99: return (CC_ERROR); /* no choices available */
1.1 lukem 100:
101: if (words->sl_cur == 1) { /* only once choice available */
1.18 lukem 102: char *p = words->sl_str[0] + wordlen;
103: ftpvis(insertstr, sizeof(insertstr), p, strlen(p));
104: if (el_insertstr(el, insertstr) == -1)
1.6 lukem 105: return (CC_ERROR);
1.1 lukem 106: else
1.6 lukem 107: return (CC_REFRESH);
1.1 lukem 108: }
109:
110: if (!list) {
111: matchlen = 0;
112: lastmatch = words->sl_str[0];
113: matchlen = strlen(lastmatch);
114: for (i = 1 ; i < words->sl_cur ; i++) {
115: for (j = wordlen ; j < strlen(words->sl_str[i]); j++)
116: if (lastmatch[j] != words->sl_str[i][j])
117: break;
118: if (j < matchlen)
119: matchlen = j;
120: }
121: if (matchlen > wordlen) {
1.18 lukem 122: ftpvis(insertstr, sizeof(insertstr),
1.20 lukem 123: lastmatch + wordlen, matchlen - wordlen);
1.18 lukem 124: if (el_insertstr(el, insertstr) == -1)
1.6 lukem 125: return (CC_ERROR);
1.21 lukem 126: else
1.13 lukem 127: return (CC_REFRESH_BEEP);
1.1 lukem 128: }
129: }
130:
1.14 lukem 131: putc('\n', ttyout);
1.1 lukem 132: qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr);
133: list_vertical(words);
1.6 lukem 134: return (CC_REDISPLAY);
1.1 lukem 135: }
136:
137: /*
138: * Complete a command
139: */
140: static unsigned char
141: complete_command(word, list)
142: char *word;
143: int list;
144: {
145: struct cmd *c;
146: StringList *words;
1.11 lukem 147: size_t wordlen;
1.1 lukem 148: unsigned char rv;
149:
150: words = sl_init();
151: wordlen = strlen(word);
152:
153: for (c = cmdtab; c->c_name != NULL; c++) {
154: if (wordlen > strlen(c->c_name))
155: continue;
156: if (strncmp(word, c->c_name, wordlen) == 0)
157: sl_add(words, c->c_name);
158: }
159:
160: rv = complete_ambiguous(word, list, words);
1.13 lukem 161: if (rv == CC_REFRESH) {
162: if (el_insertstr(el, " ") == -1)
163: rv = CC_ERROR;
164: }
1.1 lukem 165: sl_free(words, 0);
1.6 lukem 166: return (rv);
1.1 lukem 167: }
168:
169: /*
170: * Complete a local file
171: */
172: static unsigned char
173: complete_local(word, list)
174: char *word;
175: int list;
176: {
177: StringList *words;
1.3 lukem 178: char dir[MAXPATHLEN];
1.1 lukem 179: char *file;
180: DIR *dd;
181: struct dirent *dp;
182: unsigned char rv;
1.12 christos 183: size_t len;
1.1 lukem 184:
185: if ((file = strrchr(word, '/')) == NULL) {
1.6 lukem 186: dir[0] = '.';
187: dir[1] = '\0';
1.1 lukem 188: file = word;
189: } else {
1.6 lukem 190: if (file == word) {
191: dir[0] = '/';
192: dir[1] = '\0';
193: } else {
194: (void)strncpy(dir, word, file - word);
1.1 lukem 195: dir[file - word] = '\0';
196: }
1.6 lukem 197: file++;
1.17 lukem 198: }
199: if (dir[0] == '~') {
200: char *p;
201:
202: p = dir;
203: if (!globulize(&p))
204: return (CC_ERROR);
205: if (p != dir) {
206: strncpy(dir, p, sizeof(dir));
207: dir[sizeof(dir)-1] = '\0';
208: free(p);
209: }
1.1 lukem 210: }
211:
212: if ((dd = opendir(dir)) == NULL)
1.6 lukem 213: return (CC_ERROR);
1.1 lukem 214:
215: words = sl_init();
216:
1.12 christos 217: len = strlen(file);
218:
1.1 lukem 219: for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
220: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
221: continue;
1.21 lukem 222:
1.30 ! lukem 223: #if defined(DIRENT_MISSING_D_NAMLEN)
1.23 christos 224: if (len > strlen(dp->d_name))
1.12 christos 225: continue;
226: #else
1.23 christos 227: if (len > dp->d_namlen)
1.1 lukem 228: continue;
1.12 christos 229: #endif
230: if (strncmp(file, dp->d_name, len) == 0) {
1.1 lukem 231: char *tcp;
232:
1.15 lukem 233: tcp = xstrdup(dp->d_name);
1.1 lukem 234: sl_add(words, tcp);
235: }
236: }
237: closedir(dd);
238:
239: rv = complete_ambiguous(file, list, words);
1.13 lukem 240: if (rv == CC_REFRESH) {
241: struct stat sb;
242: char path[MAXPATHLEN];
243:
1.27 lukem 244: strlcpy(path, dir, sizeof(path));
245: strlcat(path, "/", sizeof(path));
246: strlcat(path, words->sl_str[0], sizeof(path));
247:
1.13 lukem 248: if (stat(path, &sb) >= 0) {
249: char suffix[2] = " ";
250:
251: if (S_ISDIR(sb.st_mode))
252: suffix[0] = '/';
253: if (el_insertstr(el, suffix) == -1)
254: rv = CC_ERROR;
255: }
256: }
1.1 lukem 257: sl_free(words, 1);
1.6 lukem 258: return (rv);
1.1 lukem 259: }
260:
261: /*
262: * Complete a remote file
263: */
264: static unsigned char
265: complete_remote(word, list)
266: char *word;
267: int list;
268: {
269: static StringList *dirlist;
1.3 lukem 270: static char lastdir[MAXPATHLEN];
1.1 lukem 271: StringList *words;
1.3 lukem 272: char dir[MAXPATHLEN];
1.1 lukem 273: char *file, *cp;
1.3 lukem 274: int i;
1.1 lukem 275: unsigned char rv;
276:
1.16 lukem 277: char *dummyargv[] = { "complete", NULL, NULL };
278: dummyargv[1] = dir;
1.1 lukem 279:
280: if ((file = strrchr(word, '/')) == NULL) {
1.6 lukem 281: dir[0] = '.';
282: dir[1] = '\0';
1.1 lukem 283: file = word;
284: } else {
1.3 lukem 285: cp = file;
286: while (*cp == '/' && cp > word)
287: cp--;
1.8 lukem 288: (void)strncpy(dir, word, cp - word + 1);
289: dir[cp - word + 1] = '\0';
1.1 lukem 290: file++;
291: }
292:
293: if (dirchange || strcmp(dir, lastdir) != 0) { /* dir not cached */
1.3 lukem 294: char *emesg;
295:
1.1 lukem 296: if (dirlist != NULL)
297: sl_free(dirlist, 1);
298: dirlist = sl_init();
299:
300: mflag = 1;
1.3 lukem 301: emesg = NULL;
302: while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) {
1.1 lukem 303: char *tcp;
304:
305: if (!mflag)
306: continue;
307: if (*cp == '\0') {
308: mflag = 0;
309: continue;
310: }
1.8 lukem 311: tcp = strrchr(cp, '/');
312: if (tcp)
313: tcp++;
314: else
315: tcp = cp;
1.15 lukem 316: tcp = xstrdup(tcp);
1.1 lukem 317: sl_add(dirlist, tcp);
318: }
1.3 lukem 319: if (emesg != NULL) {
1.14 lukem 320: fprintf(ttyout, "\n%s\n", emesg);
1.6 lukem 321: return (CC_REDISPLAY);
1.3 lukem 322: }
1.6 lukem 323: (void)strcpy(lastdir, dir);
1.1 lukem 324: dirchange = 0;
325: }
326:
327: words = sl_init();
328: for (i = 0; i < dirlist->sl_cur; i++) {
329: cp = dirlist->sl_str[i];
1.3 lukem 330: if (strlen(file) > strlen(cp))
1.1 lukem 331: continue;
1.3 lukem 332: if (strncmp(file, cp, strlen(file)) == 0)
333: sl_add(words, cp);
1.1 lukem 334: }
335: rv = complete_ambiguous(file, list, words);
336: sl_free(words, 0);
1.6 lukem 337: return (rv);
1.1 lukem 338: }
339:
340: /*
341: * Generic complete routine
342: */
343: unsigned char
344: complete(el, ch)
345: EditLine *el;
346: int ch;
347: {
348: static char word[FTPBUFLEN];
349: static int lastc_argc, lastc_argo;
350:
351: struct cmd *c;
352: const LineInfo *lf;
1.11 lukem 353: int celems, dolist;
354: size_t len;
1.1 lukem 355:
356: lf = el_line(el);
357: len = lf->lastchar - lf->buffer;
358: if (len >= sizeof(line))
1.6 lukem 359: return (CC_ERROR);
360: (void)strncpy(line, lf->buffer, len);
1.1 lukem 361: line[len] = '\0';
362: cursor_pos = line + (lf->cursor - lf->buffer);
363: lastc_argc = cursor_argc; /* remember last cursor pos */
364: lastc_argo = cursor_argo;
365: makeargv(); /* build argc/argv of current line */
366:
367: if (cursor_argo >= sizeof(word))
1.6 lukem 368: return (CC_ERROR);
1.1 lukem 369:
370: dolist = 0;
371: /* if cursor and word is same, list alternatives */
372: if (lastc_argc == cursor_argc && lastc_argo == cursor_argo
373: && strncmp(word, margv[cursor_argc], cursor_argo) == 0)
374: dolist = 1;
1.28 lukem 375: else if (cursor_argc < margc)
1.19 lukem 376: (void)strncpy(word, margv[cursor_argc], cursor_argo);
1.1 lukem 377: word[cursor_argo] = '\0';
378:
379: if (cursor_argc == 0)
1.6 lukem 380: return (complete_command(word, dolist));
1.1 lukem 381:
382: c = getcmd(margv[0]);
383: if (c == (struct cmd *)-1 || c == 0)
1.6 lukem 384: return (CC_ERROR);
1.1 lukem 385: celems = strlen(c->c_complete);
386:
387: /* check for 'continuation' completes (which are uppercase) */
388: if ((cursor_argc > celems) && (celems > 0)
1.12 christos 389: && isupper((unsigned char) c->c_complete[celems-1]))
1.1 lukem 390: cursor_argc = celems;
391:
392: if (cursor_argc > celems)
1.6 lukem 393: return (CC_ERROR);
1.1 lukem 394:
395: switch (c->c_complete[cursor_argc - 1]) {
396: case 'l': /* local complete */
397: case 'L':
1.6 lukem 398: return (complete_local(word, dolist));
1.1 lukem 399: case 'r': /* remote complete */
400: case 'R':
1.7 lukem 401: if (connected != -1) {
1.14 lukem 402: fputs("\nMust be logged in to complete.\n",
403: ttyout);
1.6 lukem 404: return (CC_REDISPLAY);
1.1 lukem 405: }
1.6 lukem 406: return (complete_remote(word, dolist));
1.1 lukem 407: case 'c': /* command complete */
408: case 'C':
1.6 lukem 409: return (complete_command(word, dolist));
1.1 lukem 410: case 'n': /* no complete */
411: default:
1.6 lukem 412: return (CC_ERROR);
1.1 lukem 413: }
1.29 lukem 414: /* NOTREACHED */
1.1 lukem 415: }
1.9 lukem 416:
1.24 cgd 417: #endif /* !NO_EDITCOMPLETE */
CVSweb <webmaster@jp.NetBSD.org>