Annotation of src/usr.bin/ftp/complete.c, Revision 1.6
1.6 ! millert 1: /* $OpenBSD: complete.c,v 1.5 1997/03/21 20:59:28 millert Exp $ */
! 2: /* $NetBSD: complete.c,v 1.7 1997/04/14 09:09:16 lukem Exp $ */
1.1 millert 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:
1.5 millert 40: #ifndef SMALL
1.1 millert 41: #ifndef lint
1.6 ! millert 42: static char rcsid[] = "$OpenBSD: complete.c,v 1.5 1997/03/21 20:59:28 millert Exp $";
1.1 millert 43: #endif /* not lint */
44:
45: /*
46: * FTP user program - command and file completion routines
47: */
48:
49: #include <ctype.h>
50: #include <err.h>
51: #include <dirent.h>
52: #include <stdio.h>
53: #include <stdlib.h>
54: #include <string.h>
55:
56: #include "ftp_var.h"
57:
58: static int
59: comparstr(a, b)
60: const void *a, *b;
61: {
1.4 millert 62: return (strcmp(*(char **)a, *(char **)b));
1.1 millert 63: }
64:
65: /*
66: * Determine if complete is ambiguous. If unique, insert.
67: * If no choices, error. If unambiguous prefix, insert that.
68: * Otherwise, list choices. words is assumed to be filtered
69: * to only contain possible choices.
70: * Args:
71: * word word which started the match
72: * list list by default
73: * words stringlist containing possible matches
74: */
75: static unsigned char
76: complete_ambiguous(word, list, words)
77: char *word;
78: int list;
79: StringList *words;
80: {
1.3 millert 81: char insertstr[MAXPATHLEN];
1.1 millert 82: char *lastmatch;
83: int i, j, matchlen, wordlen;
84:
85: wordlen = strlen(word);
86: if (words->sl_cur == 0)
1.4 millert 87: return (CC_ERROR); /* no choices available */
1.1 millert 88:
89: if (words->sl_cur == 1) { /* only once choice available */
1.5 millert 90: (void)strcpy(insertstr, words->sl_str[0]);
1.1 millert 91: if (el_insertstr(el, insertstr + wordlen) == -1)
1.4 millert 92: return (CC_ERROR);
1.1 millert 93: else
1.4 millert 94: return (CC_REFRESH);
1.1 millert 95: }
96:
97: if (!list) {
98: matchlen = 0;
99: lastmatch = words->sl_str[0];
100: matchlen = strlen(lastmatch);
101: for (i = 1 ; i < words->sl_cur ; i++) {
102: for (j = wordlen ; j < strlen(words->sl_str[i]); j++)
103: if (lastmatch[j] != words->sl_str[i][j])
104: break;
105: if (j < matchlen)
106: matchlen = j;
107: }
108: if (matchlen > wordlen) {
1.5 millert 109: (void)strncpy(insertstr, lastmatch, matchlen);
1.1 millert 110: insertstr[matchlen] = '\0';
111: if (el_insertstr(el, insertstr + wordlen) == -1)
1.4 millert 112: return (CC_ERROR);
1.1 millert 113: else
114: /*
115: * XXX: really want CC_REFRESH_BEEP
116: */
1.4 millert 117: return (CC_REFRESH);
1.1 millert 118: }
119: }
120:
121: putchar('\n');
122: qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr);
123: list_vertical(words);
1.4 millert 124: return (CC_REDISPLAY);
1.1 millert 125: }
126:
127: /*
128: * Complete a command
129: */
130: static unsigned char
131: complete_command(word, list)
132: char *word;
133: int list;
134: {
135: struct cmd *c;
136: StringList *words;
137: int wordlen;
138: unsigned char rv;
139:
140: words = sl_init();
141: wordlen = strlen(word);
142:
143: for (c = cmdtab; c->c_name != NULL; c++) {
144: if (wordlen > strlen(c->c_name))
145: continue;
146: if (strncmp(word, c->c_name, wordlen) == 0)
147: sl_add(words, c->c_name);
148: }
149:
150: rv = complete_ambiguous(word, list, words);
151: sl_free(words, 0);
1.4 millert 152: return (rv);
1.1 millert 153: }
154:
155: /*
156: * Complete a local file
157: */
158: static unsigned char
159: complete_local(word, list)
160: char *word;
161: int list;
162: {
163: StringList *words;
1.3 millert 164: char dir[MAXPATHLEN];
1.1 millert 165: char *file;
166: DIR *dd;
167: struct dirent *dp;
168: unsigned char rv;
169:
170: if ((file = strrchr(word, '/')) == NULL) {
1.5 millert 171: dir[0] = '.';
172: dir[1] = '\0';
1.1 millert 173: file = word;
174: } else {
1.5 millert 175: if (file == word) {
176: dir[0] = '/';
177: dir[1] = '\0';
178: } else {
179: (void)strncpy(dir, word, file - word);
1.1 millert 180: dir[file - word] = '\0';
181: }
1.5 millert 182: file++;
1.1 millert 183: }
184:
185: if ((dd = opendir(dir)) == NULL)
1.4 millert 186: return (CC_ERROR);
1.1 millert 187:
188: words = sl_init();
189:
190: for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
191: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
192: continue;
193: if (strlen(file) > dp->d_namlen)
194: continue;
195: if (strncmp(file, dp->d_name, strlen(file)) == 0) {
196: char *tcp;
197:
198: tcp = strdup(dp->d_name);
199: if (tcp == NULL)
200: errx(1, "Can't allocate memory for local dir");
201: sl_add(words, tcp);
202: }
203: }
204: closedir(dd);
205:
206: rv = complete_ambiguous(file, list, words);
207: sl_free(words, 1);
1.4 millert 208: return (rv);
1.1 millert 209: }
210:
211: /*
212: * Complete a remote file
213: */
214: static unsigned char
215: complete_remote(word, list)
216: char *word;
217: int list;
218: {
219: static StringList *dirlist;
1.3 millert 220: static char lastdir[MAXPATHLEN];
1.1 millert 221: StringList *words;
1.3 millert 222: char dir[MAXPATHLEN];
1.1 millert 223: char *file, *cp;
1.4 millert 224: int i;
1.1 millert 225: unsigned char rv;
226:
227: char *dummyargv[] = { "complete", dir, NULL };
228:
229: if ((file = strrchr(word, '/')) == NULL) {
1.5 millert 230: dir[0] = '.';
231: dir[1] = '\0';
1.1 millert 232: file = word;
233: } else {
1.4 millert 234: cp = file;
235: while (*cp == '/' && cp > word)
236: cp--;
237: if (cp == word) {
238: dir[0] = '/';
239: dir[1] = '\0';
240: } else {
241: (void)strncpy(dir, word, cp - word + 1);
242: dir[cp - word + 1] = '\0';
1.1 millert 243: }
244: file++;
245: }
246:
247: if (dirchange || strcmp(dir, lastdir) != 0) { /* dir not cached */
1.4 millert 248: char *emesg;
249: int dirlen, ftpdslashbug;
250:
251: dirlen = strlen(dir);
252: ftpdslashbug = 0;
253: if (strcmp(dir, "/") == 0)
254: ftpdslashbug = 1;
255: else if (strcmp(dir, ".") == 0)
256: dirlen = 0;
257: else
258: dirlen++;
1.1 millert 259: if (dirlist != NULL)
260: sl_free(dirlist, 1);
261: dirlist = sl_init();
262:
263: mflag = 1;
1.4 millert 264: emesg = NULL;
265: while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) {
1.1 millert 266: char *tcp;
267:
268: if (!mflag)
269: continue;
270: if (*cp == '\0') {
271: mflag = 0;
272: continue;
273: }
274: /*
275: * Work around ftpd(1) bug, which puts a // instead
276: * of / in front of each filename returned by "NLST /".
1.4 millert 277: * Without this, remote completes of / don't work.
278: * However, only do this if the bug is present.
1.1 millert 279: */
1.4 millert 280: if (ftpdslashbug && (cp[dirlen] != '/'))
281: ftpdslashbug = 0;
282: tcp = strdup(cp + dirlen + ftpdslashbug);
1.1 millert 283: if (tcp == NULL)
284: errx(1, "Can't allocate memory for remote dir");
285: sl_add(dirlist, tcp);
286: }
1.4 millert 287: if (emesg != NULL) {
288: printf("\n%s\n", emesg);
289: return (CC_REDISPLAY);
290: }
291: (void)strcpy(lastdir, dir);
1.1 millert 292: dirchange = 0;
293: }
294:
295: words = sl_init();
296: for (i = 0; i < dirlist->sl_cur; i++) {
297: cp = dirlist->sl_str[i];
1.4 millert 298: if (strlen(file) > strlen(cp))
1.1 millert 299: continue;
1.4 millert 300: if (strncmp(file, cp, strlen(file)) == 0)
301: sl_add(words, cp);
1.1 millert 302: }
303: rv = complete_ambiguous(file, list, words);
304: sl_free(words, 0);
1.4 millert 305: return (rv);
1.1 millert 306: }
307:
308: /*
309: * Generic complete routine
310: */
311: unsigned char
312: complete(el, ch)
313: EditLine *el;
314: int ch;
315: {
316: static char word[FTPBUFLEN];
317: static int lastc_argc, lastc_argo;
318:
319: struct cmd *c;
320: const LineInfo *lf;
321: int len, celems, dolist;
322:
323: lf = el_line(el);
324: len = lf->lastchar - lf->buffer;
325: if (len >= sizeof(line))
1.4 millert 326: return (CC_ERROR);
1.5 millert 327: (void)strncpy(line, lf->buffer, len);
1.1 millert 328: line[len] = '\0';
329: cursor_pos = line + (lf->cursor - lf->buffer);
330: lastc_argc = cursor_argc; /* remember last cursor pos */
331: lastc_argo = cursor_argo;
332: makeargv(); /* build argc/argv of current line */
333:
334: if (cursor_argo >= sizeof(word))
1.4 millert 335: return (CC_ERROR);
1.1 millert 336:
337: dolist = 0;
338: /* if cursor and word is same, list alternatives */
339: if (lastc_argc == cursor_argc && lastc_argo == cursor_argo
340: && strncmp(word, margv[cursor_argc], cursor_argo) == 0)
341: dolist = 1;
342: else
1.5 millert 343: (void)strncpy(word, margv[cursor_argc], cursor_argo);
1.1 millert 344: word[cursor_argo] = '\0';
345:
346: if (cursor_argc == 0)
1.4 millert 347: return (complete_command(word, dolist));
1.1 millert 348:
349: c = getcmd(margv[0]);
350: if (c == (struct cmd *)-1 || c == 0)
1.4 millert 351: return (CC_ERROR);
1.1 millert 352: celems = strlen(c->c_complete);
353:
354: /* check for 'continuation' completes (which are uppercase) */
355: if ((cursor_argc > celems) && (celems > 0)
356: && isupper(c->c_complete[celems-1]))
357: cursor_argc = celems;
358:
359: if (cursor_argc > celems)
1.4 millert 360: return (CC_ERROR);
1.1 millert 361:
362: switch (c->c_complete[cursor_argc - 1]) {
363: case 'l': /* local complete */
364: case 'L':
1.4 millert 365: return (complete_local(word, dolist));
1.1 millert 366: case 'r': /* remote complete */
367: case 'R':
1.6 ! millert 368: if (connected != -1) {
! 369: puts("\nMust be logged in to complete.");
1.4 millert 370: return (CC_REDISPLAY);
1.1 millert 371: }
1.4 millert 372: return (complete_remote(word, dolist));
1.1 millert 373: case 'c': /* command complete */
374: case 'C':
1.4 millert 375: return (complete_command(word, dolist));
1.1 millert 376: case 'n': /* no complete */
377: default:
1.4 millert 378: return (CC_ERROR);
1.1 millert 379: }
380:
1.4 millert 381: return (CC_ERROR);
1.1 millert 382: }
1.5 millert 383: #endif