vfs: check userland buffers before reading them.
[haiku.git] / src / bin / network / ftp / complete.c
blobe30aa79ed02a07f041b481f9b4b44c32ff414b74
1 /* $NetBSD: complete.c,v 1.38 2000/05/01 10:35:17 lukem Exp $ */
3 /*-
4 * Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
39 #include <sys/cdefs.h>
42 * FTP user program - command and file completion routines
45 #include <sys/stat.h>
47 #include <ctype.h>
48 #include <err.h>
49 #include <dirent.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
54 #include "ftp_var.h"
56 #ifndef NO_EDITCOMPLETE
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_option (char *, int);
63 static unsigned char complete_remote (char *, int);
65 static int
66 comparstr(const void *a, const void *b)
68 return (strcmp(*(const char **)a, *(const char **)b));
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 * Returns a result as per el_set(EL_ADDFN, ...)
82 static unsigned char
83 complete_ambiguous(char *word, int list, StringList *words)
85 char insertstr[MAXPATHLEN];
86 char *lastmatch, *p;
87 int i, j;
88 size_t matchlen, wordlen;
90 wordlen = strlen(word);
91 if (words->sl_cur == 0)
92 return (CC_ERROR); /* no choices available */
94 if (words->sl_cur == 1) { /* only once choice available */
95 p = words->sl_str[0] + wordlen;
96 if (*p == '\0') /* at end of word? */
97 return (CC_REFRESH);
98 ftpvis(insertstr, sizeof(insertstr), p, strlen(p));
99 if (el_insertstr(el, insertstr) == -1)
100 return (CC_ERROR);
101 else
102 return (CC_REFRESH);
105 if (!list) {
106 matchlen = 0;
107 lastmatch = words->sl_str[0];
108 matchlen = strlen(lastmatch);
109 for (i = 1 ; i < words->sl_cur ; i++) {
110 for (j = wordlen ; j < strlen(words->sl_str[i]); j++)
111 if (lastmatch[j] != words->sl_str[i][j])
112 break;
113 if (j < matchlen)
114 matchlen = j;
116 if (matchlen > wordlen) {
117 ftpvis(insertstr, sizeof(insertstr),
118 lastmatch + wordlen, matchlen - wordlen);
119 if (el_insertstr(el, insertstr) == -1)
120 return (CC_ERROR);
121 else
122 return (CC_REFRESH_BEEP);
126 putc('\n', ttyout);
127 qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr);
128 list_vertical(words);
129 return (CC_REDISPLAY);
133 * Complete a command
135 static unsigned char
136 complete_command(char *word, int list)
138 struct cmd *c;
139 StringList *words;
140 size_t wordlen;
141 unsigned char rv;
143 words = xsl_init();
144 wordlen = strlen(word);
146 for (c = cmdtab; c->c_name != NULL; c++) {
147 if (wordlen > strlen(c->c_name))
148 continue;
149 if (strncmp(word, c->c_name, wordlen) == 0)
150 xsl_add(words, c->c_name);
153 rv = complete_ambiguous(word, list, words);
154 if (rv == CC_REFRESH) {
155 if (el_insertstr(el, " ") == -1)
156 rv = CC_ERROR;
158 sl_free(words, 0);
159 return (rv);
163 * Complete a local file
165 static unsigned char
166 complete_local(char *word, int list)
168 StringList *words;
169 char dir[MAXPATHLEN];
170 char *file;
171 DIR *dd;
172 struct dirent *dp;
173 unsigned char rv;
174 size_t len;
176 if ((file = strrchr(word, '/')) == NULL) {
177 dir[0] = '.';
178 dir[1] = '\0';
179 file = word;
180 } else {
181 if (file == word) {
182 dir[0] = '/';
183 dir[1] = '\0';
184 } else
185 (void)strlcpy(dir, word, file - word + 1);
186 file++;
188 if (dir[0] == '~') {
189 char *p;
191 if ((p = globulize(dir)) == NULL)
192 return (CC_ERROR);
193 (void)strlcpy(dir, p, sizeof(dir));
194 free(p);
197 if ((dd = opendir(dir)) == NULL)
198 return (CC_ERROR);
200 words = xsl_init();
201 len = strlen(file);
203 for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
204 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
205 continue;
207 #if defined(DIRENT_MISSING_D_NAMLEN)
208 if (len > strlen(dp->d_name))
209 continue;
210 #else
211 if (len > dp->d_namlen)
212 continue;
213 #endif
214 if (strncmp(file, dp->d_name, len) == 0) {
215 char *tcp;
217 tcp = xstrdup(dp->d_name);
218 xsl_add(words, tcp);
221 closedir(dd);
223 rv = complete_ambiguous(file, list, words);
224 if (rv == CC_REFRESH) {
225 struct stat sb;
226 char path[MAXPATHLEN];
228 (void)strlcpy(path, dir, sizeof(path));
229 (void)strlcat(path, "/", sizeof(path));
230 (void)strlcat(path, words->sl_str[0], sizeof(path));
232 if (stat(path, &sb) >= 0) {
233 char suffix[2] = " ";
235 if (S_ISDIR(sb.st_mode))
236 suffix[0] = '/';
237 if (el_insertstr(el, suffix) == -1)
238 rv = CC_ERROR;
241 sl_free(words, 1);
242 return (rv);
245 * Complete an option
247 static unsigned char
248 complete_option(char *word, int list)
250 struct option *o;
251 StringList *words;
252 size_t wordlen;
253 unsigned char rv;
255 words = xsl_init();
256 wordlen = strlen(word);
258 for (o = optiontab; o->name != NULL; o++) {
259 if (wordlen > strlen(o->name))
260 continue;
261 if (strncmp(word, o->name, wordlen) == 0)
262 xsl_add(words, o->name);
265 rv = complete_ambiguous(word, list, words);
266 if (rv == CC_REFRESH) {
267 if (el_insertstr(el, " ") == -1)
268 rv = CC_ERROR;
270 sl_free(words, 0);
271 return (rv);
275 * Complete a remote file
277 static unsigned char
278 complete_remote(char *word, int list)
280 static StringList *dirlist;
281 static char lastdir[MAXPATHLEN];
282 StringList *words;
283 char dir[MAXPATHLEN];
284 char *file, *cp;
285 int i;
286 unsigned char rv;
288 char *dummyargv[] = { "complete", NULL, NULL };
289 dummyargv[1] = dir;
291 if ((file = strrchr(word, '/')) == NULL) {
292 dir[0] = '\0';
293 file = word;
294 } else {
295 cp = file;
296 while (*cp == '/' && cp > word)
297 cp--;
298 (void)strlcpy(dir, word, cp - word + 2);
299 file++;
302 if (dirchange || dirlist == NULL ||
303 strcmp(dir, lastdir) != 0) { /* dir not cached */
304 char *emesg;
306 if (dirlist != NULL)
307 sl_free(dirlist, 1);
308 dirlist = xsl_init();
310 mflag = 1;
311 emesg = NULL;
312 while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) {
313 char *tcp;
315 if (!mflag)
316 continue;
317 if (*cp == '\0') {
318 mflag = 0;
319 continue;
321 tcp = strrchr(cp, '/');
322 if (tcp)
323 tcp++;
324 else
325 tcp = cp;
326 tcp = xstrdup(tcp);
327 xsl_add(dirlist, tcp);
329 if (emesg != NULL) {
330 fprintf(ttyout, "\n%s\n", emesg);
331 return (CC_REDISPLAY);
333 (void)strlcpy(lastdir, dir, sizeof(lastdir));
334 dirchange = 0;
337 words = xsl_init();
338 for (i = 0; i < dirlist->sl_cur; i++) {
339 cp = dirlist->sl_str[i];
340 if (strlen(file) > strlen(cp))
341 continue;
342 if (strncmp(file, cp, strlen(file)) == 0)
343 xsl_add(words, cp);
345 rv = complete_ambiguous(file, list, words);
346 sl_free(words, 0);
347 return (rv);
351 * Generic complete routine
353 unsigned char
354 complete(EditLine *el, int ch)
356 static char word[FTPBUFLEN];
357 static int lastc_argc, lastc_argo;
359 struct cmd *c;
360 const LineInfo *lf;
361 int celems, dolist, cmpltype;
362 size_t len;
364 lf = el_line(el);
365 len = lf->lastchar - lf->buffer;
366 if (len >= sizeof(line))
367 return (CC_ERROR);
368 (void)strlcpy(line, lf->buffer, len + 1);
369 cursor_pos = line + (lf->cursor - lf->buffer);
370 lastc_argc = cursor_argc; /* remember last cursor pos */
371 lastc_argo = cursor_argo;
372 makeargv(); /* build argc/argv of current line */
374 if (cursor_argo >= sizeof(word))
375 return (CC_ERROR);
377 dolist = 0;
378 /* if cursor and word is same, list alternatives */
379 if (lastc_argc == cursor_argc && lastc_argo == cursor_argo
380 && strncmp(word, margv[cursor_argc] ? margv[cursor_argc] : "",
381 cursor_argo) == 0)
382 dolist = 1;
383 else if (cursor_argc < margc)
384 (void)strlcpy(word, margv[cursor_argc], cursor_argo + 1);
385 word[cursor_argo] = '\0';
387 if (cursor_argc == 0)
388 return (complete_command(word, dolist));
390 c = getcmd(margv[0]);
391 if (c == (struct cmd *)-1 || c == 0)
392 return (CC_ERROR);
393 celems = strlen(c->c_complete);
395 /* check for 'continuation' completes (which are uppercase) */
396 if ((cursor_argc > celems) && (celems > 0)
397 && isupper((unsigned char) c->c_complete[celems-1]))
398 cursor_argc = celems;
400 if (cursor_argc > celems)
401 return (CC_ERROR);
403 cmpltype = c->c_complete[cursor_argc - 1];
404 switch (cmpltype) {
405 case 'c': /* command complete */
406 case 'C':
407 return (complete_command(word, dolist));
408 case 'l': /* local complete */
409 case 'L':
410 return (complete_local(word, dolist));
411 case 'n': /* no complete */
412 case 'N': /* no complete */
413 return (CC_ERROR);
414 case 'o': /* local complete */
415 case 'O':
416 return (complete_option(word, dolist));
417 case 'r': /* remote complete */
418 case 'R':
419 if (connected != -1) {
420 fputs("\nMust be logged in to complete.\n",
421 ttyout);
422 return (CC_REDISPLAY);
424 return (complete_remote(word, dolist));
425 default:
426 errx(1, "unknown complete type `%c'", cmpltype);
427 return (CC_ERROR);
429 /* NOTREACHED */
432 #endif /* !NO_EDITCOMPLETE */