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