1 /* $NetBSD: complete.c,v 1.38 2000/05/01 10:35:17 lukem Exp $ */
4 * Copyright (c) 1997-2000 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>
42 * FTP user program - command and file completion routines
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);
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.
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, ...)
83 complete_ambiguous(char *word
, int list
, StringList
*words
)
85 char insertstr
[MAXPATHLEN
];
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? */
98 ftpvis(insertstr
, sizeof(insertstr
), p
, strlen(p
));
99 if (el_insertstr(el
, insertstr
) == -1)
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
])
116 if (matchlen
> wordlen
) {
117 ftpvis(insertstr
, sizeof(insertstr
),
118 lastmatch
+ wordlen
, matchlen
- wordlen
);
119 if (el_insertstr(el
, insertstr
) == -1)
122 return (CC_REFRESH_BEEP
);
127 qsort(words
->sl_str
, words
->sl_cur
, sizeof(char *), comparstr
);
128 list_vertical(words
);
129 return (CC_REDISPLAY
);
136 complete_command(char *word
, int list
)
144 wordlen
= strlen(word
);
146 for (c
= cmdtab
; c
->c_name
!= NULL
; c
++) {
147 if (wordlen
> strlen(c
->c_name
))
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)
163 * Complete a local file
166 complete_local(char *word
, int list
)
169 char dir
[MAXPATHLEN
];
176 if ((file
= strrchr(word
, '/')) == NULL
) {
185 (void)strlcpy(dir
, word
, file
- word
+ 1);
191 if ((p
= globulize(dir
)) == NULL
)
193 (void)strlcpy(dir
, p
, sizeof(dir
));
197 if ((dd
= opendir(dir
)) == NULL
)
203 for (dp
= readdir(dd
); dp
!= NULL
; dp
= readdir(dd
)) {
204 if (!strcmp(dp
->d_name
, ".") || !strcmp(dp
->d_name
, ".."))
207 #if defined(DIRENT_MISSING_D_NAMLEN)
208 if (len
> strlen(dp
->d_name
))
211 if (len
> dp
->d_namlen
)
214 if (strncmp(file
, dp
->d_name
, len
) == 0) {
217 tcp
= xstrdup(dp
->d_name
);
223 rv
= complete_ambiguous(file
, list
, words
);
224 if (rv
== CC_REFRESH
) {
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
))
237 if (el_insertstr(el
, suffix
) == -1)
248 complete_option(char *word
, int list
)
256 wordlen
= strlen(word
);
258 for (o
= optiontab
; o
->name
!= NULL
; o
++) {
259 if (wordlen
> strlen(o
->name
))
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)
275 * Complete a remote file
278 complete_remote(char *word
, int list
)
280 static StringList
*dirlist
;
281 static char lastdir
[MAXPATHLEN
];
283 char dir
[MAXPATHLEN
];
288 char *dummyargv
[] = { "complete", NULL
, NULL
};
291 if ((file
= strrchr(word
, '/')) == NULL
) {
296 while (*cp
== '/' && cp
> word
)
298 (void)strlcpy(dir
, word
, cp
- word
+ 2);
302 if (dirchange
|| dirlist
== NULL
||
303 strcmp(dir
, lastdir
) != 0) { /* dir not cached */
308 dirlist
= xsl_init();
312 while ((cp
= remglob(dummyargv
, 0, &emesg
)) != NULL
) {
321 tcp
= strrchr(cp
, '/');
327 xsl_add(dirlist
, tcp
);
330 fprintf(ttyout
, "\n%s\n", emesg
);
331 return (CC_REDISPLAY
);
333 (void)strlcpy(lastdir
, dir
, sizeof(lastdir
));
338 for (i
= 0; i
< dirlist
->sl_cur
; i
++) {
339 cp
= dirlist
->sl_str
[i
];
340 if (strlen(file
) > strlen(cp
))
342 if (strncmp(file
, cp
, strlen(file
)) == 0)
345 rv
= complete_ambiguous(file
, list
, words
);
351 * Generic complete routine
354 complete(EditLine
*el
, int ch
)
356 static char word
[FTPBUFLEN
];
357 static int lastc_argc
, lastc_argo
;
361 int celems
, dolist
, cmpltype
;
365 len
= lf
->lastchar
- lf
->buffer
;
366 if (len
>= sizeof(line
))
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
))
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
] : "",
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)
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
)
403 cmpltype
= c
->c_complete
[cursor_argc
- 1];
405 case 'c': /* command complete */
407 return (complete_command(word
, dolist
));
408 case 'l': /* local complete */
410 return (complete_local(word
, dolist
));
411 case 'n': /* no complete */
412 case 'N': /* no complete */
414 case 'o': /* local complete */
416 return (complete_option(word
, dolist
));
417 case 'r': /* remote complete */
419 if (connected
!= -1) {
420 fputs("\nMust be logged in to complete.\n",
422 return (CC_REDISPLAY
);
424 return (complete_remote(word
, dolist
));
426 errx(1, "unknown complete type `%c'", cmpltype
);
432 #endif /* !NO_EDITCOMPLETE */