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