1 /* $OpenBSD: complete.c,v 1.23 2009/05/05 19:35:30 martynas 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 if (debug) 235 (void)putc('\n', ttyout); 236 while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) { 237 char *tcp; 238 239 if (!mflag) 240 continue; 241 if (*cp == '\0') { 242 mflag = 0; 243 continue; 244 } 245 tcp = strrchr(cp, '/'); 246 if (tcp) 247 tcp++; 248 else 249 tcp = cp; 250 tcp = strdup(tcp); 251 if (tcp == NULL) 252 errx(1, "Can't allocate memory for remote dir"); 253 sl_add(dirlist, tcp); 254 } 255 if (emesg != NULL) { 256 fprintf(ttyout, "\n%s\n", emesg); 257 return (CC_REDISPLAY); 258 } 259 (void)strlcpy(lastdir, dir, sizeof lastdir); 260 dirchange = 0; 261 } 262 263 words = sl_init(); 264 for (i = 0; i < dirlist->sl_cur; i++) { 265 cp = dirlist->sl_str[i]; 266 if (strlen(file) > strlen(cp)) 267 continue; 268 if (strncmp(file, cp, strlen(file)) == 0) 269 sl_add(words, cp); 270 } 271 rv = complete_ambiguous(file, list, words); 272 sl_free(words, 0); 273 return (rv); 274 } 275 276 /* 277 * Generic complete routine 278 */ 279 unsigned char 280 complete(EditLine *el, int ch) 281 { 282 static char word[FTPBUFLEN]; 283 static int lastc_argc, lastc_argo; 284 struct cmd *c; 285 const LineInfo *lf; 286 int celems, dolist; 287 size_t len; 288 289 ch = ch; /* not used */ 290 lf = el_line(el); 291 len = lf->lastchar - lf->buffer; 292 if (len >= sizeof(line)) 293 return (CC_ERROR); 294 (void)memcpy(line, lf->buffer, len); 295 line[len] = '\0'; 296 cursor_pos = line + (lf->cursor - lf->buffer); 297 lastc_argc = cursor_argc; /* remember last cursor pos */ 298 lastc_argo = cursor_argo; 299 makeargv(); /* build argc/argv of current line */ 300 301 if (cursor_argo >= sizeof(word)) 302 return (CC_ERROR); 303 304 dolist = 0; 305 /* if cursor and word is same, list alternatives */ 306 if (lastc_argc == cursor_argc && lastc_argo == cursor_argo 307 && strncmp(word, margv[cursor_argc], cursor_argo) == 0) 308 dolist = 1; 309 else if (cursor_argo) 310 memcpy(word, margv[cursor_argc], cursor_argo); 311 word[cursor_argo] = '\0'; 312 313 if (cursor_argc == 0) 314 return (complete_command(word, dolist)); 315 316 c = getcmd(margv[0]); 317 if (c == (struct cmd *)-1 || c == 0) 318 return (CC_ERROR); 319 celems = strlen(c->c_complete); 320 321 /* check for 'continuation' completes (which are uppercase) */ 322 if ((cursor_argc > celems) && (celems > 0) 323 && isupper(c->c_complete[celems-1])) 324 cursor_argc = celems; 325 326 if (cursor_argc > celems) 327 return (CC_ERROR); 328 329 switch (c->c_complete[cursor_argc - 1]) { 330 case 'l': /* local complete */ 331 case 'L': 332 return (complete_local(word, dolist)); 333 case 'r': /* remote complete */ 334 case 'R': 335 if (connected != -1) { 336 fputs("\nMust be logged in to complete.\n", ttyout); 337 return (CC_REDISPLAY); 338 } 339 return (complete_remote(word, dolist)); 340 case 'c': /* command complete */ 341 case 'C': 342 return (complete_command(word, dolist)); 343 case 'n': /* no complete */ 344 return (CC_ERROR); 345 } 346 347 return (CC_ERROR); 348 } 349 350 #endif /* !SMALL */ 351 352