1145519Sdarrenr/* $NetBSD: complete.c,v 1.10 2009/05/20 12:53:47 lukem Exp $ */ 2145510Sdarrenr/* from NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp */ 322514Sdarrenr 453024Sguido/*- 522514Sdarrenr * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. 680486Sdarrenr * All rights reserved. 7145510Sdarrenr * 822514Sdarrenr * This code is derived from software contributed to The NetBSD Foundation 931183Speter * by Luke Mewburn. 1031183Speter * 11255332Scy * Redistribution and use in source and binary forms, with or without 1222514Sdarrenr * modification, are permitted provided that the following conditions 1322514Sdarrenr * are met: 1422514Sdarrenr * 1. Redistributions of source code must retain the above copyright 1522514Sdarrenr * notice, this list of conditions and the following disclaimer. 1622514Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright 1722514Sdarrenr * notice, this list of conditions and the following disclaimer in the 1822514Sdarrenr * documentation and/or other materials provided with the distribution. 1922514Sdarrenr * 2022514Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2122514Sdarrenr * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2222514Sdarrenr * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2322514Sdarrenr * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2422514Sdarrenr * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2522514Sdarrenr * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2622514Sdarrenr * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2722514Sdarrenr * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2822514Sdarrenr * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2922514Sdarrenr * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3022514Sdarrenr * POSSIBILITY OF SUCH DAMAGE. 3122514Sdarrenr */ 3222514Sdarrenr 3322514Sdarrenr#include "tnftp.h" 3422514Sdarrenr 3522514Sdarrenr#if 0 /* tnftp */ 3622514Sdarrenr 3722514Sdarrenr#include <sys/cdefs.h> 3822514Sdarrenr#ifndef lint 3922514Sdarrenr__RCSID(" NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp "); 4022514Sdarrenr#endif /* not lint */ 4122514Sdarrenr 4222514Sdarrenr/* 4322514Sdarrenr * FTP user program - command and file completion routines 4422514Sdarrenr */ 4522514Sdarrenr 4622514Sdarrenr#include <sys/stat.h> 4722514Sdarrenr 4831183Speter#include <ctype.h> 4922514Sdarrenr#include <err.h> 5022514Sdarrenr#include <dirent.h> 5124583Sdarrenr#include <stdio.h> 5222514Sdarrenr#include <stdlib.h> 5322514Sdarrenr#include <string.h> 5422514Sdarrenr 5522514Sdarrenr#endif /* tnftp */ 5622514Sdarrenr 5722514Sdarrenr#include "ftp_var.h" 5822514Sdarrenr 5922514Sdarrenr#ifndef NO_EDITCOMPLETE 6022514Sdarrenr 6122514Sdarrenrstatic int comparstr (const void *, const void *); 6222514Sdarrenrstatic unsigned char complete_ambiguous (char *, int, StringList *); 6322514Sdarrenrstatic unsigned char complete_command (char *, int); 6422514Sdarrenrstatic unsigned char complete_local (char *, int); 6522514Sdarrenrstatic unsigned char complete_option (char *, int); 6622514Sdarrenrstatic unsigned char complete_remote (char *, int); 6722514Sdarrenr 6822514Sdarrenrstatic int 69255332Scycomparstr(const void *a, const void *b) 70255332Scy{ 71255332Scy return (strcmp(*(const char * const *)a, *(const char * const *)b)); 7222514Sdarrenr} 7322514Sdarrenr 7422514Sdarrenr/* 7522514Sdarrenr * Determine if complete is ambiguous. If unique, insert. 7622514Sdarrenr * If no choices, error. If unambiguous prefix, insert that. 7722514Sdarrenr * Otherwise, list choices. words is assumed to be filtered 7822514Sdarrenr * to only contain possible choices. 7922514Sdarrenr * Args: 8022514Sdarrenr * word word which started the match 8122514Sdarrenr * list list by default 8222514Sdarrenr * words stringlist containing possible matches 8322514Sdarrenr * Returns a result as per el_set(EL_ADDFN, ...) 8422514Sdarrenr */ 8522514Sdarrenrstatic unsigned char 8622514Sdarrenrcomplete_ambiguous(char *word, int list, StringList *words) 8722514Sdarrenr{ 8822514Sdarrenr char insertstr[MAXPATHLEN]; 8922514Sdarrenr char *lastmatch, *p; 9022514Sdarrenr size_t i, j; 9122514Sdarrenr size_t matchlen, wordlen; 9222514Sdarrenr 9322514Sdarrenr wordlen = strlen(word); 9422514Sdarrenr if (words->sl_cur == 0) 9522514Sdarrenr return (CC_ERROR); /* no choices available */ 9622514Sdarrenr 9722514Sdarrenr if (words->sl_cur == 1) { /* only once choice available */ 9822514Sdarrenr p = words->sl_str[0] + wordlen; 9922514Sdarrenr if (*p == '\0') /* at end of word? */ 10022514Sdarrenr return (CC_REFRESH); 10122514Sdarrenr ftpvis(insertstr, sizeof(insertstr), p, strlen(p)); 10222514Sdarrenr if (el_insertstr(el, insertstr) == -1) 10322514Sdarrenr return (CC_ERROR); 10422514Sdarrenr else 10522514Sdarrenr return (CC_REFRESH); 10622514Sdarrenr } 10722514Sdarrenr 10822514Sdarrenr if (!list) { 10922514Sdarrenr matchlen = 0; 11024583Sdarrenr lastmatch = words->sl_str[0]; 11122514Sdarrenr matchlen = strlen(lastmatch); 11222514Sdarrenr for (i = 1 ; i < words->sl_cur ; i++) { 11322514Sdarrenr for (j = wordlen ; j < strlen(words->sl_str[i]); j++) 11422514Sdarrenr if (lastmatch[j] != words->sl_str[i][j]) 11522514Sdarrenr break; 11624583Sdarrenr if (j < matchlen) 11722514Sdarrenr matchlen = j; 11822514Sdarrenr } 11922514Sdarrenr if (matchlen > wordlen) { 12022514Sdarrenr ftpvis(insertstr, sizeof(insertstr), 12122514Sdarrenr lastmatch + wordlen, matchlen - wordlen); 12222514Sdarrenr if (el_insertstr(el, insertstr) == -1) 12324583Sdarrenr return (CC_ERROR); 12422514Sdarrenr else 12522514Sdarrenr return (CC_REFRESH_BEEP); 12622514Sdarrenr } 12722514Sdarrenr } 12822514Sdarrenr 12922514Sdarrenr putc('\n', ttyout); 13022514Sdarrenr qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr); 13122514Sdarrenr list_vertical(words); 13224583Sdarrenr return (CC_REDISPLAY); 13322514Sdarrenr} 13422514Sdarrenr 13522514Sdarrenr/* 13622514Sdarrenr * Complete a command 13722514Sdarrenr */ 13822514Sdarrenrstatic unsigned char 13922514Sdarrenrcomplete_command(char *word, int list) 14022514Sdarrenr{ 14122514Sdarrenr struct cmd *c; 14222514Sdarrenr StringList *words; 14322514Sdarrenr size_t wordlen; 14422514Sdarrenr unsigned char rv; 14522514Sdarrenr 14622514Sdarrenr words = ftp_sl_init(); 14722514Sdarrenr wordlen = strlen(word); 14822514Sdarrenr 14922514Sdarrenr for (c = cmdtab; c->c_name != NULL; c++) { 15022514Sdarrenr if (wordlen > strlen(c->c_name)) 15122514Sdarrenr continue; 15222514Sdarrenr if (strncmp(word, c->c_name, wordlen) == 0) 153255332Scy ftp_sl_add(words, ftp_strdup(c->c_name)); 154255332Scy } 15522514Sdarrenr 15622514Sdarrenr rv = complete_ambiguous(word, list, words); 15722514Sdarrenr if (rv == CC_REFRESH) { 15822514Sdarrenr if (el_insertstr(el, " ") == -1) 15922514Sdarrenr rv = CC_ERROR; 16022514Sdarrenr } 16122514Sdarrenr sl_free(words, 1); 16222514Sdarrenr return (rv); 16322514Sdarrenr} 16422514Sdarrenr 16522514Sdarrenr/* 16622514Sdarrenr * Complete a local file 16724583Sdarrenr */ 16822514Sdarrenrstatic unsigned char 16922514Sdarrenrcomplete_local(char *word, int list) 17022514Sdarrenr{ 17122514Sdarrenr StringList *words; 17222514Sdarrenr char dir[MAXPATHLEN]; 17322514Sdarrenr char *file; 17424583Sdarrenr DIR *dd; 17522514Sdarrenr struct dirent *dp; 17622514Sdarrenr unsigned char rv; 17722514Sdarrenr size_t len; 17822514Sdarrenr 17922514Sdarrenr if ((file = strrchr(word, '/')) == NULL) { 18022514Sdarrenr dir[0] = '.'; 18122514Sdarrenr dir[1] = '\0'; 18224583Sdarrenr file = word; 18322514Sdarrenr } else { 18422514Sdarrenr if (file == word) { 18522514Sdarrenr dir[0] = '/'; 18622514Sdarrenr dir[1] = '\0'; 18722514Sdarrenr } else 18822514Sdarrenr (void)strlcpy(dir, word, file - word + 1); 18922514Sdarrenr file++; 19022514Sdarrenr } 19131183Speter if (dir[0] == '~') { 192255332Scy char *p; 193255332Scy 194255332Scy if ((p = globulize(dir)) == NULL) 195255332Scy return (CC_ERROR); 19622514Sdarrenr (void)strlcpy(dir, p, sizeof(dir)); 19722514Sdarrenr free(p); 19822514Sdarrenr } 19922514Sdarrenr 20022514Sdarrenr if ((dd = opendir(dir)) == NULL) 20122514Sdarrenr return (CC_ERROR); 20222514Sdarrenr 20322514Sdarrenr words = ftp_sl_init(); 20422514Sdarrenr len = strlen(file); 20522514Sdarrenr 20622514Sdarrenr for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { 20722514Sdarrenr if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 20822514Sdarrenr continue; 20922514Sdarrenr 21022514Sdarrenr#if defined(DIRENT_MISSING_D_NAMLEN) 21122514Sdarrenr if (len > strlen(dp->d_name)) 21222514Sdarrenr continue; 21322514Sdarrenr#else 21422514Sdarrenr if (len > dp->d_namlen) 21522514Sdarrenr continue; 21622514Sdarrenr#endif 21722514Sdarrenr if (strncmp(file, dp->d_name, len) == 0) { 21822514Sdarrenr char *tcp; 21922514Sdarrenr 22022514Sdarrenr tcp = ftp_strdup(dp->d_name); 22122514Sdarrenr ftp_sl_add(words, tcp); 22222514Sdarrenr } 22322514Sdarrenr } 22422514Sdarrenr closedir(dd); 22522514Sdarrenr 22622514Sdarrenr rv = complete_ambiguous(file, list, words); 22722514Sdarrenr if (rv == CC_REFRESH) { 22822514Sdarrenr struct stat sb; 229145510Sdarrenr char path[MAXPATHLEN]; 230161357Sguido 231161357Sguido (void)strlcpy(path, dir, sizeof(path)); 23222514Sdarrenr (void)strlcat(path, "/", sizeof(path)); 23322514Sdarrenr (void)strlcat(path, words->sl_str[0], sizeof(path)); 23422514Sdarrenr 23522514Sdarrenr if (stat(path, &sb) >= 0) { 23622514Sdarrenr char suffix[2] = " "; 23722514Sdarrenr 23822514Sdarrenr if (S_ISDIR(sb.st_mode)) 23922514Sdarrenr suffix[0] = '/'; 24022514Sdarrenr if (el_insertstr(el, suffix) == -1) 24122514Sdarrenr rv = CC_ERROR; 24222514Sdarrenr } 24322514Sdarrenr } 24422514Sdarrenr sl_free(words, 1); 24522514Sdarrenr return (rv); 24622514Sdarrenr} 24724583Sdarrenr/* 24822514Sdarrenr * Complete an option 24922514Sdarrenr */ 25022514Sdarrenrstatic unsigned char 25131183Spetercomplete_option(char *word, int list) 25222514Sdarrenr{ 25331183Speter struct option *o; 25422514Sdarrenr StringList *words; 25522514Sdarrenr size_t wordlen; 25622514Sdarrenr unsigned char rv; 25722514Sdarrenr 25822514Sdarrenr words = ftp_sl_init(); 25922514Sdarrenr wordlen = strlen(word); 260 261 for (o = optiontab; o->name != NULL; o++) { 262 if (wordlen > strlen(o->name)) 263 continue; 264 if (strncmp(word, o->name, wordlen) == 0) 265 ftp_sl_add(words, ftp_strdup(o->name)); 266 } 267 268 rv = complete_ambiguous(word, list, words); 269 if (rv == CC_REFRESH) { 270 if (el_insertstr(el, " ") == -1) 271 rv = CC_ERROR; 272 } 273 sl_free(words, 1); 274 return (rv); 275} 276 277/* 278 * Complete a remote file 279 */ 280static unsigned char 281complete_remote(char *word, int list) 282{ 283 static StringList *dirlist; 284 static char lastdir[MAXPATHLEN]; 285 StringList *words; 286 char dir[MAXPATHLEN]; 287 char *file, *cp; 288 size_t i; 289 unsigned char rv; 290 char cmdbuf[MAX_C_NAME]; 291 char *dummyargv[3] = { NULL, NULL, NULL }; 292 293 (void)strlcpy(cmdbuf, "complete", sizeof(cmdbuf)); 294 dummyargv[0] = cmdbuf; 295 dummyargv[1] = dir; 296 297 if ((file = strrchr(word, '/')) == NULL) { 298 dir[0] = '\0'; 299 file = word; 300 } else { 301 cp = file; 302 while (*cp == '/' && cp > word) 303 cp--; 304 (void)strlcpy(dir, word, cp - word + 2); 305 file++; 306 } 307 308 if (dirchange || dirlist == NULL || 309 strcmp(dir, lastdir) != 0) { /* dir not cached */ 310 const char *emesg; 311 312 if (dirlist != NULL) 313 sl_free(dirlist, 1); 314 dirlist = ftp_sl_init(); 315 316 mflag = 1; 317 emesg = NULL; 318 while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) { 319 char *tcp; 320 321 if (!mflag) 322 continue; 323 if (*cp == '\0') { 324 mflag = 0; 325 continue; 326 } 327 tcp = strrchr(cp, '/'); 328 if (tcp) 329 tcp++; 330 else 331 tcp = cp; 332 tcp = ftp_strdup(tcp); 333 ftp_sl_add(dirlist, tcp); 334 } 335 if (emesg != NULL) { 336 fprintf(ttyout, "\n%s\n", emesg); 337 return (CC_REDISPLAY); 338 } 339 (void)strlcpy(lastdir, dir, sizeof(lastdir)); 340 dirchange = 0; 341 } 342 343 words = ftp_sl_init(); 344 for (i = 0; i < dirlist->sl_cur; i++) { 345 cp = dirlist->sl_str[i]; 346 if (strlen(file) > strlen(cp)) 347 continue; 348 if (strncmp(file, cp, strlen(file)) == 0) 349 ftp_sl_add(words, cp); 350 } 351 rv = complete_ambiguous(file, list, words); 352 sl_free(words, 0); 353 return (rv); 354} 355 356/* 357 * Generic complete routine 358 */ 359unsigned char 360complete(EditLine *cel, int ch) 361{ 362 static char word[FTPBUFLEN]; 363 static size_t lastc_argc, lastc_argo; 364 365 struct cmd *c; 366 const LineInfo *lf; 367 int dolist, cmpltype; 368 size_t celems, len; 369 370 lf = el_line(cel); 371 len = lf->lastchar - lf->buffer; 372 if (len >= sizeof(line)) 373 return (CC_ERROR); 374 (void)strlcpy(line, lf->buffer, len + 1); 375 cursor_pos = line + (lf->cursor - lf->buffer); 376 lastc_argc = cursor_argc; /* remember last cursor pos */ 377 lastc_argo = cursor_argo; 378 makeargv(); /* build argc/argv of current line */ 379 380 if (cursor_argo >= sizeof(word)) 381 return (CC_ERROR); 382 383 dolist = 0; 384 /* if cursor and word is same, list alternatives */ 385 if (lastc_argc == cursor_argc && lastc_argo == cursor_argo 386 && strncmp(word, margv[cursor_argc] ? margv[cursor_argc] : "", 387 cursor_argo) == 0) 388 dolist = 1; 389 else if (cursor_argc < (size_t)margc) 390 (void)strlcpy(word, margv[cursor_argc], cursor_argo + 1); 391 word[cursor_argo] = '\0'; 392 393 if (cursor_argc == 0) 394 return (complete_command(word, dolist)); 395 396 c = getcmd(margv[0]); 397 if (c == (struct cmd *)-1 || c == 0) 398 return (CC_ERROR); 399 celems = strlen(c->c_complete); 400 401 /* check for 'continuation' completes (which are uppercase) */ 402 if ((cursor_argc > celems) && (celems > 0) 403 && isupper((unsigned char) c->c_complete[celems-1])) 404 cursor_argc = celems; 405 406 if (cursor_argc > celems) 407 return (CC_ERROR); 408 409 cmpltype = c->c_complete[cursor_argc - 1]; 410 switch (cmpltype) { 411 case 'c': /* command complete */ 412 case 'C': 413 return (complete_command(word, dolist)); 414 case 'l': /* local complete */ 415 case 'L': 416 return (complete_local(word, dolist)); 417 case 'n': /* no complete */ 418 case 'N': /* no complete */ 419 return (CC_ERROR); 420 case 'o': /* local complete */ 421 case 'O': 422 return (complete_option(word, dolist)); 423 case 'r': /* remote complete */ 424 case 'R': 425 if (connected != -1) { 426 fputs("\nMust be logged in to complete.\n", 427 ttyout); 428 return (CC_REDISPLAY); 429 } 430 return (complete_remote(word, dolist)); 431 default: 432 errx(1, "complete: unknown complete type `%c'", 433 cmpltype); 434 return (CC_ERROR); 435 } 436 /* NOTREACHED */ 437} 438 439#endif /* !NO_EDITCOMPLETE */ 440