Annotation of src/usr.bin/ftp/complete.c, Revision 1.10
1.10 ! millert 1: /* $OpenBSD: complete.c,v 1.9 1997/09/04 04:37:14 millert Exp $ */
1.9 millert 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.10 ! millert 42: static char rcsid[] = "$OpenBSD: complete.c,v 1.9 1997/09/04 04:37:14 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;
1.10 ! millert 258: if (debug)
! 259: (void)putc('\n', ttyout);
1.4 millert 260: while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) {
1.1 millert 261: char *tcp;
262:
263: if (!mflag)
264: continue;
265: if (*cp == '\0') {
266: mflag = 0;
267: continue;
268: }
1.8 millert 269: tcp = strrchr(cp, '/');
270: if (tcp)
271: tcp++;
272: else
273: tcp = cp;
274: tcp = strdup(tcp);
1.1 millert 275: if (tcp == NULL)
276: errx(1, "Can't allocate memory for remote dir");
277: sl_add(dirlist, tcp);
278: }
1.4 millert 279: if (emesg != NULL) {
1.7 deraadt 280: fprintf(ttyout, "\n%s\n", emesg);
1.4 millert 281: return (CC_REDISPLAY);
282: }
283: (void)strcpy(lastdir, dir);
1.1 millert 284: dirchange = 0;
285: }
286:
287: words = sl_init();
288: for (i = 0; i < dirlist->sl_cur; i++) {
289: cp = dirlist->sl_str[i];
1.4 millert 290: if (strlen(file) > strlen(cp))
1.1 millert 291: continue;
1.4 millert 292: if (strncmp(file, cp, strlen(file)) == 0)
293: sl_add(words, cp);
1.1 millert 294: }
295: rv = complete_ambiguous(file, list, words);
296: sl_free(words, 0);
1.4 millert 297: return (rv);
1.1 millert 298: }
299:
300: /*
301: * Generic complete routine
302: */
303: unsigned char
304: complete(el, ch)
305: EditLine *el;
306: int ch;
307: {
308: static char word[FTPBUFLEN];
309: static int lastc_argc, lastc_argo;
310:
311: struct cmd *c;
312: const LineInfo *lf;
1.8 millert 313: int celems, dolist;
314: size_t len;
1.1 millert 315:
316: lf = el_line(el);
317: len = lf->lastchar - lf->buffer;
318: if (len >= sizeof(line))
1.4 millert 319: return (CC_ERROR);
1.5 millert 320: (void)strncpy(line, lf->buffer, len);
1.1 millert 321: line[len] = '\0';
322: cursor_pos = line + (lf->cursor - lf->buffer);
323: lastc_argc = cursor_argc; /* remember last cursor pos */
324: lastc_argo = cursor_argo;
325: makeargv(); /* build argc/argv of current line */
326:
327: if (cursor_argo >= sizeof(word))
1.4 millert 328: return (CC_ERROR);
1.1 millert 329:
330: dolist = 0;
331: /* if cursor and word is same, list alternatives */
332: if (lastc_argc == cursor_argc && lastc_argo == cursor_argo
333: && strncmp(word, margv[cursor_argc], cursor_argo) == 0)
334: dolist = 1;
335: else
1.5 millert 336: (void)strncpy(word, margv[cursor_argc], cursor_argo);
1.1 millert 337: word[cursor_argo] = '\0';
338:
339: if (cursor_argc == 0)
1.4 millert 340: return (complete_command(word, dolist));
1.1 millert 341:
342: c = getcmd(margv[0]);
343: if (c == (struct cmd *)-1 || c == 0)
1.4 millert 344: return (CC_ERROR);
1.1 millert 345: celems = strlen(c->c_complete);
346:
347: /* check for 'continuation' completes (which are uppercase) */
348: if ((cursor_argc > celems) && (celems > 0)
349: && isupper(c->c_complete[celems-1]))
350: cursor_argc = celems;
351:
352: if (cursor_argc > celems)
1.4 millert 353: return (CC_ERROR);
1.1 millert 354:
355: switch (c->c_complete[cursor_argc - 1]) {
356: case 'l': /* local complete */
357: case 'L':
1.4 millert 358: return (complete_local(word, dolist));
1.1 millert 359: case 'r': /* remote complete */
360: case 'R':
1.6 millert 361: if (connected != -1) {
1.7 deraadt 362: fputs("\nMust be logged in to complete.\n", ttyout);
1.4 millert 363: return (CC_REDISPLAY);
1.1 millert 364: }
1.4 millert 365: return (complete_remote(word, dolist));
1.1 millert 366: case 'c': /* command complete */
367: case 'C':
1.4 millert 368: return (complete_command(word, dolist));
1.1 millert 369: case 'n': /* no complete */
370: default:
1.4 millert 371: return (CC_ERROR);
1.1 millert 372: }
373:
1.4 millert 374: return (CC_ERROR);
1.1 millert 375: }
1.8 millert 376:
377: #endif /* !SMALL */