1223328Sgavin/* $NetBSD: complete.c,v 1.10 2009/05/20 12:53:47 lukem Exp $ */ 2223328Sgavin/* from NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp */ 379971Sobrien 479971Sobrien/*- 5223328Sgavin * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. 679971Sobrien * All rights reserved. 779971Sobrien * 879971Sobrien * This code is derived from software contributed to The NetBSD Foundation 979971Sobrien * by Luke Mewburn. 1079971Sobrien * 1179971Sobrien * Redistribution and use in source and binary forms, with or without 1279971Sobrien * modification, are permitted provided that the following conditions 1379971Sobrien * are met: 1479971Sobrien * 1. Redistributions of source code must retain the above copyright 1579971Sobrien * notice, this list of conditions and the following disclaimer. 1679971Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1779971Sobrien * notice, this list of conditions and the following disclaimer in the 1879971Sobrien * documentation and/or other materials provided with the distribution. 1979971Sobrien * 2079971Sobrien * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2179971Sobrien * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2279971Sobrien * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2379971Sobrien * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2479971Sobrien * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2579971Sobrien * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2679971Sobrien * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2779971Sobrien * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2879971Sobrien * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2979971Sobrien * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3079971Sobrien * POSSIBILITY OF SUCH DAMAGE. 3179971Sobrien */ 3279971Sobrien 33223328Sgavin#include "tnftp.h" 34223328Sgavin 35223328Sgavin#if 0 /* tnftp */ 36223328Sgavin 37116424Smikeh#include <sys/cdefs.h> 38116424Smikeh#ifndef lint 39223328Sgavin__RCSID(" NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp "); 40116424Smikeh#endif /* not lint */ 41116424Smikeh 4279971Sobrien/* 4379971Sobrien * FTP user program - command and file completion routines 4479971Sobrien */ 4579971Sobrien 46116424Smikeh#include <sys/stat.h> 4779971Sobrien 48116424Smikeh#include <ctype.h> 49116424Smikeh#include <err.h> 50116424Smikeh#include <dirent.h> 51116424Smikeh#include <stdio.h> 52116424Smikeh#include <stdlib.h> 53116424Smikeh#include <string.h> 54116424Smikeh 55223328Sgavin#endif /* tnftp */ 56223328Sgavin 5779971Sobrien#include "ftp_var.h" 5879971Sobrien 5979971Sobrien#ifndef NO_EDITCOMPLETE 6079971Sobrien 6179971Sobrienstatic int comparstr (const void *, const void *); 6279971Sobrienstatic unsigned char complete_ambiguous (char *, int, StringList *); 6379971Sobrienstatic unsigned char complete_command (char *, int); 6479971Sobrienstatic unsigned char complete_local (char *, int); 6579971Sobrienstatic unsigned char complete_option (char *, int); 6679971Sobrienstatic unsigned char complete_remote (char *, int); 6779971Sobrien 6879971Sobrienstatic int 6979971Sobriencomparstr(const void *a, const void *b) 7079971Sobrien{ 71223328Sgavin return (strcmp(*(const char * const *)a, *(const char * const *)b)); 7279971Sobrien} 7379971Sobrien 7479971Sobrien/* 7579971Sobrien * Determine if complete is ambiguous. If unique, insert. 7679971Sobrien * If no choices, error. If unambiguous prefix, insert that. 7779971Sobrien * Otherwise, list choices. words is assumed to be filtered 7879971Sobrien * to only contain possible choices. 7979971Sobrien * Args: 8079971Sobrien * word word which started the match 8179971Sobrien * list list by default 8279971Sobrien * words stringlist containing possible matches 8379971Sobrien * Returns a result as per el_set(EL_ADDFN, ...) 8479971Sobrien */ 8579971Sobrienstatic unsigned char 8679971Sobriencomplete_ambiguous(char *word, int list, StringList *words) 8779971Sobrien{ 8879971Sobrien char insertstr[MAXPATHLEN]; 8979971Sobrien char *lastmatch, *p; 90223328Sgavin size_t i, j; 9179971Sobrien size_t matchlen, wordlen; 9279971Sobrien 9379971Sobrien wordlen = strlen(word); 9479971Sobrien if (words->sl_cur == 0) 9579971Sobrien return (CC_ERROR); /* no choices available */ 9679971Sobrien 9779971Sobrien if (words->sl_cur == 1) { /* only once choice available */ 9879971Sobrien p = words->sl_str[0] + wordlen; 9979971Sobrien if (*p == '\0') /* at end of word? */ 10079971Sobrien return (CC_REFRESH); 10179971Sobrien ftpvis(insertstr, sizeof(insertstr), p, strlen(p)); 10279971Sobrien if (el_insertstr(el, insertstr) == -1) 10379971Sobrien return (CC_ERROR); 10479971Sobrien else 10579971Sobrien return (CC_REFRESH); 10679971Sobrien } 10779971Sobrien 10879971Sobrien if (!list) { 10979971Sobrien matchlen = 0; 11079971Sobrien lastmatch = words->sl_str[0]; 11179971Sobrien matchlen = strlen(lastmatch); 11279971Sobrien for (i = 1 ; i < words->sl_cur ; i++) { 11379971Sobrien for (j = wordlen ; j < strlen(words->sl_str[i]); j++) 11479971Sobrien if (lastmatch[j] != words->sl_str[i][j]) 11579971Sobrien break; 11679971Sobrien if (j < matchlen) 11779971Sobrien matchlen = j; 11879971Sobrien } 11979971Sobrien if (matchlen > wordlen) { 12079971Sobrien ftpvis(insertstr, sizeof(insertstr), 12179971Sobrien lastmatch + wordlen, matchlen - wordlen); 12279971Sobrien if (el_insertstr(el, insertstr) == -1) 12379971Sobrien return (CC_ERROR); 12479971Sobrien else 12579971Sobrien return (CC_REFRESH_BEEP); 12679971Sobrien } 12779971Sobrien } 12879971Sobrien 12979971Sobrien putc('\n', ttyout); 13079971Sobrien qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr); 13179971Sobrien list_vertical(words); 13279971Sobrien return (CC_REDISPLAY); 13379971Sobrien} 13479971Sobrien 13579971Sobrien/* 13679971Sobrien * Complete a command 13779971Sobrien */ 13879971Sobrienstatic unsigned char 13979971Sobriencomplete_command(char *word, int list) 14079971Sobrien{ 14179971Sobrien struct cmd *c; 14279971Sobrien StringList *words; 14379971Sobrien size_t wordlen; 14479971Sobrien unsigned char rv; 14579971Sobrien 146223328Sgavin words = ftp_sl_init(); 14779971Sobrien wordlen = strlen(word); 14879971Sobrien 14979971Sobrien for (c = cmdtab; c->c_name != NULL; c++) { 15079971Sobrien if (wordlen > strlen(c->c_name)) 15179971Sobrien continue; 15279971Sobrien if (strncmp(word, c->c_name, wordlen) == 0) 153223328Sgavin ftp_sl_add(words, ftp_strdup(c->c_name)); 15479971Sobrien } 15579971Sobrien 15679971Sobrien rv = complete_ambiguous(word, list, words); 15779971Sobrien if (rv == CC_REFRESH) { 15879971Sobrien if (el_insertstr(el, " ") == -1) 15979971Sobrien rv = CC_ERROR; 16079971Sobrien } 161223328Sgavin sl_free(words, 1); 16279971Sobrien return (rv); 16379971Sobrien} 16479971Sobrien 16579971Sobrien/* 16679971Sobrien * Complete a local file 16779971Sobrien */ 16879971Sobrienstatic unsigned char 16979971Sobriencomplete_local(char *word, int list) 17079971Sobrien{ 17179971Sobrien StringList *words; 17279971Sobrien char dir[MAXPATHLEN]; 17379971Sobrien char *file; 17479971Sobrien DIR *dd; 17579971Sobrien struct dirent *dp; 17679971Sobrien unsigned char rv; 17779971Sobrien size_t len; 17879971Sobrien 17979971Sobrien if ((file = strrchr(word, '/')) == NULL) { 18079971Sobrien dir[0] = '.'; 18179971Sobrien dir[1] = '\0'; 18279971Sobrien file = word; 18379971Sobrien } else { 18479971Sobrien if (file == word) { 18579971Sobrien dir[0] = '/'; 18679971Sobrien dir[1] = '\0'; 18779971Sobrien } else 18879971Sobrien (void)strlcpy(dir, word, file - word + 1); 18979971Sobrien file++; 19079971Sobrien } 19179971Sobrien if (dir[0] == '~') { 19279971Sobrien char *p; 19379971Sobrien 19479971Sobrien if ((p = globulize(dir)) == NULL) 19579971Sobrien return (CC_ERROR); 19679971Sobrien (void)strlcpy(dir, p, sizeof(dir)); 19779971Sobrien free(p); 19879971Sobrien } 19979971Sobrien 20079971Sobrien if ((dd = opendir(dir)) == NULL) 20179971Sobrien return (CC_ERROR); 20279971Sobrien 203223328Sgavin words = ftp_sl_init(); 20479971Sobrien len = strlen(file); 20579971Sobrien 20679971Sobrien for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { 20779971Sobrien if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 20879971Sobrien continue; 20979971Sobrien 21079971Sobrien#if defined(DIRENT_MISSING_D_NAMLEN) 21179971Sobrien if (len > strlen(dp->d_name)) 21279971Sobrien continue; 21379971Sobrien#else 21479971Sobrien if (len > dp->d_namlen) 21579971Sobrien continue; 21679971Sobrien#endif 21779971Sobrien if (strncmp(file, dp->d_name, len) == 0) { 21879971Sobrien char *tcp; 21979971Sobrien 220223328Sgavin tcp = ftp_strdup(dp->d_name); 221223328Sgavin ftp_sl_add(words, tcp); 22279971Sobrien } 22379971Sobrien } 22479971Sobrien closedir(dd); 22579971Sobrien 22679971Sobrien rv = complete_ambiguous(file, list, words); 22779971Sobrien if (rv == CC_REFRESH) { 22879971Sobrien struct stat sb; 22979971Sobrien char path[MAXPATHLEN]; 23079971Sobrien 23179971Sobrien (void)strlcpy(path, dir, sizeof(path)); 23279971Sobrien (void)strlcat(path, "/", sizeof(path)); 23379971Sobrien (void)strlcat(path, words->sl_str[0], sizeof(path)); 23479971Sobrien 23579971Sobrien if (stat(path, &sb) >= 0) { 23679971Sobrien char suffix[2] = " "; 23779971Sobrien 23879971Sobrien if (S_ISDIR(sb.st_mode)) 23979971Sobrien suffix[0] = '/'; 24079971Sobrien if (el_insertstr(el, suffix) == -1) 24179971Sobrien rv = CC_ERROR; 24279971Sobrien } 24379971Sobrien } 24479971Sobrien sl_free(words, 1); 24579971Sobrien return (rv); 24679971Sobrien} 24779971Sobrien/* 24879971Sobrien * Complete an option 24979971Sobrien */ 25079971Sobrienstatic unsigned char 25179971Sobriencomplete_option(char *word, int list) 25279971Sobrien{ 25379971Sobrien struct option *o; 25479971Sobrien StringList *words; 25579971Sobrien size_t wordlen; 25679971Sobrien unsigned char rv; 25779971Sobrien 258223328Sgavin words = ftp_sl_init(); 25979971Sobrien wordlen = strlen(word); 26079971Sobrien 26179971Sobrien for (o = optiontab; o->name != NULL; o++) { 26279971Sobrien if (wordlen > strlen(o->name)) 26379971Sobrien continue; 26479971Sobrien if (strncmp(word, o->name, wordlen) == 0) 265223328Sgavin ftp_sl_add(words, ftp_strdup(o->name)); 26679971Sobrien } 26779971Sobrien 26879971Sobrien rv = complete_ambiguous(word, list, words); 26979971Sobrien if (rv == CC_REFRESH) { 27079971Sobrien if (el_insertstr(el, " ") == -1) 27179971Sobrien rv = CC_ERROR; 27279971Sobrien } 273223328Sgavin sl_free(words, 1); 27479971Sobrien return (rv); 27579971Sobrien} 27679971Sobrien 27779971Sobrien/* 27879971Sobrien * Complete a remote file 27979971Sobrien */ 28079971Sobrienstatic unsigned char 28179971Sobriencomplete_remote(char *word, int list) 28279971Sobrien{ 28379971Sobrien static StringList *dirlist; 28479971Sobrien static char lastdir[MAXPATHLEN]; 28579971Sobrien StringList *words; 28679971Sobrien char dir[MAXPATHLEN]; 28779971Sobrien char *file, *cp; 288223328Sgavin size_t i; 28979971Sobrien unsigned char rv; 290223328Sgavin char cmdbuf[MAX_C_NAME]; 291223328Sgavin char *dummyargv[3] = { NULL, NULL, NULL }; 29279971Sobrien 293223328Sgavin (void)strlcpy(cmdbuf, "complete", sizeof(cmdbuf)); 294223328Sgavin dummyargv[0] = cmdbuf; 29579971Sobrien dummyargv[1] = dir; 29679971Sobrien 29779971Sobrien if ((file = strrchr(word, '/')) == NULL) { 29879971Sobrien dir[0] = '\0'; 29979971Sobrien file = word; 30079971Sobrien } else { 30179971Sobrien cp = file; 30279971Sobrien while (*cp == '/' && cp > word) 30379971Sobrien cp--; 30479971Sobrien (void)strlcpy(dir, word, cp - word + 2); 30579971Sobrien file++; 30679971Sobrien } 30779971Sobrien 30879971Sobrien if (dirchange || dirlist == NULL || 30979971Sobrien strcmp(dir, lastdir) != 0) { /* dir not cached */ 310223328Sgavin const char *emesg; 31179971Sobrien 31279971Sobrien if (dirlist != NULL) 31379971Sobrien sl_free(dirlist, 1); 314223328Sgavin dirlist = ftp_sl_init(); 31579971Sobrien 31679971Sobrien mflag = 1; 31779971Sobrien emesg = NULL; 31879971Sobrien while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) { 31979971Sobrien char *tcp; 32079971Sobrien 32179971Sobrien if (!mflag) 32279971Sobrien continue; 32379971Sobrien if (*cp == '\0') { 32479971Sobrien mflag = 0; 32579971Sobrien continue; 32679971Sobrien } 32779971Sobrien tcp = strrchr(cp, '/'); 32879971Sobrien if (tcp) 32979971Sobrien tcp++; 33079971Sobrien else 33179971Sobrien tcp = cp; 332223328Sgavin tcp = ftp_strdup(tcp); 333223328Sgavin ftp_sl_add(dirlist, tcp); 33479971Sobrien } 33579971Sobrien if (emesg != NULL) { 33679971Sobrien fprintf(ttyout, "\n%s\n", emesg); 33779971Sobrien return (CC_REDISPLAY); 33879971Sobrien } 33979971Sobrien (void)strlcpy(lastdir, dir, sizeof(lastdir)); 34079971Sobrien dirchange = 0; 34179971Sobrien } 34279971Sobrien 343223328Sgavin words = ftp_sl_init(); 34479971Sobrien for (i = 0; i < dirlist->sl_cur; i++) { 34579971Sobrien cp = dirlist->sl_str[i]; 34679971Sobrien if (strlen(file) > strlen(cp)) 34779971Sobrien continue; 34879971Sobrien if (strncmp(file, cp, strlen(file)) == 0) 349223328Sgavin ftp_sl_add(words, cp); 35079971Sobrien } 35179971Sobrien rv = complete_ambiguous(file, list, words); 35279971Sobrien sl_free(words, 0); 35379971Sobrien return (rv); 35479971Sobrien} 35579971Sobrien 35679971Sobrien/* 35779971Sobrien * Generic complete routine 35879971Sobrien */ 35979971Sobrienunsigned char 360223328Sgavincomplete(EditLine *cel, int ch) 36179971Sobrien{ 36279971Sobrien static char word[FTPBUFLEN]; 363223328Sgavin static size_t lastc_argc, lastc_argo; 36479971Sobrien 36579971Sobrien struct cmd *c; 36679971Sobrien const LineInfo *lf; 367223328Sgavin int dolist, cmpltype; 368223328Sgavin size_t celems, len; 36979971Sobrien 370223328Sgavin lf = el_line(cel); 37179971Sobrien len = lf->lastchar - lf->buffer; 37279971Sobrien if (len >= sizeof(line)) 37379971Sobrien return (CC_ERROR); 37479971Sobrien (void)strlcpy(line, lf->buffer, len + 1); 37579971Sobrien cursor_pos = line + (lf->cursor - lf->buffer); 37679971Sobrien lastc_argc = cursor_argc; /* remember last cursor pos */ 37779971Sobrien lastc_argo = cursor_argo; 37879971Sobrien makeargv(); /* build argc/argv of current line */ 37979971Sobrien 38079971Sobrien if (cursor_argo >= sizeof(word)) 38179971Sobrien return (CC_ERROR); 38279971Sobrien 38379971Sobrien dolist = 0; 38479971Sobrien /* if cursor and word is same, list alternatives */ 38579971Sobrien if (lastc_argc == cursor_argc && lastc_argo == cursor_argo 38679971Sobrien && strncmp(word, margv[cursor_argc] ? margv[cursor_argc] : "", 38779971Sobrien cursor_argo) == 0) 38879971Sobrien dolist = 1; 389223328Sgavin else if (cursor_argc < (size_t)margc) 39079971Sobrien (void)strlcpy(word, margv[cursor_argc], cursor_argo + 1); 39179971Sobrien word[cursor_argo] = '\0'; 39279971Sobrien 39379971Sobrien if (cursor_argc == 0) 39479971Sobrien return (complete_command(word, dolist)); 39579971Sobrien 39679971Sobrien c = getcmd(margv[0]); 39779971Sobrien if (c == (struct cmd *)-1 || c == 0) 39879971Sobrien return (CC_ERROR); 39979971Sobrien celems = strlen(c->c_complete); 40079971Sobrien 40179971Sobrien /* check for 'continuation' completes (which are uppercase) */ 40279971Sobrien if ((cursor_argc > celems) && (celems > 0) 40379971Sobrien && isupper((unsigned char) c->c_complete[celems-1])) 40479971Sobrien cursor_argc = celems; 40579971Sobrien 40679971Sobrien if (cursor_argc > celems) 40779971Sobrien return (CC_ERROR); 40879971Sobrien 40979971Sobrien cmpltype = c->c_complete[cursor_argc - 1]; 41079971Sobrien switch (cmpltype) { 41179971Sobrien case 'c': /* command complete */ 41279971Sobrien case 'C': 41379971Sobrien return (complete_command(word, dolist)); 41479971Sobrien case 'l': /* local complete */ 41579971Sobrien case 'L': 41679971Sobrien return (complete_local(word, dolist)); 41779971Sobrien case 'n': /* no complete */ 41879971Sobrien case 'N': /* no complete */ 41979971Sobrien return (CC_ERROR); 42079971Sobrien case 'o': /* local complete */ 42179971Sobrien case 'O': 42279971Sobrien return (complete_option(word, dolist)); 42379971Sobrien case 'r': /* remote complete */ 42479971Sobrien case 'R': 42579971Sobrien if (connected != -1) { 42679971Sobrien fputs("\nMust be logged in to complete.\n", 42779971Sobrien ttyout); 42879971Sobrien return (CC_REDISPLAY); 42979971Sobrien } 43079971Sobrien return (complete_remote(word, dolist)); 43179971Sobrien default: 432223328Sgavin errx(1, "complete: unknown complete type `%c'", 433223328Sgavin cmpltype); 43479971Sobrien return (CC_ERROR); 43579971Sobrien } 43679971Sobrien /* NOTREACHED */ 43779971Sobrien} 43879971Sobrien 43979971Sobrien#endif /* !NO_EDITCOMPLETE */ 440