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