1 /* $NetBSD: complete.c,v 1.42 2007/04/17 05:52:03 lukem Exp $ */
4 * Copyright (c) 1997-2000,2005 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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>
41 __RCSID("$NetBSD: complete.c,v 1.42 2007/04/17 05:52:03 lukem Exp $");
45 * FTP user program - command and file completion routines
59 #ifndef NO_EDITCOMPLETE
61 static int comparstr (const void *, const void *);
62 static unsigned char complete_ambiguous (char *, int, StringList
*);
63 static unsigned char complete_command (char *, int);
64 static unsigned char complete_local (char *, int);
65 static unsigned char complete_option (char *, int);
66 static unsigned char complete_remote (char *, int);
69 comparstr(const void *a
, const void *b
)
71 return (strcmp(*(const char * const *)a
, *(const char * const *)b
));
75 * Determine if complete is ambiguous. If unique, insert.
76 * If no choices, error. If unambiguous prefix, insert that.
77 * Otherwise, list choices. words is assumed to be filtered
78 * to only contain possible choices.
80 * word word which started the match
81 * list list by default
82 * words stringlist containing possible matches
83 * Returns a result as per el_set(EL_ADDFN, ...)
86 complete_ambiguous(char *word
, int list
, StringList
*words
)
88 char insertstr
[MAXPATHLEN
];
91 size_t matchlen
, wordlen
;
93 wordlen
= strlen(word
);
94 if (words
->sl_cur
== 0)
95 return (CC_ERROR
); /* no choices available */
97 if (words
->sl_cur
== 1) { /* only once choice available */
98 p
= words
->sl_str
[0] + wordlen
;
99 if (*p
== '\0') /* at end of word? */
101 ftpvis(insertstr
, sizeof(insertstr
), p
, strlen(p
));
102 if (el_insertstr(el
, insertstr
) == -1)
110 lastmatch
= words
->sl_str
[0];
111 matchlen
= strlen(lastmatch
);
112 for (i
= 1 ; i
< words
->sl_cur
; i
++) {
113 for (j
= wordlen
; j
< strlen(words
->sl_str
[i
]); j
++)
114 if (lastmatch
[j
] != words
->sl_str
[i
][j
])
119 if (matchlen
> wordlen
) {
120 ftpvis(insertstr
, sizeof(insertstr
),
121 lastmatch
+ wordlen
, matchlen
- wordlen
);
122 if (el_insertstr(el
, insertstr
) == -1)
125 return (CC_REFRESH_BEEP
);
130 qsort(words
->sl_str
, words
->sl_cur
, sizeof(char *), comparstr
);
131 list_vertical(words
);
132 return (CC_REDISPLAY
);
139 complete_command(char *word
, int list
)
146 words
= ftp_sl_init();
147 wordlen
= strlen(word
);
149 for (c
= cmdtab
; c
->c_name
!= NULL
; c
++) {
150 if (wordlen
> strlen(c
->c_name
))
152 if (strncmp(word
, c
->c_name
, wordlen
) == 0)
153 ftp_sl_add(words
, c
->c_name
);
156 rv
= complete_ambiguous(word
, list
, words
);
157 if (rv
== CC_REFRESH
) {
158 if (el_insertstr(el
, " ") == -1)
166 * Complete a local file
169 complete_local(char *word
, int list
)
172 char dir
[MAXPATHLEN
];
179 if ((file
= strrchr(word
, '/')) == NULL
) {
188 (void)strlcpy(dir
, word
, file
- word
+ 1);
194 if ((p
= globulize(dir
)) == NULL
)
196 (void)strlcpy(dir
, p
, sizeof(dir
));
200 if ((dd
= opendir(dir
)) == NULL
)
203 words
= ftp_sl_init();
206 for (dp
= readdir(dd
); dp
!= NULL
; dp
= readdir(dd
)) {
207 if (!strcmp(dp
->d_name
, ".") || !strcmp(dp
->d_name
, ".."))
210 #if defined(DIRENT_MISSING_D_NAMLEN)
211 if (len
> strlen(dp
->d_name
))
214 if (len
> dp
->d_namlen
)
217 if (strncmp(file
, dp
->d_name
, len
) == 0) {
220 tcp
= ftp_strdup(dp
->d_name
);
221 ftp_sl_add(words
, tcp
);
226 rv
= complete_ambiguous(file
, list
, words
);
227 if (rv
== CC_REFRESH
) {
229 char path
[MAXPATHLEN
];
231 (void)strlcpy(path
, dir
, sizeof(path
));
232 (void)strlcat(path
, "/", sizeof(path
));
233 (void)strlcat(path
, words
->sl_str
[0], sizeof(path
));
235 if (stat(path
, &sb
) >= 0) {
236 char suffix
[2] = " ";
238 if (S_ISDIR(sb
.st_mode
))
240 if (el_insertstr(el
, suffix
) == -1)
251 complete_option(char *word
, int list
)
258 words
= ftp_sl_init();
259 wordlen
= strlen(word
);
261 for (o
= optiontab
; o
->name
!= NULL
; o
++) {
262 if (wordlen
> strlen(o
->name
))
264 if (strncmp(word
, o
->name
, wordlen
) == 0)
265 ftp_sl_add(words
, o
->name
);
268 rv
= complete_ambiguous(word
, list
, words
);
269 if (rv
== CC_REFRESH
) {
270 if (el_insertstr(el
, " ") == -1)
278 * Complete a remote file
281 complete_remote(char *word
, int list
)
283 static StringList
*dirlist
;
284 static char lastdir
[MAXPATHLEN
];
286 char dir
[MAXPATHLEN
];
291 char *dummyargv
[] = { "complete", NULL
, NULL
};
294 if ((file
= strrchr(word
, '/')) == NULL
) {
299 while (*cp
== '/' && cp
> word
)
301 (void)strlcpy(dir
, word
, cp
- word
+ 2);
305 if (dirchange
|| dirlist
== NULL
||
306 strcmp(dir
, lastdir
) != 0) { /* dir not cached */
311 dirlist
= ftp_sl_init();
315 while ((cp
= remglob(dummyargv
, 0, &emesg
)) != NULL
) {
324 tcp
= strrchr(cp
, '/');
329 tcp
= ftp_strdup(tcp
);
330 ftp_sl_add(dirlist
, tcp
);
333 fprintf(ttyout
, "\n%s\n", emesg
);
334 return (CC_REDISPLAY
);
336 (void)strlcpy(lastdir
, dir
, sizeof(lastdir
));
340 words
= ftp_sl_init();
341 for (i
= 0; i
< dirlist
->sl_cur
; i
++) {
342 cp
= dirlist
->sl_str
[i
];
343 if (strlen(file
) > strlen(cp
))
345 if (strncmp(file
, cp
, strlen(file
)) == 0)
346 ftp_sl_add(words
, cp
);
348 rv
= complete_ambiguous(file
, list
, words
);
354 * Generic complete routine
357 complete(EditLine
*el
, int ch
)
359 static char word
[FTPBUFLEN
];
360 static int lastc_argc
, lastc_argo
;
364 int celems
, dolist
, cmpltype
;
368 len
= lf
->lastchar
- lf
->buffer
;
369 if (len
>= sizeof(line
))
371 (void)strlcpy(line
, lf
->buffer
, len
+ 1);
372 cursor_pos
= line
+ (lf
->cursor
- lf
->buffer
);
373 lastc_argc
= cursor_argc
; /* remember last cursor pos */
374 lastc_argo
= cursor_argo
;
375 makeargv(); /* build argc/argv of current line */
377 if (cursor_argo
>= sizeof(word
))
381 /* if cursor and word is same, list alternatives */
382 if (lastc_argc
== cursor_argc
&& lastc_argo
== cursor_argo
383 && strncmp(word
, margv
[cursor_argc
] ? margv
[cursor_argc
] : "",
386 else if (cursor_argc
< margc
)
387 (void)strlcpy(word
, margv
[cursor_argc
], cursor_argo
+ 1);
388 word
[cursor_argo
] = '\0';
390 if (cursor_argc
== 0)
391 return (complete_command(word
, dolist
));
393 c
= getcmd(margv
[0]);
394 if (c
== (struct cmd
*)-1 || c
== 0)
396 celems
= strlen(c
->c_complete
);
398 /* check for 'continuation' completes (which are uppercase) */
399 if ((cursor_argc
> celems
) && (celems
> 0)
400 && isupper((unsigned char) c
->c_complete
[celems
-1]))
401 cursor_argc
= celems
;
403 if (cursor_argc
> celems
)
406 cmpltype
= c
->c_complete
[cursor_argc
- 1];
408 case 'c': /* command complete */
410 return (complete_command(word
, dolist
));
411 case 'l': /* local complete */
413 return (complete_local(word
, dolist
));
414 case 'n': /* no complete */
415 case 'N': /* no complete */
417 case 'o': /* local complete */
419 return (complete_option(word
, dolist
));
420 case 'r': /* remote complete */
422 if (connected
!= -1) {
423 fputs("\nMust be logged in to complete.\n",
425 return (CC_REDISPLAY
);
427 return (complete_remote(word
, dolist
));
429 errx(1, "complete: unknown complete type `%c'",
436 #endif /* !NO_EDITCOMPLETE */