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