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