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