165793Smsmith/* $NetBSD: complete.c,v 1.10 2009/05/20 12:53:47 lukem Exp $ */ 265793Smsmith/* from NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp */ 381082Sscottl 465793Smsmith/*- 581082Sscottl * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. 665793Smsmith * All rights reserved. 765793Smsmith * 865793Smsmith * This code is derived from software contributed to The NetBSD Foundation 965793Smsmith * by Luke Mewburn. 1065793Smsmith * 1165793Smsmith * Redistribution and use in source and binary forms, with or without 1265793Smsmith * modification, are permitted provided that the following conditions 1365793Smsmith * are met: 1465793Smsmith * 1. Redistributions of source code must retain the above copyright 1565793Smsmith * notice, this list of conditions and the following disclaimer. 1665793Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1765793Smsmith * notice, this list of conditions and the following disclaimer in the 1865793Smsmith * documentation and/or other materials provided with the distribution. 1965793Smsmith * 2065793Smsmith * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2165793Smsmith * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2265793Smsmith * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2365793Smsmith * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2465793Smsmith * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2565793Smsmith * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2665793Smsmith * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2765793Smsmith * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2865793Smsmith * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2965793Smsmith * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30119418Sobrien * POSSIBILITY OF SUCH DAMAGE. 31119418Sobrien */ 32119418Sobrien 3381151Sscottl#include "tnftp.h" 3481151Sscottl 3565793Smsmith#if 0 /* tnftp */ 3665793Smsmith 3765793Smsmith#include <sys/cdefs.h> 38129879Sphk#ifndef lint 3965793Smsmith__RCSID(" NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp "); 4065793Smsmith#endif /* not lint */ 4165793Smsmith 4265793Smsmith/* 4365793Smsmith * FTP user program - command and file completion routines 4482527Sscottl */ 4582527Sscottl 4682527Sscottl#include <sys/stat.h> 4782527Sscottl 4865793Smsmith#include <ctype.h> 4965793Smsmith#include <err.h> 5065793Smsmith#include <dirent.h> 5165793Smsmith#include <stdio.h> 52138635Sscottl#include <stdlib.h> 5365793Smsmith#include <string.h> 5465793Smsmith 5565793Smsmith#endif /* tnftp */ 5665793Smsmith 5765793Smsmith#include "ftp_var.h" 5865793Smsmith 5965793Smsmith#ifndef NO_EDITCOMPLETE 6065793Smsmith 6165793Smsmithstatic int comparstr (const void *, const void *); 6265793Smsmithstatic unsigned char complete_ambiguous (char *, int, StringList *); 6365793Smsmithstatic unsigned char complete_command (char *, int); 6465793Smsmithstatic unsigned char complete_local (char *, int); 65111525Sscottlstatic unsigned char complete_option (char *, int); 66111525Sscottlstatic unsigned char complete_remote (char *, int); 67111525Sscottl 68111220Sphkstatic int 6965793Smsmithcomparstr(const void *a, const void *b) 7089112Smsmith{ 7165793Smsmith return (strcmp(*(const char * const *)a, *(const char * const *)b)); 7265793Smsmith} 7383114Sscottl 7483114Sscottl/* 7583114Sscottl * Determine if complete is ambiguous. If unique, insert. 76247570Smarius * If no choices, error. If unambiguous prefix, insert that. 7765793Smsmith * Otherwise, list choices. words is assumed to be filtered 7865793Smsmith * to only contain possible choices. 7965793Smsmith * Args: 8083114Sscottl * word word which started the match 8183114Sscottl * list list by default 8283114Sscottl * words stringlist containing possible matches 8365793Smsmith * Returns a result as per el_set(EL_ADDFN, ...) 8465793Smsmith */ 85247570Smariusstatic unsigned char 8665793Smsmithcomplete_ambiguous(char *word, int list, StringList *words) 8783114Sscottl{ 8865793Smsmith char insertstr[MAXPATHLEN]; 8965793Smsmith char *lastmatch, *p; 90206534Semaste size_t i, j; 9165793Smsmith size_t matchlen, wordlen; 9265793Smsmith 9365793Smsmith wordlen = strlen(word); 94111525Sscottl if (words->sl_cur == 0) 9565793Smsmith return (CC_ERROR); /* no choices available */ 9683114Sscottl 9765793Smsmith if (words->sl_cur == 1) { /* only once choice available */ 98177567Semaste p = words->sl_str[0] + wordlen; 9983114Sscottl if (*p == '\0') /* at end of word? */ 100111525Sscottl return (CC_REFRESH); 10165793Smsmith ftpvis(insertstr, sizeof(insertstr), p, strlen(p)); 102109088Sscottl if (el_insertstr(el, insertstr) == -1) 103109088Sscottl return (CC_ERROR); 10483114Sscottl else 105109088Sscottl return (CC_REFRESH); 10665793Smsmith } 10783114Sscottl 108109088Sscottl if (!list) { 109212773Semaste matchlen = 0; 110212773Semaste lastmatch = words->sl_str[0]; 111212773Semaste matchlen = strlen(lastmatch); 11283114Sscottl for (i = 1 ; i < words->sl_cur ; i++) { 113109088Sscottl for (j = wordlen ; j < strlen(words->sl_str[i]); j++) 11465793Smsmith if (lastmatch[j] != words->sl_str[i][j]) 11583114Sscottl break; 11683114Sscottl if (j < matchlen) 11765793Smsmith matchlen = j; 11865793Smsmith } 11983114Sscottl if (matchlen > wordlen) { 12065793Smsmith ftpvis(insertstr, sizeof(insertstr), 12165793Smsmith lastmatch + wordlen, matchlen - wordlen); 12265793Smsmith if (el_insertstr(el, insertstr) == -1) 123111525Sscottl return (CC_ERROR); 12465793Smsmith else 12583114Sscottl return (CC_REFRESH_BEEP); 12665793Smsmith } 127177567Semaste } 12883114Sscottl 129111525Sscottl putc('\n', ttyout); 13065793Smsmith qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr); 13183114Sscottl list_vertical(words); 13283114Sscottl return (CC_REDISPLAY); 13365793Smsmith} 13483114Sscottl 13583114Sscottl/* 13665793Smsmith * Complete a command 13765793Smsmith */ 13883114Sscottlstatic unsigned char 13965793Smsmithcomplete_command(char *word, int list) 14065793Smsmith{ 14165793Smsmith struct cmd *c; 14265793Smsmith StringList *words; 14365793Smsmith size_t wordlen; 14483114Sscottl unsigned char rv; 14565793Smsmith 146111525Sscottl words = ftp_sl_init(); 147177567Semaste wordlen = strlen(word); 14865793Smsmith 14983114Sscottl for (c = cmdtab; c->c_name != NULL; c++) { 15083114Sscottl if (wordlen > strlen(c->c_name)) 15183114Sscottl continue; 15283114Sscottl if (strncmp(word, c->c_name, wordlen) == 0) 15383114Sscottl ftp_sl_add(words, ftp_strdup(c->c_name)); 15483114Sscottl } 15583114Sscottl 15682527Sscottl rv = complete_ambiguous(word, list, words); 15783114Sscottl if (rv == CC_REFRESH) { 15883114Sscottl if (el_insertstr(el, " ") == -1) 15983114Sscottl rv = CC_ERROR; 16083114Sscottl } 16183114Sscottl sl_free(words, 1); 16283114Sscottl return (rv); 16365793Smsmith} 16483114Sscottl 16583114Sscottl/* 16683114Sscottl * Complete a local file 167133540Sscottl */ 16883114Sscottlstatic unsigned char 169133540Sscottlcomplete_local(char *word, int list) 17065793Smsmith{ 17165793Smsmith StringList *words; 17283114Sscottl char dir[MAXPATHLEN]; 17395350Sscottl char *file; 17495350Sscottl DIR *dd; 17595350Sscottl struct dirent *dp; 17695350Sscottl unsigned char rv; 17795350Sscottl size_t len; 17895350Sscottl 17995350Sscottl if ((file = strrchr(word, '/')) == NULL) { 18095350Sscottl dir[0] = '.'; 18195350Sscottl dir[1] = '\0'; 18295350Sscottl file = word; 18395350Sscottl } else { 18495350Sscottl if (file == word) { 18595350Sscottl dir[0] = '/'; 18695350Sscottl dir[1] = '\0'; 18795350Sscottl } else 18895350Sscottl (void)strlcpy(dir, word, file - word + 1); 18995350Sscottl file++; 190116553Sscottl } 191116553Sscottl if (dir[0] == '~') { 19295350Sscottl char *p; 19395350Sscottl 19495350Sscottl if ((p = globulize(dir)) == NULL) 19595350Sscottl return (CC_ERROR); 19695350Sscottl (void)strlcpy(dir, p, sizeof(dir)); 19795350Sscottl free(p); 19895350Sscottl } 19995350Sscottl 200177899Semaste if ((dd = opendir(dir)) == NULL) 201177899Semaste return (CC_ERROR); 202177899Semaste 203177899Semaste words = ftp_sl_init(); 204177899Semaste len = strlen(file); 205177899Semaste 206177899Semaste for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { 207177899Semaste if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 208177899Semaste continue; 209177899Semaste 210177899Semaste#if defined(DIRENT_MISSING_D_NAMLEN) 211177899Semaste if (len > strlen(dp->d_name)) 212177899Semaste continue; 213177899Semaste#else 214177899Semaste if (len > dp->d_namlen) 215177899Semaste continue; 216177899Semaste#endif 217177899Semaste if (strncmp(file, dp->d_name, len) == 0) { 218177899Semaste char *tcp; 219177899Semaste 220177899Semaste tcp = ftp_strdup(dp->d_name); 221177899Semaste ftp_sl_add(words, tcp); 222177899Semaste } 223177899Semaste } 224177899Semaste closedir(dd); 22582527Sscottl 22682527Sscottl rv = complete_ambiguous(file, list, words); 227195614Sjkim if (rv == CC_REFRESH) { 22882527Sscottl struct stat sb; 22982527Sscottl char path[MAXPATHLEN]; 230111220Sphk 23182527Sscottl (void)strlcpy(path, dir, sizeof(path)); 23283114Sscottl (void)strlcat(path, "/", sizeof(path)); 23383114Sscottl (void)strlcat(path, words->sl_str[0], sizeof(path)); 23495350Sscottl 235195614Sjkim if (stat(path, &sb) >= 0) { 23695350Sscottl char suffix[2] = " "; 23795350Sscottl 23895350Sscottl if (S_ISDIR(sb.st_mode)) 239111220Sphk suffix[0] = '/'; 240177899Semaste if (el_insertstr(el, suffix) == -1) 241177899Semaste rv = CC_ERROR; 24282527Sscottl } 243111220Sphk } 244111525Sscottl sl_free(words, 1); 24582527Sscottl return (rv); 24683114Sscottl} 24795350Sscottl/* 24882527Sscottl * Complete an option 24983114Sscottl */ 25082527Sscottlstatic unsigned char 25195350Sscottlcomplete_option(char *word, int list) 25295350Sscottl{ 25395350Sscottl struct option *o; 254212773Semaste StringList *words; 255212773Semaste size_t wordlen; 25695350Sscottl unsigned char rv; 25795350Sscottl 25895350Sscottl words = ftp_sl_init(); 25982527Sscottl wordlen = strlen(word); 260130006Sscottl 261130006Sscottl for (o = optiontab; o->name != NULL; o++) { 26282527Sscottl if (wordlen > strlen(o->name)) 26395350Sscottl continue; 264195614Sjkim if (strncmp(word, o->name, wordlen) == 0) 265195614Sjkim ftp_sl_add(words, ftp_strdup(o->name)); 266177899Semaste } 267177899Semaste 268177899Semaste rv = complete_ambiguous(word, list, words); 269177899Semaste if (rv == CC_REFRESH) { 270177899Semaste if (el_insertstr(el, " ") == -1) 271177899Semaste rv = CC_ERROR; 272177899Semaste } 273177899Semaste sl_free(words, 1); 274177899Semaste return (rv); 275177899Semaste} 276177899Semaste 277177899Semaste/* 278177899Semaste * Complete a remote file 279177899Semaste */ 280177899Semastestatic unsigned char 281177899Semastecomplete_remote(char *word, int list) 282177899Semaste{ 283177899Semaste static StringList *dirlist; 284177899Semaste static char lastdir[MAXPATHLEN]; 285177899Semaste StringList *words; 286177899Semaste char dir[MAXPATHLEN]; 287177899Semaste char *file, *cp; 288177899Semaste size_t i; 289177899Semaste unsigned char rv; 290116553Sscottl char cmdbuf[MAX_C_NAME]; 291116553Sscottl char *dummyargv[3] = { NULL, NULL, NULL }; 292116553Sscottl 293116553Sscottl (void)strlcpy(cmdbuf, "complete", sizeof(cmdbuf)); 294116553Sscottl dummyargv[0] = cmdbuf; 295116553Sscottl dummyargv[1] = dir; 296116553Sscottl 297116553Sscottl if ((file = strrchr(word, '/')) == NULL) { 298177899Semaste dir[0] = '\0'; 299145811Sscottl file = word; 300116553Sscottl } else { 30195350Sscottl cp = file; 30295350Sscottl while (*cp == '/' && cp > word) 30382527Sscottl cp--; 30495350Sscottl (void)strlcpy(dir, word, cp - word + 2); 305177899Semaste file++; 30683114Sscottl } 307177899Semaste 308212773Semaste if (dirchange || dirlist == NULL || 309212773Semaste strcmp(dir, lastdir) != 0) { /* dir not cached */ 310212773Semaste const char *emesg; 31195350Sscottl 31283114Sscottl if (dirlist != NULL) 313116553Sscottl sl_free(dirlist, 1); 314145811Sscottl dirlist = ftp_sl_init(); 315145811Sscottl 316145811Sscottl mflag = 1; 317145811Sscottl emesg = NULL; 318145811Sscottl while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) { 31995350Sscottl char *tcp; 32095350Sscottl 321132771Skan if (!mflag) 32283114Sscottl continue; 32382527Sscottl if (*cp == '\0') { 32483114Sscottl mflag = 0; 32582527Sscottl continue; 32682527Sscottl } 32783114Sscottl tcp = strrchr(cp, '/'); 32865793Smsmith if (tcp) 32965793Smsmith tcp++; 33065793Smsmith else 33170393Smsmith tcp = cp; 33265793Smsmith tcp = ftp_strdup(tcp); 333177567Semaste ftp_sl_add(dirlist, tcp); 33483114Sscottl } 335238601Ssbruno if (emesg != NULL) { 336238601Ssbruno fprintf(ttyout, "\n%s\n", emesg); 337103675Sphk return (CC_REDISPLAY); 338238601Ssbruno } 339111691Sscottl (void)strlcpy(lastdir, dir, sizeof(lastdir)); 34083114Sscottl dirchange = 0; 34165793Smsmith } 34265793Smsmith 34383114Sscottl words = ftp_sl_init(); 34465793Smsmith for (i = 0; i < dirlist->sl_cur; i++) { 34565793Smsmith cp = dirlist->sl_str[i]; 34665793Smsmith if (strlen(file) > strlen(cp)) 34765793Smsmith continue; 34865793Smsmith if (strncmp(file, cp, strlen(file)) == 0) 34965793Smsmith ftp_sl_add(words, cp); 350177567Semaste } 35165793Smsmith rv = complete_ambiguous(file, list, words); 35283114Sscottl sl_free(words, 0); 35365793Smsmith return (rv); 35465793Smsmith} 35583114Sscottl 35665793Smsmith/* 35765793Smsmith * Generic complete routine 35865793Smsmith */ 35965793Smsmithunsigned char 36065793Smsmithcomplete(EditLine *cel, int ch) 36183114Sscottl{ 36283114Sscottl static char word[FTPBUFLEN]; 36383114Sscottl static size_t lastc_argc, lastc_argo; 364177567Semaste 36565793Smsmith struct cmd *c; 36683114Sscottl const LineInfo *lf; 36783114Sscottl int dolist, cmpltype; 36883114Sscottl size_t celems, len; 36983114Sscottl 37083114Sscottl lf = el_line(cel); 37165793Smsmith len = lf->lastchar - lf->buffer; 37283114Sscottl if (len >= sizeof(line)) 37383114Sscottl return (CC_ERROR); 37483114Sscottl (void)strlcpy(line, lf->buffer, len + 1); 37583114Sscottl cursor_pos = line + (lf->cursor - lf->buffer); 37683114Sscottl lastc_argc = cursor_argc; /* remember last cursor pos */ 377177619Semaste lastc_argo = cursor_argo; 378177619Semaste makeargv(); /* build argc/argv of current line */ 379177619Semaste 38083114Sscottl if (cursor_argo >= sizeof(word)) 38183114Sscottl return (CC_ERROR); 38283114Sscottl 38383114Sscottl dolist = 0; 38483114Sscottl /* if cursor and word is same, list alternatives */ 38583114Sscottl if (lastc_argc == cursor_argc && lastc_argo == cursor_argo 38683114Sscottl && strncmp(word, margv[cursor_argc] ? margv[cursor_argc] : "", 38783114Sscottl cursor_argo) == 0) 38883114Sscottl dolist = 1; 38983114Sscottl else if (cursor_argc < (size_t)margc) 39083114Sscottl (void)strlcpy(word, margv[cursor_argc], cursor_argo + 1); 39165793Smsmith word[cursor_argo] = '\0'; 392177619Semaste 393177619Semaste if (cursor_argc == 0) 394177619Semaste return (complete_command(word, dolist)); 39565793Smsmith 39683114Sscottl c = getcmd(margv[0]); 39783114Sscottl if (c == (struct cmd *)-1 || c == 0) 398125975Sphk return (CC_ERROR); 399125975Sphk celems = strlen(c->c_complete); 400251116Smarius 401125975Sphk /* check for 'continuation' completes (which are uppercase) */ 402195614Sjkim if ((cursor_argc > celems) && (celems > 0) 403125975Sphk && isupper((unsigned char) c->c_complete[celems-1])) 404125975Sphk cursor_argc = celems; 405125975Sphk 406125975Sphk if (cursor_argc > celems) 407125975Sphk return (CC_ERROR); 408125975Sphk 409125975Sphk cmpltype = c->c_complete[cursor_argc - 1]; 410125975Sphk switch (cmpltype) { 411125975Sphk case 'c': /* command complete */ 412125975Sphk case 'C': 41381082Sscottl return (complete_command(word, dolist)); 41483114Sscottl case 'l': /* local complete */ 41565793Smsmith case 'L': 41665793Smsmith return (complete_local(word, dolist)); 41783114Sscottl case 'n': /* no complete */ 41865793Smsmith case 'N': /* no complete */ 41965793Smsmith return (CC_ERROR); 42065793Smsmith case 'o': /* local complete */ 42165793Smsmith case 'O': 42265793Smsmith return (complete_option(word, dolist)); 42383114Sscottl case 'r': /* remote complete */ 42465793Smsmith case 'R': 42583114Sscottl if (connected != -1) { 426177567Semaste fputs("\nMust be logged in to complete.\n", 42765793Smsmith ttyout); 42883114Sscottl return (CC_REDISPLAY); 42983114Sscottl } 43083114Sscottl return (complete_remote(word, dolist)); 431125975Sphk default: 43265793Smsmith errx(1, "complete: unknown complete type `%c'", 43383114Sscottl cmpltype); 43465793Smsmith return (CC_ERROR); 435 } 436 /* NOTREACHED */ 437} 438 439#endif /* !NO_EDITCOMPLETE */ 440