Home | History | Annotate | Line # | Download | only in ftp
complete.c revision 1.2
      1 /*	$NetBSD: complete.c,v 1.2 1997/02/01 10:44:57 lukem Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1997 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Luke Mewburn.
      9  *
     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.
     25  *
     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 REGENTS OR CONTRIBUTORS BE
     30  * 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.
     37  */
     38 
     39 #ifndef lint
     40 static char rcsid[] = "$NetBSD: complete.c,v 1.2 1997/02/01 10:44:57 lukem Exp $";
     41 #endif /* not lint */
     42 
     43 /*
     44  * FTP user program - command and file completion routines
     45  */
     46 
     47 #include <ctype.h>
     48 #include <err.h>
     49 #include <dirent.h>
     50 #include <stdio.h>
     51 #include <stdlib.h>
     52 #include <string.h>
     53 
     54 #include "ftp_var.h"
     55 
     56 static int
     57 comparstr(a, b)
     58 	const void *a, *b;
     59 {
     60 	return strcmp(*(char **)a, *(char **)b);
     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
     74 complete_ambiguous(word, list, words)
     75 	char *word;
     76 	int list;
     77 	StringList *words;
     78 {
     79 	char insertstr[MAXPATHLEN + 1];
     80 	char *lastmatch;
     81 	int i, j, matchlen, wordlen;
     82 
     83 	wordlen = strlen(word);
     84 	if (words->sl_cur == 0)
     85 		return CC_ERROR;	/* no choices available */
     86 
     87 	if (words->sl_cur == 1) {	/* only once choice available */
     88 		strcpy(insertstr, words->sl_str[0]);
     89 		if (el_insertstr(el, insertstr + wordlen) == -1)
     90 			return CC_ERROR;
     91 		else
     92 			return CC_REFRESH;
     93 	}
     94 
     95 	if (!list) {
     96 		matchlen = 0;
     97 		lastmatch = words->sl_str[0];
     98 		matchlen = strlen(lastmatch);
     99 		for (i = 1 ; i < words->sl_cur ; i++) {
    100 			for (j = wordlen ; j < strlen(words->sl_str[i]); j++)
    101 				if (lastmatch[j] != words->sl_str[i][j])
    102 					break;
    103 			if (j < matchlen)
    104 				matchlen = j;
    105 		}
    106 		if (matchlen > wordlen) {
    107 			strncpy(insertstr, lastmatch, matchlen);
    108 			insertstr[matchlen] = '\0';
    109 			if (el_insertstr(el, insertstr + wordlen) == -1)
    110 				return CC_ERROR;
    111 			else
    112 					/*
    113 					 * XXX: really want CC_REFRESH_BEEP
    114 					 */
    115 				return CC_REFRESH;
    116 		}
    117 	}
    118 
    119 	putchar('\n');
    120 	qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr);
    121 	list_vertical(words);
    122 	return CC_REDISPLAY;
    123 }
    124 
    125 /*
    126  * Complete a command
    127  */
    128 static unsigned char
    129 complete_command(word, list)
    130 	char *word;
    131 	int list;
    132 {
    133 	struct cmd *c;
    134 	StringList *words;
    135 	int wordlen;
    136 	unsigned char rv;
    137 
    138 	words = sl_init();
    139 	wordlen = strlen(word);
    140 
    141 	for (c = cmdtab; c->c_name != NULL; c++) {
    142 		if (wordlen > strlen(c->c_name))
    143 			continue;
    144 		if (strncmp(word, c->c_name, wordlen) == 0)
    145 			sl_add(words, c->c_name);
    146 	}
    147 
    148 	rv = complete_ambiguous(word, list, words);
    149 	sl_free(words, 0);
    150 	return rv;
    151 }
    152 
    153 /*
    154  * Complete a local file
    155  */
    156 static unsigned char
    157 complete_local(word, list)
    158 	char *word;
    159 	int list;
    160 {
    161 	StringList *words;
    162 	char dir[MAXPATHLEN + 1];
    163 	char *file;
    164 	DIR *dd;
    165 	struct dirent *dp;
    166 	unsigned char rv;
    167 
    168 	if ((file = strrchr(word, '/')) == NULL) {
    169 		strcpy(dir, ".");
    170 		file = word;
    171 	} else {
    172 		if (file == word)
    173 			strcpy(dir, "/");
    174 		else {
    175 			strncpy(dir, word, file - word);
    176 			dir[file - word] = '\0';
    177 		}
    178 		++file;
    179 	}
    180 
    181 	if ((dd = opendir(dir)) == NULL)
    182 		return CC_ERROR;
    183 
    184 	words = sl_init();
    185 
    186 	for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
    187 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
    188 			continue;
    189 		if (strlen(file) > dp->d_namlen)
    190 			continue;
    191 		if (strncmp(file, dp->d_name, strlen(file)) == 0) {
    192 			char *tcp;
    193 
    194 			tcp = strdup(dp->d_name);
    195 			if (tcp == NULL)
    196 				errx(1, "Can't allocate memory for local dir");
    197 			sl_add(words, tcp);
    198 		}
    199 	}
    200 	closedir(dd);
    201 
    202 	rv = complete_ambiguous(file, list, words);
    203 	sl_free(words, 1);
    204 	return rv;
    205 }
    206 
    207 /*
    208  * Complete a remote file
    209  */
    210 static unsigned char
    211 complete_remote(word, list)
    212 	char *word;
    213 	int list;
    214 {
    215 	static StringList *dirlist;
    216 	static char	 lastdir[MAXPATHLEN + 1];
    217 	static int	 ftpdslashbug;
    218 	StringList	*words;
    219 	char		 dir[MAXPATHLEN + 1];
    220 	char		*file, *cp;
    221 	int		 i, offset;
    222 	unsigned char	 rv;
    223 
    224 	char *dummyargv[] = { "complete", dir, NULL };
    225 
    226 	offset = 0;
    227 	if ((file = strrchr(word, '/')) == NULL) {
    228 		strcpy(dir, ".");
    229 		file = word;
    230 	} else {
    231 		if (file == word)
    232 			strcpy(dir, "/");
    233 		else {
    234 			offset = file - word;
    235 			strncpy(dir, word, offset);
    236 			dir[offset] = '\0';
    237 			offset++;
    238 		}
    239 		file++;
    240 	}
    241 
    242 	if (dirchange || strcmp(dir, lastdir) != 0) {	/* dir not cached */
    243 		if (dirlist != NULL)
    244 			sl_free(dirlist, 1);
    245 		dirlist = sl_init();
    246 
    247 		ftpdslashbug = 0;
    248 		mflag = 1;
    249 		while ((cp = remglob(dummyargv, 0)) != NULL) {
    250 			char *tcp;
    251 
    252 			if (!mflag)
    253 				continue;
    254 			if (*cp == '\0') {
    255 				mflag = 0;
    256 				continue;
    257 			}
    258 			/*
    259 			 * Work around ftpd(1) bug, which puts a // instead
    260 			 * of / in front of each filename returned by "NLST /".
    261 			 * Without this, remote completes of / look ugly.
    262 			 */
    263 			if (dir[0] == '/' && dir[1] == '\0' &&
    264 			    cp[0] == '/' && cp[1] == '/') {
    265 				cp++;
    266 				ftpdslashbug = 1;
    267 			}
    268 			tcp = strdup(cp);
    269 			if (tcp == NULL)
    270 				errx(1, "Can't allocate memory for remote dir");
    271 			sl_add(dirlist, tcp);
    272 		}
    273 		strcpy(lastdir, dir);
    274 		dirchange = 0;
    275 	}
    276 
    277 	words = sl_init();
    278 	for (i = 0; i < dirlist->sl_cur; i++) {
    279 		cp = dirlist->sl_str[i];
    280 		if (strlen(word) > strlen(cp))
    281 			continue;
    282 		if (strncmp(word, cp, strlen(word)) == 0)
    283 			sl_add(words, cp + offset + ftpdslashbug);
    284 	}
    285 	rv = complete_ambiguous(file, list, words);
    286 	sl_free(words, 0);
    287 	return rv;
    288 }
    289 
    290 /*
    291  * Generic complete routine
    292  */
    293 unsigned char
    294 complete(el, ch)
    295 	EditLine *el;
    296 	int ch;
    297 {
    298 	static char word[FTPBUFLEN];
    299 	static int lastc_argc, lastc_argo;
    300 
    301 	struct cmd *c;
    302 	const LineInfo *lf;
    303 	int len, celems, dolist;
    304 
    305 	lf = el_line(el);
    306 	len = lf->lastchar - lf->buffer;
    307 	if (len >= sizeof(line))
    308 		return CC_ERROR;
    309 	strncpy(line, lf->buffer, len);
    310 	line[len] = '\0';
    311 	cursor_pos = line + (lf->cursor - lf->buffer);
    312 	lastc_argc = cursor_argc;	/* remember last cursor pos */
    313 	lastc_argo = cursor_argo;
    314 	makeargv();			/* build argc/argv of current line */
    315 
    316 	if (cursor_argo >= sizeof(word))
    317 		return CC_ERROR;
    318 
    319 	dolist = 0;
    320 			/* if cursor and word is same, list alternatives */
    321 	if (lastc_argc == cursor_argc && lastc_argo == cursor_argo
    322 	    && strncmp(word, margv[cursor_argc], cursor_argo) == 0)
    323 		dolist = 1;
    324 	else
    325 	    strncpy(word, margv[cursor_argc], cursor_argo);
    326 	word[cursor_argo] = '\0';
    327 
    328 	if (cursor_argc == 0)
    329 		return complete_command(word, dolist);
    330 
    331 	c = getcmd(margv[0]);
    332 	if (c == (struct cmd *)-1 || c == 0)
    333 		return CC_ERROR;
    334 	celems = strlen(c->c_complete);
    335 
    336 		/* check for 'continuation' completes (which are uppercase) */
    337 	if ((cursor_argc > celems) && (celems > 0)
    338 	    && isupper(c->c_complete[celems-1]))
    339 		cursor_argc = celems;
    340 
    341 	if (cursor_argc > celems)
    342 		return CC_ERROR;
    343 
    344 	switch (c->c_complete[cursor_argc - 1]) {
    345 		case 'l':			/* local complete */
    346 		case 'L':
    347 			return complete_local(word, dolist);
    348 		case 'r':			/* remote complete */
    349 		case 'R':
    350 			if (!connected) {
    351 				printf("\nMust be connected to complete\n");
    352 				return CC_REDISPLAY;
    353 			}
    354 			return complete_remote(word, dolist);
    355 		case 'c':			/* command complete */
    356 		case 'C':
    357 			return complete_command(word, dolist);
    358 		case 'n':			/* no complete */
    359 		default:
    360 			return CC_ERROR;
    361 	}
    362 
    363 	return CC_ERROR;
    364 }
    365