1*f982db4aSGavin Atkinson /* $NetBSD: complete.c,v 1.38 2000/05/01 10:35:17 lukem Exp $ */ 2*f982db4aSGavin Atkinson 3*f982db4aSGavin Atkinson /*- 4*f982db4aSGavin Atkinson * Copyright (c) 1997-2000 The NetBSD Foundation, Inc. 5*f982db4aSGavin Atkinson * All rights reserved. 6*f982db4aSGavin Atkinson * 7*f982db4aSGavin Atkinson * This code is derived from software contributed to The NetBSD Foundation 8*f982db4aSGavin Atkinson * by Luke Mewburn. 9*f982db4aSGavin Atkinson * 10*f982db4aSGavin Atkinson * Redistribution and use in source and binary forms, with or without 11*f982db4aSGavin Atkinson * modification, are permitted provided that the following conditions 12*f982db4aSGavin Atkinson * are met: 13*f982db4aSGavin Atkinson * 1. Redistributions of source code must retain the above copyright 14*f982db4aSGavin Atkinson * notice, this list of conditions and the following disclaimer. 15*f982db4aSGavin Atkinson * 2. Redistributions in binary form must reproduce the above copyright 16*f982db4aSGavin Atkinson * notice, this list of conditions and the following disclaimer in the 17*f982db4aSGavin Atkinson * documentation and/or other materials provided with the distribution. 18*f982db4aSGavin Atkinson * 3. All advertising materials mentioning features or use of this software 19*f982db4aSGavin Atkinson * must display the following acknowledgement: 20*f982db4aSGavin Atkinson * This product includes software developed by the NetBSD 21*f982db4aSGavin Atkinson * Foundation, Inc. and its contributors. 22*f982db4aSGavin Atkinson * 4. Neither the name of The NetBSD Foundation nor the names of its 23*f982db4aSGavin Atkinson * contributors may be used to endorse or promote products derived 24*f982db4aSGavin Atkinson * from this software without specific prior written permission. 25*f982db4aSGavin Atkinson * 26*f982db4aSGavin Atkinson * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27*f982db4aSGavin Atkinson * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28*f982db4aSGavin Atkinson * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29*f982db4aSGavin Atkinson * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30*f982db4aSGavin Atkinson * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31*f982db4aSGavin Atkinson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32*f982db4aSGavin Atkinson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33*f982db4aSGavin Atkinson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34*f982db4aSGavin Atkinson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35*f982db4aSGavin Atkinson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36*f982db4aSGavin Atkinson * POSSIBILITY OF SUCH DAMAGE. 37*f982db4aSGavin Atkinson */ 38*f982db4aSGavin Atkinson 39*f982db4aSGavin Atkinson #include <sys/cdefs.h> 40*f982db4aSGavin Atkinson #ifndef lint 41*f982db4aSGavin Atkinson __RCSID("$NetBSD: complete.c,v 1.38 2000/05/01 10:35:17 lukem Exp $"); 42*f982db4aSGavin Atkinson #endif /* not lint */ 43*f982db4aSGavin Atkinson 44*f982db4aSGavin Atkinson /* 45*f982db4aSGavin Atkinson * FTP user program - command and file completion routines 46*f982db4aSGavin Atkinson */ 47*f982db4aSGavin Atkinson 48*f982db4aSGavin Atkinson #include <sys/stat.h> 49*f982db4aSGavin Atkinson 50*f982db4aSGavin Atkinson #include <ctype.h> 51*f982db4aSGavin Atkinson #include <err.h> 52*f982db4aSGavin Atkinson #include <dirent.h> 53*f982db4aSGavin Atkinson #include <stdio.h> 54*f982db4aSGavin Atkinson #include <stdlib.h> 55*f982db4aSGavin Atkinson #include <string.h> 56*f982db4aSGavin Atkinson 57*f982db4aSGavin Atkinson #include "ftp_var.h" 58*f982db4aSGavin Atkinson 59*f982db4aSGavin Atkinson #ifndef NO_EDITCOMPLETE 60*f982db4aSGavin Atkinson 61*f982db4aSGavin Atkinson static int comparstr (const void *, const void *); 62*f982db4aSGavin Atkinson static unsigned char complete_ambiguous (char *, int, StringList *); 63*f982db4aSGavin Atkinson static unsigned char complete_command (char *, int); 64*f982db4aSGavin Atkinson static unsigned char complete_local (char *, int); 65*f982db4aSGavin Atkinson static unsigned char complete_option (char *, int); 66*f982db4aSGavin Atkinson static unsigned char complete_remote (char *, int); 67*f982db4aSGavin Atkinson 68*f982db4aSGavin Atkinson static int 69*f982db4aSGavin Atkinson comparstr(const void *a, const void *b) 70*f982db4aSGavin Atkinson { 71*f982db4aSGavin Atkinson return (strcmp(*(const char **)a, *(const char **)b)); 72*f982db4aSGavin Atkinson } 73*f982db4aSGavin Atkinson 74*f982db4aSGavin Atkinson /* 75*f982db4aSGavin Atkinson * Determine if complete is ambiguous. If unique, insert. 76*f982db4aSGavin Atkinson * If no choices, error. If unambiguous prefix, insert that. 77*f982db4aSGavin Atkinson * Otherwise, list choices. words is assumed to be filtered 78*f982db4aSGavin Atkinson * to only contain possible choices. 79*f982db4aSGavin Atkinson * Args: 80*f982db4aSGavin Atkinson * word word which started the match 81*f982db4aSGavin Atkinson * list list by default 82*f982db4aSGavin Atkinson * words stringlist containing possible matches 83*f982db4aSGavin Atkinson * Returns a result as per el_set(EL_ADDFN, ...) 84*f982db4aSGavin Atkinson */ 85*f982db4aSGavin Atkinson static unsigned char 86*f982db4aSGavin Atkinson complete_ambiguous(char *word, int list, StringList *words) 87*f982db4aSGavin Atkinson { 88*f982db4aSGavin Atkinson char insertstr[MAXPATHLEN]; 89*f982db4aSGavin Atkinson char *lastmatch, *p; 90*f982db4aSGavin Atkinson int i, j; 91*f982db4aSGavin Atkinson size_t matchlen, wordlen; 92*f982db4aSGavin Atkinson 93*f982db4aSGavin Atkinson wordlen = strlen(word); 94*f982db4aSGavin Atkinson if (words->sl_cur == 0) 95*f982db4aSGavin Atkinson return (CC_ERROR); /* no choices available */ 96*f982db4aSGavin Atkinson 97*f982db4aSGavin Atkinson if (words->sl_cur == 1) { /* only once choice available */ 98*f982db4aSGavin Atkinson p = words->sl_str[0] + wordlen; 99*f982db4aSGavin Atkinson if (*p == '\0') /* at end of word? */ 100*f982db4aSGavin Atkinson return (CC_REFRESH); 101*f982db4aSGavin Atkinson ftpvis(insertstr, sizeof(insertstr), p, strlen(p)); 102*f982db4aSGavin Atkinson if (el_insertstr(el, insertstr) == -1) 103*f982db4aSGavin Atkinson return (CC_ERROR); 104*f982db4aSGavin Atkinson else 105*f982db4aSGavin Atkinson return (CC_REFRESH); 106*f982db4aSGavin Atkinson } 107*f982db4aSGavin Atkinson 108*f982db4aSGavin Atkinson if (!list) { 109*f982db4aSGavin Atkinson matchlen = 0; 110*f982db4aSGavin Atkinson lastmatch = words->sl_str[0]; 111*f982db4aSGavin Atkinson matchlen = strlen(lastmatch); 112*f982db4aSGavin Atkinson for (i = 1 ; i < words->sl_cur ; i++) { 113*f982db4aSGavin Atkinson for (j = wordlen ; j < strlen(words->sl_str[i]); j++) 114*f982db4aSGavin Atkinson if (lastmatch[j] != words->sl_str[i][j]) 115*f982db4aSGavin Atkinson break; 116*f982db4aSGavin Atkinson if (j < matchlen) 117*f982db4aSGavin Atkinson matchlen = j; 118*f982db4aSGavin Atkinson } 119*f982db4aSGavin Atkinson if (matchlen > wordlen) { 120*f982db4aSGavin Atkinson ftpvis(insertstr, sizeof(insertstr), 121*f982db4aSGavin Atkinson lastmatch + wordlen, matchlen - wordlen); 122*f982db4aSGavin Atkinson if (el_insertstr(el, insertstr) == -1) 123*f982db4aSGavin Atkinson return (CC_ERROR); 124*f982db4aSGavin Atkinson else 125*f982db4aSGavin Atkinson return (CC_REFRESH_BEEP); 126*f982db4aSGavin Atkinson } 127*f982db4aSGavin Atkinson } 128*f982db4aSGavin Atkinson 129*f982db4aSGavin Atkinson putc('\n', ttyout); 130*f982db4aSGavin Atkinson qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr); 131*f982db4aSGavin Atkinson list_vertical(words); 132*f982db4aSGavin Atkinson return (CC_REDISPLAY); 133*f982db4aSGavin Atkinson } 134*f982db4aSGavin Atkinson 135*f982db4aSGavin Atkinson /* 136*f982db4aSGavin Atkinson * Complete a command 137*f982db4aSGavin Atkinson */ 138*f982db4aSGavin Atkinson static unsigned char 139*f982db4aSGavin Atkinson complete_command(char *word, int list) 140*f982db4aSGavin Atkinson { 141*f982db4aSGavin Atkinson struct cmd *c; 142*f982db4aSGavin Atkinson StringList *words; 143*f982db4aSGavin Atkinson size_t wordlen; 144*f982db4aSGavin Atkinson unsigned char rv; 145*f982db4aSGavin Atkinson 146*f982db4aSGavin Atkinson words = xsl_init(); 147*f982db4aSGavin Atkinson wordlen = strlen(word); 148*f982db4aSGavin Atkinson 149*f982db4aSGavin Atkinson for (c = cmdtab; c->c_name != NULL; c++) { 150*f982db4aSGavin Atkinson if (wordlen > strlen(c->c_name)) 151*f982db4aSGavin Atkinson continue; 152*f982db4aSGavin Atkinson if (strncmp(word, c->c_name, wordlen) == 0) 153*f982db4aSGavin Atkinson xsl_add(words, c->c_name); 154*f982db4aSGavin Atkinson } 155*f982db4aSGavin Atkinson 156*f982db4aSGavin Atkinson rv = complete_ambiguous(word, list, words); 157*f982db4aSGavin Atkinson if (rv == CC_REFRESH) { 158*f982db4aSGavin Atkinson if (el_insertstr(el, " ") == -1) 159*f982db4aSGavin Atkinson rv = CC_ERROR; 160*f982db4aSGavin Atkinson } 161*f982db4aSGavin Atkinson sl_free(words, 0); 162*f982db4aSGavin Atkinson return (rv); 163*f982db4aSGavin Atkinson } 164*f982db4aSGavin Atkinson 165*f982db4aSGavin Atkinson /* 166*f982db4aSGavin Atkinson * Complete a local file 167*f982db4aSGavin Atkinson */ 168*f982db4aSGavin Atkinson static unsigned char 169*f982db4aSGavin Atkinson complete_local(char *word, int list) 170*f982db4aSGavin Atkinson { 171*f982db4aSGavin Atkinson StringList *words; 172*f982db4aSGavin Atkinson char dir[MAXPATHLEN]; 173*f982db4aSGavin Atkinson char *file; 174*f982db4aSGavin Atkinson DIR *dd; 175*f982db4aSGavin Atkinson struct dirent *dp; 176*f982db4aSGavin Atkinson unsigned char rv; 177*f982db4aSGavin Atkinson size_t len; 178*f982db4aSGavin Atkinson 179*f982db4aSGavin Atkinson if ((file = strrchr(word, '/')) == NULL) { 180*f982db4aSGavin Atkinson dir[0] = '.'; 181*f982db4aSGavin Atkinson dir[1] = '\0'; 182*f982db4aSGavin Atkinson file = word; 183*f982db4aSGavin Atkinson } else { 184*f982db4aSGavin Atkinson if (file == word) { 185*f982db4aSGavin Atkinson dir[0] = '/'; 186*f982db4aSGavin Atkinson dir[1] = '\0'; 187*f982db4aSGavin Atkinson } else 188*f982db4aSGavin Atkinson (void)strlcpy(dir, word, file - word + 1); 189*f982db4aSGavin Atkinson file++; 190*f982db4aSGavin Atkinson } 191*f982db4aSGavin Atkinson if (dir[0] == '~') { 192*f982db4aSGavin Atkinson char *p; 193*f982db4aSGavin Atkinson 194*f982db4aSGavin Atkinson if ((p = globulize(dir)) == NULL) 195*f982db4aSGavin Atkinson return (CC_ERROR); 196*f982db4aSGavin Atkinson (void)strlcpy(dir, p, sizeof(dir)); 197*f982db4aSGavin Atkinson free(p); 198*f982db4aSGavin Atkinson } 199*f982db4aSGavin Atkinson 200*f982db4aSGavin Atkinson if ((dd = opendir(dir)) == NULL) 201*f982db4aSGavin Atkinson return (CC_ERROR); 202*f982db4aSGavin Atkinson 203*f982db4aSGavin Atkinson words = xsl_init(); 204*f982db4aSGavin Atkinson len = strlen(file); 205*f982db4aSGavin Atkinson 206*f982db4aSGavin Atkinson for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { 207*f982db4aSGavin Atkinson if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 208*f982db4aSGavin Atkinson continue; 209*f982db4aSGavin Atkinson 210*f982db4aSGavin Atkinson #if defined(DIRENT_MISSING_D_NAMLEN) 211*f982db4aSGavin Atkinson if (len > strlen(dp->d_name)) 212*f982db4aSGavin Atkinson continue; 213*f982db4aSGavin Atkinson #else 214*f982db4aSGavin Atkinson if (len > dp->d_namlen) 215*f982db4aSGavin Atkinson continue; 216*f982db4aSGavin Atkinson #endif 217*f982db4aSGavin Atkinson if (strncmp(file, dp->d_name, len) == 0) { 218*f982db4aSGavin Atkinson char *tcp; 219*f982db4aSGavin Atkinson 220*f982db4aSGavin Atkinson tcp = xstrdup(dp->d_name); 221*f982db4aSGavin Atkinson xsl_add(words, tcp); 222*f982db4aSGavin Atkinson } 223*f982db4aSGavin Atkinson } 224*f982db4aSGavin Atkinson closedir(dd); 225*f982db4aSGavin Atkinson 226*f982db4aSGavin Atkinson rv = complete_ambiguous(file, list, words); 227*f982db4aSGavin Atkinson if (rv == CC_REFRESH) { 228*f982db4aSGavin Atkinson struct stat sb; 229*f982db4aSGavin Atkinson char path[MAXPATHLEN]; 230*f982db4aSGavin Atkinson 231*f982db4aSGavin Atkinson (void)strlcpy(path, dir, sizeof(path)); 232*f982db4aSGavin Atkinson (void)strlcat(path, "/", sizeof(path)); 233*f982db4aSGavin Atkinson (void)strlcat(path, words->sl_str[0], sizeof(path)); 234*f982db4aSGavin Atkinson 235*f982db4aSGavin Atkinson if (stat(path, &sb) >= 0) { 236*f982db4aSGavin Atkinson char suffix[2] = " "; 237*f982db4aSGavin Atkinson 238*f982db4aSGavin Atkinson if (S_ISDIR(sb.st_mode)) 239*f982db4aSGavin Atkinson suffix[0] = '/'; 240*f982db4aSGavin Atkinson if (el_insertstr(el, suffix) == -1) 241*f982db4aSGavin Atkinson rv = CC_ERROR; 242*f982db4aSGavin Atkinson } 243*f982db4aSGavin Atkinson } 244*f982db4aSGavin Atkinson sl_free(words, 1); 245*f982db4aSGavin Atkinson return (rv); 246*f982db4aSGavin Atkinson } 247*f982db4aSGavin Atkinson /* 248*f982db4aSGavin Atkinson * Complete an option 249*f982db4aSGavin Atkinson */ 250*f982db4aSGavin Atkinson static unsigned char 251*f982db4aSGavin Atkinson complete_option(char *word, int list) 252*f982db4aSGavin Atkinson { 253*f982db4aSGavin Atkinson struct option *o; 254*f982db4aSGavin Atkinson StringList *words; 255*f982db4aSGavin Atkinson size_t wordlen; 256*f982db4aSGavin Atkinson unsigned char rv; 257*f982db4aSGavin Atkinson 258*f982db4aSGavin Atkinson words = xsl_init(); 259*f982db4aSGavin Atkinson wordlen = strlen(word); 260*f982db4aSGavin Atkinson 261*f982db4aSGavin Atkinson for (o = optiontab; o->name != NULL; o++) { 262*f982db4aSGavin Atkinson if (wordlen > strlen(o->name)) 263*f982db4aSGavin Atkinson continue; 264*f982db4aSGavin Atkinson if (strncmp(word, o->name, wordlen) == 0) 265*f982db4aSGavin Atkinson xsl_add(words, o->name); 266*f982db4aSGavin Atkinson } 267*f982db4aSGavin Atkinson 268*f982db4aSGavin Atkinson rv = complete_ambiguous(word, list, words); 269*f982db4aSGavin Atkinson if (rv == CC_REFRESH) { 270*f982db4aSGavin Atkinson if (el_insertstr(el, " ") == -1) 271*f982db4aSGavin Atkinson rv = CC_ERROR; 272*f982db4aSGavin Atkinson } 273*f982db4aSGavin Atkinson sl_free(words, 0); 274*f982db4aSGavin Atkinson return (rv); 275*f982db4aSGavin Atkinson } 276*f982db4aSGavin Atkinson 277*f982db4aSGavin Atkinson /* 278*f982db4aSGavin Atkinson * Complete a remote file 279*f982db4aSGavin Atkinson */ 280*f982db4aSGavin Atkinson static unsigned char 281*f982db4aSGavin Atkinson complete_remote(char *word, int list) 282*f982db4aSGavin Atkinson { 283*f982db4aSGavin Atkinson static StringList *dirlist; 284*f982db4aSGavin Atkinson static char lastdir[MAXPATHLEN]; 285*f982db4aSGavin Atkinson StringList *words; 286*f982db4aSGavin Atkinson char dir[MAXPATHLEN]; 287*f982db4aSGavin Atkinson char *file, *cp; 288*f982db4aSGavin Atkinson int i; 289*f982db4aSGavin Atkinson unsigned char rv; 290*f982db4aSGavin Atkinson 291*f982db4aSGavin Atkinson char *dummyargv[] = { "complete", NULL, NULL }; 292*f982db4aSGavin Atkinson dummyargv[1] = dir; 293*f982db4aSGavin Atkinson 294*f982db4aSGavin Atkinson if ((file = strrchr(word, '/')) == NULL) { 295*f982db4aSGavin Atkinson dir[0] = '\0'; 296*f982db4aSGavin Atkinson file = word; 297*f982db4aSGavin Atkinson } else { 298*f982db4aSGavin Atkinson cp = file; 299*f982db4aSGavin Atkinson while (*cp == '/' && cp > word) 300*f982db4aSGavin Atkinson cp--; 301*f982db4aSGavin Atkinson (void)strlcpy(dir, word, cp - word + 2); 302*f982db4aSGavin Atkinson file++; 303*f982db4aSGavin Atkinson } 304*f982db4aSGavin Atkinson 305*f982db4aSGavin Atkinson if (dirchange || dirlist == NULL || 306*f982db4aSGavin Atkinson strcmp(dir, lastdir) != 0) { /* dir not cached */ 307*f982db4aSGavin Atkinson char *emesg; 308*f982db4aSGavin Atkinson 309*f982db4aSGavin Atkinson if (dirlist != NULL) 310*f982db4aSGavin Atkinson sl_free(dirlist, 1); 311*f982db4aSGavin Atkinson dirlist = xsl_init(); 312*f982db4aSGavin Atkinson 313*f982db4aSGavin Atkinson mflag = 1; 314*f982db4aSGavin Atkinson emesg = NULL; 315*f982db4aSGavin Atkinson while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) { 316*f982db4aSGavin Atkinson char *tcp; 317*f982db4aSGavin Atkinson 318*f982db4aSGavin Atkinson if (!mflag) 319*f982db4aSGavin Atkinson continue; 320*f982db4aSGavin Atkinson if (*cp == '\0') { 321*f982db4aSGavin Atkinson mflag = 0; 322*f982db4aSGavin Atkinson continue; 323*f982db4aSGavin Atkinson } 324*f982db4aSGavin Atkinson tcp = strrchr(cp, '/'); 325*f982db4aSGavin Atkinson if (tcp) 326*f982db4aSGavin Atkinson tcp++; 327*f982db4aSGavin Atkinson else 328*f982db4aSGavin Atkinson tcp = cp; 329*f982db4aSGavin Atkinson tcp = xstrdup(tcp); 330*f982db4aSGavin Atkinson xsl_add(dirlist, tcp); 331*f982db4aSGavin Atkinson } 332*f982db4aSGavin Atkinson if (emesg != NULL) { 333*f982db4aSGavin Atkinson fprintf(ttyout, "\n%s\n", emesg); 334*f982db4aSGavin Atkinson return (CC_REDISPLAY); 335*f982db4aSGavin Atkinson } 336*f982db4aSGavin Atkinson (void)strlcpy(lastdir, dir, sizeof(lastdir)); 337*f982db4aSGavin Atkinson dirchange = 0; 338*f982db4aSGavin Atkinson } 339*f982db4aSGavin Atkinson 340*f982db4aSGavin Atkinson words = xsl_init(); 341*f982db4aSGavin Atkinson for (i = 0; i < dirlist->sl_cur; i++) { 342*f982db4aSGavin Atkinson cp = dirlist->sl_str[i]; 343*f982db4aSGavin Atkinson if (strlen(file) > strlen(cp)) 344*f982db4aSGavin Atkinson continue; 345*f982db4aSGavin Atkinson if (strncmp(file, cp, strlen(file)) == 0) 346*f982db4aSGavin Atkinson xsl_add(words, cp); 347*f982db4aSGavin Atkinson } 348*f982db4aSGavin Atkinson rv = complete_ambiguous(file, list, words); 349*f982db4aSGavin Atkinson sl_free(words, 0); 350*f982db4aSGavin Atkinson return (rv); 351*f982db4aSGavin Atkinson } 352*f982db4aSGavin Atkinson 353*f982db4aSGavin Atkinson /* 354*f982db4aSGavin Atkinson * Generic complete routine 355*f982db4aSGavin Atkinson */ 356*f982db4aSGavin Atkinson unsigned char 357*f982db4aSGavin Atkinson complete(EditLine *el, int ch) 358*f982db4aSGavin Atkinson { 359*f982db4aSGavin Atkinson static char word[FTPBUFLEN]; 360*f982db4aSGavin Atkinson static int lastc_argc, lastc_argo; 361*f982db4aSGavin Atkinson 362*f982db4aSGavin Atkinson struct cmd *c; 363*f982db4aSGavin Atkinson const LineInfo *lf; 364*f982db4aSGavin Atkinson int celems, dolist, cmpltype; 365*f982db4aSGavin Atkinson size_t len; 366*f982db4aSGavin Atkinson 367*f982db4aSGavin Atkinson lf = el_line(el); 368*f982db4aSGavin Atkinson len = lf->lastchar - lf->buffer; 369*f982db4aSGavin Atkinson if (len >= sizeof(line)) 370*f982db4aSGavin Atkinson return (CC_ERROR); 371*f982db4aSGavin Atkinson (void)strlcpy(line, lf->buffer, len + 1); 372*f982db4aSGavin Atkinson cursor_pos = line + (lf->cursor - lf->buffer); 373*f982db4aSGavin Atkinson lastc_argc = cursor_argc; /* remember last cursor pos */ 374*f982db4aSGavin Atkinson lastc_argo = cursor_argo; 375*f982db4aSGavin Atkinson makeargv(); /* build argc/argv of current line */ 376*f982db4aSGavin Atkinson 377*f982db4aSGavin Atkinson if (cursor_argo >= sizeof(word)) 378*f982db4aSGavin Atkinson return (CC_ERROR); 379*f982db4aSGavin Atkinson 380*f982db4aSGavin Atkinson dolist = 0; 381*f982db4aSGavin Atkinson /* if cursor and word is same, list alternatives */ 382*f982db4aSGavin Atkinson if (lastc_argc == cursor_argc && lastc_argo == cursor_argo 383*f982db4aSGavin Atkinson && strncmp(word, margv[cursor_argc] ? margv[cursor_argc] : "", 384*f982db4aSGavin Atkinson cursor_argo) == 0) 385*f982db4aSGavin Atkinson dolist = 1; 386*f982db4aSGavin Atkinson else if (cursor_argc < margc) 387*f982db4aSGavin Atkinson (void)strlcpy(word, margv[cursor_argc], cursor_argo + 1); 388*f982db4aSGavin Atkinson word[cursor_argo] = '\0'; 389*f982db4aSGavin Atkinson 390*f982db4aSGavin Atkinson if (cursor_argc == 0) 391*f982db4aSGavin Atkinson return (complete_command(word, dolist)); 392*f982db4aSGavin Atkinson 393*f982db4aSGavin Atkinson c = getcmd(margv[0]); 394*f982db4aSGavin Atkinson if (c == (struct cmd *)-1 || c == 0) 395*f982db4aSGavin Atkinson return (CC_ERROR); 396*f982db4aSGavin Atkinson celems = strlen(c->c_complete); 397*f982db4aSGavin Atkinson 398*f982db4aSGavin Atkinson /* check for 'continuation' completes (which are uppercase) */ 399*f982db4aSGavin Atkinson if ((cursor_argc > celems) && (celems > 0) 400*f982db4aSGavin Atkinson && isupper((unsigned char) c->c_complete[celems-1])) 401*f982db4aSGavin Atkinson cursor_argc = celems; 402*f982db4aSGavin Atkinson 403*f982db4aSGavin Atkinson if (cursor_argc > celems) 404*f982db4aSGavin Atkinson return (CC_ERROR); 405*f982db4aSGavin Atkinson 406*f982db4aSGavin Atkinson cmpltype = c->c_complete[cursor_argc - 1]; 407*f982db4aSGavin Atkinson switch (cmpltype) { 408*f982db4aSGavin Atkinson case 'c': /* command complete */ 409*f982db4aSGavin Atkinson case 'C': 410*f982db4aSGavin Atkinson return (complete_command(word, dolist)); 411*f982db4aSGavin Atkinson case 'l': /* local complete */ 412*f982db4aSGavin Atkinson case 'L': 413*f982db4aSGavin Atkinson return (complete_local(word, dolist)); 414*f982db4aSGavin Atkinson case 'n': /* no complete */ 415*f982db4aSGavin Atkinson case 'N': /* no complete */ 416*f982db4aSGavin Atkinson return (CC_ERROR); 417*f982db4aSGavin Atkinson case 'o': /* local complete */ 418*f982db4aSGavin Atkinson case 'O': 419*f982db4aSGavin Atkinson return (complete_option(word, dolist)); 420*f982db4aSGavin Atkinson case 'r': /* remote complete */ 421*f982db4aSGavin Atkinson case 'R': 422*f982db4aSGavin Atkinson if (connected != -1) { 423*f982db4aSGavin Atkinson fputs("\nMust be logged in to complete.\n", 424*f982db4aSGavin Atkinson ttyout); 425*f982db4aSGavin Atkinson return (CC_REDISPLAY); 426*f982db4aSGavin Atkinson } 427*f982db4aSGavin Atkinson return (complete_remote(word, dolist)); 428*f982db4aSGavin Atkinson default: 429*f982db4aSGavin Atkinson errx(1, "unknown complete type `%c'", cmpltype); 430*f982db4aSGavin Atkinson return (CC_ERROR); 431*f982db4aSGavin Atkinson } 432*f982db4aSGavin Atkinson /* NOTREACHED */ 433*f982db4aSGavin Atkinson } 434*f982db4aSGavin Atkinson 435*f982db4aSGavin Atkinson #endif /* !NO_EDITCOMPLETE */ 436