1 /* $OpenBSD: complete.c,v 1.26 2010/04/26 16:51:59 stsp Exp $ */ 2 /* $NetBSD: complete.c,v 1.10 1997/08/18 10:20:18 lukem Exp $ */ 3 4 /*- 5 * Copyright (c) 1997 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Luke Mewburn. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #ifndef SMALL 34 35 /* 36 * FTP user program - command and file completion routines 37 */ 38 39 #include <ctype.h> 40 #include <err.h> 41 #include <dirent.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 46 #include "ftp_var.h" 47 48 static int comparstr(const void *, const void *); 49 static unsigned char complete_ambiguous(char *, int, StringList *); 50 static unsigned char complete_command(char *, int); 51 static unsigned char complete_local(char *, int); 52 static unsigned char complete_remote(char *, int); 53 static void ftpvis(char *, size_t, const char *, size_t); 54 55 static int 56 comparstr(const void *a, const void *b) 57 { 58 return (strcmp(*(char **)a, *(char **)b)); 59 } 60 61 /* 62 * Determine if complete is ambiguous. If unique, insert. 63 * If no choices, error. If unambiguous prefix, insert that. 64 * Otherwise, list choices. words is assumed to be filtered 65 * to only contain possible choices. 66 * Args: 67 * word word which started the match 68 * list list by default 69 * words stringlist containing possible matches 70 */ 71 static unsigned char 72 complete_ambiguous(char *word, int list, StringList *words) 73 { 74 char insertstr[MAXPATHLEN * 2]; 75 char *lastmatch; 76 int i, j; 77 size_t matchlen, wordlen; 78 79 wordlen = strlen(word); 80 if (words->sl_cur == 0) 81 return (CC_ERROR); /* no choices available */ 82 83 if (words->sl_cur == 1) { /* only once choice available */ 84 char *p = words->sl_str[0] + wordlen; 85 ftpvis(insertstr, sizeof(insertstr), p, strlen(p)); 86 if (el_insertstr(el, insertstr) == -1) 87 return (CC_ERROR); 88 else 89 return (CC_REFRESH); 90 } 91 92 if (!list) { 93 matchlen = 0; 94 lastmatch = words->sl_str[0]; 95 matchlen = strlen(lastmatch); 96 for (i = 1 ; i < words->sl_cur ; i++) { 97 for (j = wordlen ; j < strlen(words->sl_str[i]); j++) 98 if (lastmatch[j] != words->sl_str[i][j]) 99 break; 100 if (j < matchlen) 101 matchlen = j; 102 } 103 if (matchlen > wordlen) { 104 ftpvis(insertstr, sizeof(insertstr), 105 lastmatch + wordlen, matchlen - wordlen); 106 if (el_insertstr(el, insertstr) == -1) 107 return (CC_ERROR); 108 else 109 /* 110 * XXX: really want CC_REFRESH_BEEP 111 */ 112 return (CC_REFRESH); 113 } 114 } 115 116 putc('\n', ttyout); 117 qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr); 118 list_vertical(words); 119 return (CC_REDISPLAY); 120 } 121 122 /* 123 * Complete a command 124 */ 125 static unsigned char 126 complete_command(char *word, int list) 127 { 128 struct cmd *c; 129 StringList *words; 130 size_t wordlen; 131 unsigned char rv; 132 133 words = sl_init(); 134 wordlen = strlen(word); 135 136 for (c = cmdtab; c->c_name != NULL; c++) { 137 if (wordlen > strlen(c->c_name)) 138 continue; 139 if (strncmp(word, c->c_name, wordlen) == 0) 140 sl_add(words, c->c_name); 141 } 142 143 rv = complete_ambiguous(word, list, words); 144 sl_free(words, 0); 145 return (rv); 146 } 147 148 /* 149 * Complete a local file 150 */ 151 static unsigned char 152 complete_local(char *word, int list) 153 { 154 StringList *words; 155 char dir[MAXPATHLEN]; 156 char *file; 157 DIR *dd; 158 struct dirent *dp; 159 unsigned char rv; 160 161 if ((file = strrchr(word, '/')) == NULL) { 162 dir[0] = '.'; 163 dir[1] = '\0'; 164 file = word; 165 } else { 166 if (file == word) { 167 dir[0] = '/'; 168 dir[1] = '\0'; 169 } else { 170 (void)strlcpy(dir, word, (size_t)(file - word) + 1); 171 } 172 file++; 173 } 174 175 if ((dd = opendir(dir)) == NULL) 176 return (CC_ERROR); 177 178 words = sl_init(); 179 180 for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { 181 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 182 continue; 183 if (strlen(file) > dp->d_namlen) 184 continue; 185 if (strncmp(file, dp->d_name, strlen(file)) == 0) { 186 char *tcp; 187 188 tcp = strdup(dp->d_name); 189 if (tcp == NULL) 190 errx(1, "Can't allocate memory for local dir"); 191 sl_add(words, tcp); 192 } 193 } 194 closedir(dd); 195 196 rv = complete_ambiguous(file, list, words); 197 sl_free(words, 1); 198 return (rv); 199 } 200 201 /* 202 * Complete a remote file 203 */ 204 static unsigned char 205 complete_remote(char *word, int list) 206 { 207 static StringList *dirlist; 208 static char lastdir[MAXPATHLEN]; 209 StringList *words; 210 char dir[MAXPATHLEN]; 211 char *file, *cp; 212 int i; 213 unsigned char rv; 214 215 char *dummyargv[] = { "complete", dir, NULL }; 216 217 if ((file = strrchr(word, '/')) == NULL) { 218 dir[0] = '.'; 219 dir[1] = '\0'; 220 file = word; 221 } else { 222 cp = file; 223 while (*cp == '/' && cp > word) 224 cp--; 225 (void)strlcpy(dir, word, (size_t)(cp - word + 2)); 226 file++; 227 } 228 229 if (dirchange || strcmp(dir, lastdir) != 0) { /* dir not cached */ 230 char *emesg; 231 232 sl_free(dirlist, 1); 233 dirlist = sl_init(); 234 235 mflag = 1; 236 emesg = NULL; 237 if (debug) 238 (void)putc('\n', ttyout); 239 while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) { 240 char *tcp; 241 242 if (!mflag) 243 continue; 244 if (*cp == '\0') { 245 mflag = 0; 246 continue; 247 } 248 tcp = strrchr(cp, '/'); 249 if (tcp) 250 tcp++; 251 else 252 tcp = cp; 253 tcp = strdup(tcp); 254 if (tcp == NULL) 255 errx(1, "Can't allocate memory for remote dir"); 256 sl_add(dirlist, tcp); 257 } 258 if (emesg != NULL) { 259 fprintf(ttyout, "\n%s\n", emesg); 260 return (CC_REDISPLAY); 261 } 262 (void)strlcpy(lastdir, dir, sizeof lastdir); 263 dirchange = 0; 264 } 265 266 words = sl_init(); 267 for (i = 0; i < dirlist->sl_cur; i++) { 268 cp = dirlist->sl_str[i]; 269 if (strlen(file) > strlen(cp)) 270 continue; 271 if (strncmp(file, cp, strlen(file)) == 0) 272 sl_add(words, cp); 273 } 274 rv = complete_ambiguous(file, list, words); 275 sl_free(words, 0); 276 return (rv); 277 } 278 279 /* 280 * Generic complete routine 281 */ 282 unsigned char 283 complete(EditLine *el, int ch) 284 { 285 static char word[FTPBUFLEN]; 286 static int lastc_argc, lastc_argo; 287 struct cmd *c; 288 const LineInfo *lf; 289 int celems, dolist; 290 size_t len; 291 292 ch = ch; /* not used */ 293 lf = el_line(el); 294 len = lf->lastchar - lf->buffer; 295 if (len >= sizeof(line)) 296 return (CC_ERROR); 297 (void)memcpy(line, lf->buffer, len); 298 line[len] = '\0'; 299 cursor_pos = line + (lf->cursor - lf->buffer); 300 lastc_argc = cursor_argc; /* remember last cursor pos */ 301 lastc_argo = cursor_argo; 302 makeargv(); /* build argc/argv of current line */ 303 304 if (cursor_argo >= sizeof(word)) 305 return (CC_ERROR); 306 307 dolist = 0; 308 /* if cursor and word is same, list alternatives */ 309 if (lastc_argc == cursor_argc && lastc_argo == cursor_argo 310 && strncmp(word, margv[cursor_argc], cursor_argo) == 0) 311 dolist = 1; 312 else if (cursor_argo) 313 memcpy(word, margv[cursor_argc], cursor_argo); 314 word[cursor_argo] = '\0'; 315 316 if (cursor_argc == 0) 317 return (complete_command(word, dolist)); 318 319 c = getcmd(margv[0]); 320 if (c == (struct cmd *)-1 || c == 0) 321 return (CC_ERROR); 322 celems = strlen(c->c_complete); 323 324 /* check for 'continuation' completes (which are uppercase) */ 325 if ((cursor_argc > celems) && (celems > 0) 326 && isupper(c->c_complete[celems-1])) 327 cursor_argc = celems; 328 329 if (cursor_argc > celems) 330 return (CC_ERROR); 331 332 switch (c->c_complete[cursor_argc - 1]) { 333 case 'l': /* local complete */ 334 case 'L': 335 return (complete_local(word, dolist)); 336 case 'r': /* remote complete */ 337 case 'R': 338 if (connected != -1) { 339 fputs("\nMust be logged in to complete.\n", ttyout); 340 return (CC_REDISPLAY); 341 } 342 return (complete_remote(word, dolist)); 343 case 'c': /* command complete */ 344 case 'C': 345 return (complete_command(word, dolist)); 346 case 'n': /* no complete */ 347 return (CC_ERROR); 348 } 349 350 return (CC_ERROR); 351 } 352 353 /* 354 * Copy characters from src into dst, \ quoting characters that require it. 355 */ 356 static void 357 ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen) 358 { 359 size_t di, si; 360 361 di = si = 0; 362 while (di + 1 < dstlen && si < srclen && src[si] != '\0') { 363 switch (src[si]) { 364 case '\\': 365 case ' ': 366 case '\t': 367 case '\r': 368 case '\n': 369 case '"': 370 /* Need room for two characters and NUL, avoiding 371 * incomplete escape sequences at end of dst. */ 372 if (di + 3 >= dstlen) 373 break; 374 dst[di++] = '\\'; 375 /* FALLTHROUGH */ 376 default: 377 dst[di++] = src[si++]; 378 } 379 } 380 if (dstlen != 0) 381 dst[di] = '\0'; 382 } 383 #endif /* !SMALL */ 384