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