1 /* $NetBSD: complete.c,v 1.8 1997/05/24 16:34:30 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 SMALL 40 #ifndef lint 41 static char rcsid[] = "$NetBSD: complete.c,v 1.8 1997/05/24 16:34:30 lukem Exp $"; 42 #endif /* not lint */ 43 44 /* 45 * FTP user program - command and file completion routines 46 */ 47 48 #include <ctype.h> 49 #include <err.h> 50 #include <dirent.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 55 #include "ftp_var.h" 56 57 static int 58 comparstr(a, b) 59 const void *a, *b; 60 { 61 return (strcmp(*(char **)a, *(char **)b)); 62 } 63 64 /* 65 * Determine if complete is ambiguous. If unique, insert. 66 * If no choices, error. If unambiguous prefix, insert that. 67 * Otherwise, list choices. words is assumed to be filtered 68 * to only contain possible choices. 69 * Args: 70 * word word which started the match 71 * list list by default 72 * words stringlist containing possible matches 73 */ 74 static unsigned char 75 complete_ambiguous(word, list, words) 76 char *word; 77 int list; 78 StringList *words; 79 { 80 char insertstr[MAXPATHLEN]; 81 char *lastmatch; 82 int i, j, matchlen, wordlen; 83 84 wordlen = strlen(word); 85 if (words->sl_cur == 0) 86 return (CC_ERROR); /* no choices available */ 87 88 if (words->sl_cur == 1) { /* only once choice available */ 89 (void)strcpy(insertstr, words->sl_str[0]); 90 if (el_insertstr(el, insertstr + wordlen) == -1) 91 return (CC_ERROR); 92 else 93 return (CC_REFRESH); 94 } 95 96 if (!list) { 97 matchlen = 0; 98 lastmatch = words->sl_str[0]; 99 matchlen = strlen(lastmatch); 100 for (i = 1 ; i < words->sl_cur ; i++) { 101 for (j = wordlen ; j < strlen(words->sl_str[i]); j++) 102 if (lastmatch[j] != words->sl_str[i][j]) 103 break; 104 if (j < matchlen) 105 matchlen = j; 106 } 107 if (matchlen > wordlen) { 108 (void)strncpy(insertstr, lastmatch, matchlen); 109 insertstr[matchlen] = '\0'; 110 if (el_insertstr(el, insertstr + wordlen) == -1) 111 return (CC_ERROR); 112 else 113 /* 114 * XXX: really want CC_REFRESH_BEEP 115 */ 116 return (CC_REFRESH); 117 } 118 } 119 120 putchar('\n'); 121 qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr); 122 list_vertical(words); 123 return (CC_REDISPLAY); 124 } 125 126 /* 127 * Complete a command 128 */ 129 static unsigned char 130 complete_command(word, list) 131 char *word; 132 int list; 133 { 134 struct cmd *c; 135 StringList *words; 136 int wordlen; 137 unsigned char rv; 138 139 words = sl_init(); 140 wordlen = strlen(word); 141 142 for (c = cmdtab; c->c_name != NULL; c++) { 143 if (wordlen > strlen(c->c_name)) 144 continue; 145 if (strncmp(word, c->c_name, wordlen) == 0) 146 sl_add(words, c->c_name); 147 } 148 149 rv = complete_ambiguous(word, list, words); 150 sl_free(words, 0); 151 return (rv); 152 } 153 154 /* 155 * Complete a local file 156 */ 157 static unsigned char 158 complete_local(word, list) 159 char *word; 160 int list; 161 { 162 StringList *words; 163 char dir[MAXPATHLEN]; 164 char *file; 165 DIR *dd; 166 struct dirent *dp; 167 unsigned char rv; 168 169 if ((file = strrchr(word, '/')) == NULL) { 170 dir[0] = '.'; 171 dir[1] = '\0'; 172 file = word; 173 } else { 174 if (file == word) { 175 dir[0] = '/'; 176 dir[1] = '\0'; 177 } else { 178 (void)strncpy(dir, word, file - word); 179 dir[file - word] = '\0'; 180 } 181 file++; 182 } 183 184 if ((dd = opendir(dir)) == NULL) 185 return (CC_ERROR); 186 187 words = sl_init(); 188 189 for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { 190 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 191 continue; 192 if (strlen(file) > dp->d_namlen) 193 continue; 194 if (strncmp(file, dp->d_name, strlen(file)) == 0) { 195 char *tcp; 196 197 tcp = strdup(dp->d_name); 198 if (tcp == NULL) 199 errx(1, "Can't allocate memory for local dir"); 200 sl_add(words, tcp); 201 } 202 } 203 closedir(dd); 204 205 rv = complete_ambiguous(file, list, words); 206 sl_free(words, 1); 207 return (rv); 208 } 209 210 /* 211 * Complete a remote file 212 */ 213 static unsigned char 214 complete_remote(word, list) 215 char *word; 216 int list; 217 { 218 static StringList *dirlist; 219 static char lastdir[MAXPATHLEN]; 220 StringList *words; 221 char dir[MAXPATHLEN]; 222 char *file, *cp; 223 int i; 224 unsigned char rv; 225 226 char *dummyargv[] = { "complete", dir, NULL }; 227 228 if ((file = strrchr(word, '/')) == NULL) { 229 dir[0] = '.'; 230 dir[1] = '\0'; 231 file = word; 232 } else { 233 cp = file; 234 while (*cp == '/' && cp > word) 235 cp--; 236 (void)strncpy(dir, word, cp - word + 1); 237 dir[cp - word + 1] = '\0'; 238 file++; 239 } 240 241 if (dirchange || strcmp(dir, lastdir) != 0) { /* dir not cached */ 242 char *emesg; 243 244 if (dirlist != NULL) 245 sl_free(dirlist, 1); 246 dirlist = sl_init(); 247 248 mflag = 1; 249 emesg = NULL; 250 while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) { 251 char *tcp; 252 253 if (!mflag) 254 continue; 255 if (*cp == '\0') { 256 mflag = 0; 257 continue; 258 } 259 tcp = strrchr(cp, '/'); 260 if (tcp) 261 tcp++; 262 else 263 tcp = cp; 264 tcp = strdup(tcp); 265 if (tcp == NULL) 266 errx(1, "Can't allocate memory for remote dir"); 267 sl_add(dirlist, tcp); 268 } 269 if (emesg != NULL) { 270 printf("\n%s\n", emesg); 271 return (CC_REDISPLAY); 272 } 273 (void)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(file) > strlen(cp)) 281 continue; 282 if (strncmp(file, cp, strlen(file)) == 0) 283 sl_add(words, cp); 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 (void)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 (void)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 != -1) { 351 puts("\nMust be logged in to complete."); 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 #endif 366