1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28*0Sstevel@tonic-gate /* All Rights Reserved */ 29*0Sstevel@tonic-gate 30*0Sstevel@tonic-gate /* 31*0Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 32*0Sstevel@tonic-gate * The Regents of the University of California 33*0Sstevel@tonic-gate * All Rights Reserved 34*0Sstevel@tonic-gate * 35*0Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 36*0Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 37*0Sstevel@tonic-gate * contributors. 38*0Sstevel@tonic-gate */ 39*0Sstevel@tonic-gate 40*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 41*0Sstevel@tonic-gate 42*0Sstevel@tonic-gate /* 43*0Sstevel@tonic-gate * FTP User Program -- Command Interface. 44*0Sstevel@tonic-gate */ 45*0Sstevel@tonic-gate #define EXTERN 46*0Sstevel@tonic-gate #include "ftp_var.h" 47*0Sstevel@tonic-gate #include <deflt.h> /* macros that make using libcmd easier */ 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate static void usage(void); 50*0Sstevel@tonic-gate static void timeout_sig(int sig); 51*0Sstevel@tonic-gate static void cmdscanner(int top); 52*0Sstevel@tonic-gate static void intr(int sig); 53*0Sstevel@tonic-gate static char *slurpstring(void); 54*0Sstevel@tonic-gate extern int use_eprt; 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate boolean_t ls_invokes_NLST = B_TRUE; 57*0Sstevel@tonic-gate 58*0Sstevel@tonic-gate #include <gssapi/gssapi.h> 59*0Sstevel@tonic-gate #include <gssapi/gssapi_ext.h> 60*0Sstevel@tonic-gate #define GETOPT_STR "dginptvET:axfm:" 61*0Sstevel@tonic-gate #define USAGE_STR "[-adfginptvx] [-m mech] [-T timeout] " \ 62*0Sstevel@tonic-gate "[hostname [port]]" 63*0Sstevel@tonic-gate 64*0Sstevel@tonic-gate int 65*0Sstevel@tonic-gate main(int argc, char *argv[]) 66*0Sstevel@tonic-gate { 67*0Sstevel@tonic-gate char *cp; 68*0Sstevel@tonic-gate int c, top; 69*0Sstevel@tonic-gate struct passwd *pw = NULL; 70*0Sstevel@tonic-gate char homedir[MAXPATHLEN]; 71*0Sstevel@tonic-gate char *temp_string = NULL; 72*0Sstevel@tonic-gate 73*0Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 74*0Sstevel@tonic-gate 75*0Sstevel@tonic-gate buf = (char *)memalign(getpagesize(), FTPBUFSIZ); 76*0Sstevel@tonic-gate if (buf == NULL) { 77*0Sstevel@tonic-gate (void) fprintf(stderr, "ftp: memory allocation failed\n"); 78*0Sstevel@tonic-gate return (1); 79*0Sstevel@tonic-gate } 80*0Sstevel@tonic-gate 81*0Sstevel@tonic-gate timeoutms = timeout = 0; 82*0Sstevel@tonic-gate doglob = 1; 83*0Sstevel@tonic-gate interactive = 1; 84*0Sstevel@tonic-gate autologin = 1; 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gate autoauth = 0; 87*0Sstevel@tonic-gate fflag = 0; 88*0Sstevel@tonic-gate autoencrypt = 0; 89*0Sstevel@tonic-gate goteof = 0; 90*0Sstevel@tonic-gate mechstr[0] = '\0'; 91*0Sstevel@tonic-gate 92*0Sstevel@tonic-gate sendport = -1; /* tri-state variable. start out in "automatic" mode. */ 93*0Sstevel@tonic-gate passivemode = 0; 94*0Sstevel@tonic-gate 95*0Sstevel@tonic-gate while ((c = getopt(argc, argv, GETOPT_STR)) != EOF) { 96*0Sstevel@tonic-gate switch (c) { 97*0Sstevel@tonic-gate case 'd': 98*0Sstevel@tonic-gate options |= SO_DEBUG; 99*0Sstevel@tonic-gate debug++; 100*0Sstevel@tonic-gate break; 101*0Sstevel@tonic-gate 102*0Sstevel@tonic-gate case 'g': 103*0Sstevel@tonic-gate doglob = 0; 104*0Sstevel@tonic-gate break; 105*0Sstevel@tonic-gate 106*0Sstevel@tonic-gate case 'i': 107*0Sstevel@tonic-gate interactive = 0; 108*0Sstevel@tonic-gate break; 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate case 'n': 111*0Sstevel@tonic-gate autologin = 0; 112*0Sstevel@tonic-gate break; 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate case 'p': 115*0Sstevel@tonic-gate passivemode = 1; 116*0Sstevel@tonic-gate break; 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate case 't': 119*0Sstevel@tonic-gate trace++; 120*0Sstevel@tonic-gate break; 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate case 'v': 123*0Sstevel@tonic-gate verbose++; 124*0Sstevel@tonic-gate break; 125*0Sstevel@tonic-gate 126*0Sstevel@tonic-gate /* undocumented option: allows testing of EPRT */ 127*0Sstevel@tonic-gate case 'E': 128*0Sstevel@tonic-gate use_eprt = 1; 129*0Sstevel@tonic-gate break; 130*0Sstevel@tonic-gate 131*0Sstevel@tonic-gate case 'T': 132*0Sstevel@tonic-gate if (!isdigit(*optarg)) { 133*0Sstevel@tonic-gate (void) fprintf(stderr, 134*0Sstevel@tonic-gate "ftp: bad timeout: \"%s\"\n", optarg); 135*0Sstevel@tonic-gate break; 136*0Sstevel@tonic-gate } 137*0Sstevel@tonic-gate timeout = atoi(optarg); 138*0Sstevel@tonic-gate timeoutms = timeout * MILLISEC; 139*0Sstevel@tonic-gate break; 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gate case 'a': 142*0Sstevel@tonic-gate autoauth = 1; 143*0Sstevel@tonic-gate break; 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gate case 'f': 146*0Sstevel@tonic-gate autoauth = 1; 147*0Sstevel@tonic-gate fflag = 1; 148*0Sstevel@tonic-gate break; 149*0Sstevel@tonic-gate 150*0Sstevel@tonic-gate case 'm': 151*0Sstevel@tonic-gate autoauth = 1; 152*0Sstevel@tonic-gate call(setmech, "ftp", optarg, 0); 153*0Sstevel@tonic-gate if (code != 0) 154*0Sstevel@tonic-gate exit(1); 155*0Sstevel@tonic-gate break; 156*0Sstevel@tonic-gate 157*0Sstevel@tonic-gate case 'x': 158*0Sstevel@tonic-gate autoauth = 1; 159*0Sstevel@tonic-gate autoencrypt = 1; 160*0Sstevel@tonic-gate break; 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate case '?': 163*0Sstevel@tonic-gate default: 164*0Sstevel@tonic-gate usage(); 165*0Sstevel@tonic-gate } 166*0Sstevel@tonic-gate } 167*0Sstevel@tonic-gate argc -= optind; 168*0Sstevel@tonic-gate argv += optind; 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gate if (argc > 2) 171*0Sstevel@tonic-gate usage(); 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gate fromatty = isatty(fileno(stdin)); 174*0Sstevel@tonic-gate /* 175*0Sstevel@tonic-gate * Scan env, then DEFAULTFTPFILE 176*0Sstevel@tonic-gate * for FTP_LS_SENDS_NLST 177*0Sstevel@tonic-gate */ 178*0Sstevel@tonic-gate temp_string = getenv("FTP_LS_SENDS_NLST"); 179*0Sstevel@tonic-gate if (temp_string == NULL) { /* env var not set */ 180*0Sstevel@tonic-gate if (defopen(DEFAULTFTPFILE) == 0) { 181*0Sstevel@tonic-gate /* 182*0Sstevel@tonic-gate * turn off case sensitivity 183*0Sstevel@tonic-gate */ 184*0Sstevel@tonic-gate int flags = defcntl(DC_GETFLAGS, 0); 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate TURNOFF(flags, DC_CASE); 187*0Sstevel@tonic-gate (void) defcntl(DC_SETFLAGS, flags); 188*0Sstevel@tonic-gate 189*0Sstevel@tonic-gate temp_string = defread("FTP_LS_SENDS_NLST="); 190*0Sstevel@tonic-gate (void) defopen(NULL); /* close default file */ 191*0Sstevel@tonic-gate } 192*0Sstevel@tonic-gate } 193*0Sstevel@tonic-gate if (temp_string != NULL && 194*0Sstevel@tonic-gate strncasecmp(temp_string, "n", 1) == 0) 195*0Sstevel@tonic-gate ls_invokes_NLST = B_FALSE; 196*0Sstevel@tonic-gate 197*0Sstevel@tonic-gate /* 198*0Sstevel@tonic-gate * Set up defaults for FTP. 199*0Sstevel@tonic-gate */ 200*0Sstevel@tonic-gate (void) strcpy(typename, "ascii"), type = TYPE_A; 201*0Sstevel@tonic-gate (void) strcpy(formname, "non-print"), form = FORM_N; 202*0Sstevel@tonic-gate (void) strcpy(modename, "stream"), mode = MODE_S; 203*0Sstevel@tonic-gate (void) strcpy(structname, "file"), stru = STRU_F; 204*0Sstevel@tonic-gate (void) strcpy(bytename, "8"), bytesize = 8; 205*0Sstevel@tonic-gate if (fromatty) 206*0Sstevel@tonic-gate verbose++; 207*0Sstevel@tonic-gate cpend = 0; /* no pending replies */ 208*0Sstevel@tonic-gate proxy = 0; /* proxy not active */ 209*0Sstevel@tonic-gate crflag = 1; /* strip c.r. on ascii gets */ 210*0Sstevel@tonic-gate 211*0Sstevel@tonic-gate if (mechstr[0] == '\0') { 212*0Sstevel@tonic-gate strlcpy(mechstr, FTP_DEF_MECH, MECH_SZ); 213*0Sstevel@tonic-gate } 214*0Sstevel@tonic-gate 215*0Sstevel@tonic-gate /* 216*0Sstevel@tonic-gate * Set up the home directory in case we're globbing. 217*0Sstevel@tonic-gate */ 218*0Sstevel@tonic-gate cp = getlogin(); 219*0Sstevel@tonic-gate if (cp != NULL) { 220*0Sstevel@tonic-gate pw = getpwnam(cp); 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate if (pw == NULL) 223*0Sstevel@tonic-gate pw = getpwuid(getuid()); 224*0Sstevel@tonic-gate if (pw != NULL) { 225*0Sstevel@tonic-gate home = homedir; 226*0Sstevel@tonic-gate (void) strcpy(home, pw->pw_dir); 227*0Sstevel@tonic-gate } 228*0Sstevel@tonic-gate if (setjmp(timeralarm)) { 229*0Sstevel@tonic-gate (void) fflush(stdout); 230*0Sstevel@tonic-gate (void) printf("Connection timeout\n"); 231*0Sstevel@tonic-gate exit(1); 232*0Sstevel@tonic-gate } 233*0Sstevel@tonic-gate (void) signal(SIGALRM, timeout_sig); 234*0Sstevel@tonic-gate reset_timer(); 235*0Sstevel@tonic-gate if (argc > 0) { 236*0Sstevel@tonic-gate int nargc = 0; 237*0Sstevel@tonic-gate char *nargv[4]; 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate if (setjmp(toplevel)) 240*0Sstevel@tonic-gate return (0); 241*0Sstevel@tonic-gate (void) signal(SIGINT, intr); 242*0Sstevel@tonic-gate (void) signal(SIGPIPE, lostpeer); 243*0Sstevel@tonic-gate nargv[nargc++] = "ftp"; 244*0Sstevel@tonic-gate nargv[nargc++] = argv[0]; /* hostname */ 245*0Sstevel@tonic-gate if (argc > 1) 246*0Sstevel@tonic-gate nargv[nargc++] = argv[1]; /* port */ 247*0Sstevel@tonic-gate nargv[nargc] = NULL; 248*0Sstevel@tonic-gate setpeer(nargc, nargv); 249*0Sstevel@tonic-gate } 250*0Sstevel@tonic-gate top = setjmp(toplevel) == 0; 251*0Sstevel@tonic-gate if (top) { 252*0Sstevel@tonic-gate (void) signal(SIGINT, intr); 253*0Sstevel@tonic-gate (void) signal(SIGPIPE, lostpeer); 254*0Sstevel@tonic-gate } 255*0Sstevel@tonic-gate 256*0Sstevel@tonic-gate for (;;) { 257*0Sstevel@tonic-gate cmdscanner(top); 258*0Sstevel@tonic-gate top = 1; 259*0Sstevel@tonic-gate } 260*0Sstevel@tonic-gate } 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gate static void 263*0Sstevel@tonic-gate usage(void) 264*0Sstevel@tonic-gate { 265*0Sstevel@tonic-gate (void) fprintf(stderr, "usage: ftp %s\n", USAGE_STR); 266*0Sstevel@tonic-gate exit(1); 267*0Sstevel@tonic-gate } 268*0Sstevel@tonic-gate 269*0Sstevel@tonic-gate void 270*0Sstevel@tonic-gate reset_timer() 271*0Sstevel@tonic-gate { 272*0Sstevel@tonic-gate /* The test is just to reduce syscalls if timeouts aren't used */ 273*0Sstevel@tonic-gate if (timeout) 274*0Sstevel@tonic-gate alarm(timeout); 275*0Sstevel@tonic-gate } 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate void 278*0Sstevel@tonic-gate stop_timer() 279*0Sstevel@tonic-gate { 280*0Sstevel@tonic-gate if (timeout) 281*0Sstevel@tonic-gate alarm(0); 282*0Sstevel@tonic-gate } 283*0Sstevel@tonic-gate 284*0Sstevel@tonic-gate /*ARGSUSED*/ 285*0Sstevel@tonic-gate static void 286*0Sstevel@tonic-gate timeout_sig(int sig) 287*0Sstevel@tonic-gate { 288*0Sstevel@tonic-gate longjmp(timeralarm, 1); 289*0Sstevel@tonic-gate } 290*0Sstevel@tonic-gate 291*0Sstevel@tonic-gate /*ARGSUSED*/ 292*0Sstevel@tonic-gate static void 293*0Sstevel@tonic-gate intr(int sig) 294*0Sstevel@tonic-gate { 295*0Sstevel@tonic-gate longjmp(toplevel, 1); 296*0Sstevel@tonic-gate } 297*0Sstevel@tonic-gate 298*0Sstevel@tonic-gate /*ARGSUSED*/ 299*0Sstevel@tonic-gate void 300*0Sstevel@tonic-gate lostpeer(int sig) 301*0Sstevel@tonic-gate { 302*0Sstevel@tonic-gate extern FILE *ctrl_out; 303*0Sstevel@tonic-gate extern int data; 304*0Sstevel@tonic-gate 305*0Sstevel@tonic-gate if (connected) { 306*0Sstevel@tonic-gate if (ctrl_out != NULL) { 307*0Sstevel@tonic-gate (void) shutdown(fileno(ctrl_out), 1+1); 308*0Sstevel@tonic-gate (void) fclose(ctrl_out); 309*0Sstevel@tonic-gate ctrl_out = NULL; 310*0Sstevel@tonic-gate } 311*0Sstevel@tonic-gate if (data >= 0) { 312*0Sstevel@tonic-gate (void) shutdown(data, 1+1); 313*0Sstevel@tonic-gate (void) close(data); 314*0Sstevel@tonic-gate data = -1; 315*0Sstevel@tonic-gate } 316*0Sstevel@tonic-gate connected = 0; 317*0Sstevel@tonic-gate 318*0Sstevel@tonic-gate auth_type = AUTHTYPE_NONE; 319*0Sstevel@tonic-gate clevel = dlevel = PROT_C; 320*0Sstevel@tonic-gate goteof = 0; 321*0Sstevel@tonic-gate } 322*0Sstevel@tonic-gate pswitch(1); 323*0Sstevel@tonic-gate if (connected) { 324*0Sstevel@tonic-gate if (ctrl_out != NULL) { 325*0Sstevel@tonic-gate (void) shutdown(fileno(ctrl_out), 1+1); 326*0Sstevel@tonic-gate (void) fclose(ctrl_out); 327*0Sstevel@tonic-gate ctrl_out = NULL; 328*0Sstevel@tonic-gate } 329*0Sstevel@tonic-gate connected = 0; 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gate auth_type = AUTHTYPE_NONE; 332*0Sstevel@tonic-gate clevel = dlevel = PROT_C; 333*0Sstevel@tonic-gate goteof = 0; 334*0Sstevel@tonic-gate } 335*0Sstevel@tonic-gate proxflag = 0; 336*0Sstevel@tonic-gate pswitch(0); 337*0Sstevel@tonic-gate } 338*0Sstevel@tonic-gate 339*0Sstevel@tonic-gate /* 340*0Sstevel@tonic-gate * Command parser. 341*0Sstevel@tonic-gate */ 342*0Sstevel@tonic-gate static void 343*0Sstevel@tonic-gate cmdscanner(int top) 344*0Sstevel@tonic-gate { 345*0Sstevel@tonic-gate struct cmd *c; 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gate if (!top) 348*0Sstevel@tonic-gate (void) putchar('\n'); 349*0Sstevel@tonic-gate for (;;) { 350*0Sstevel@tonic-gate stop_timer(); 351*0Sstevel@tonic-gate if (fromatty) { 352*0Sstevel@tonic-gate (void) printf("ftp> "); 353*0Sstevel@tonic-gate (void) fflush(stdout); 354*0Sstevel@tonic-gate } 355*0Sstevel@tonic-gate if (fgets(line, sizeof (line), stdin) == 0) { 356*0Sstevel@tonic-gate if (feof(stdin) || ferror(stdin)) 357*0Sstevel@tonic-gate quit(0, NULL); 358*0Sstevel@tonic-gate break; 359*0Sstevel@tonic-gate } 360*0Sstevel@tonic-gate if (line[0] == 0) 361*0Sstevel@tonic-gate break; 362*0Sstevel@tonic-gate /* If not all, just discard rest of line */ 363*0Sstevel@tonic-gate if (line[strlen(line)-1] != '\n') { 364*0Sstevel@tonic-gate while (fgetc(stdin) != '\n' && !feof(stdin) && 365*0Sstevel@tonic-gate !ferror(stdin)) 366*0Sstevel@tonic-gate ; 367*0Sstevel@tonic-gate (void) printf("Line too long\n"); 368*0Sstevel@tonic-gate continue; 369*0Sstevel@tonic-gate } else 370*0Sstevel@tonic-gate line[strlen(line)-1] = 0; 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate makeargv(); 373*0Sstevel@tonic-gate if (margc == 0) { 374*0Sstevel@tonic-gate continue; 375*0Sstevel@tonic-gate } 376*0Sstevel@tonic-gate c = getcmd(margv[0]); 377*0Sstevel@tonic-gate if (c == (struct cmd *)-1) { 378*0Sstevel@tonic-gate (void) printf("?Ambiguous command\n"); 379*0Sstevel@tonic-gate continue; 380*0Sstevel@tonic-gate } 381*0Sstevel@tonic-gate if (c == 0) { 382*0Sstevel@tonic-gate (void) printf("?Invalid command\n"); 383*0Sstevel@tonic-gate continue; 384*0Sstevel@tonic-gate } 385*0Sstevel@tonic-gate if (c->c_conn && !connected) { 386*0Sstevel@tonic-gate (void) printf("Not connected.\n"); 387*0Sstevel@tonic-gate continue; 388*0Sstevel@tonic-gate } 389*0Sstevel@tonic-gate reset_timer(); 390*0Sstevel@tonic-gate (*c->c_handler)(margc, margv); 391*0Sstevel@tonic-gate #ifndef CTRL 392*0Sstevel@tonic-gate #define CTRL(c) ((c)&037) 393*0Sstevel@tonic-gate #endif 394*0Sstevel@tonic-gate stop_timer(); 395*0Sstevel@tonic-gate if (bell && c->c_bell) 396*0Sstevel@tonic-gate (void) putchar(CTRL('g')); 397*0Sstevel@tonic-gate if (c->c_handler != help) 398*0Sstevel@tonic-gate break; 399*0Sstevel@tonic-gate } 400*0Sstevel@tonic-gate (void) signal(SIGINT, intr); 401*0Sstevel@tonic-gate (void) signal(SIGPIPE, lostpeer); 402*0Sstevel@tonic-gate } 403*0Sstevel@tonic-gate 404*0Sstevel@tonic-gate struct cmd * 405*0Sstevel@tonic-gate getcmd(char *name) 406*0Sstevel@tonic-gate { 407*0Sstevel@tonic-gate char *p, *q; 408*0Sstevel@tonic-gate struct cmd *c, *found; 409*0Sstevel@tonic-gate int nmatches, longest; 410*0Sstevel@tonic-gate extern struct cmd cmdtab[]; 411*0Sstevel@tonic-gate 412*0Sstevel@tonic-gate if (name == NULL) 413*0Sstevel@tonic-gate return (0); 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gate longest = 0; 416*0Sstevel@tonic-gate nmatches = 0; 417*0Sstevel@tonic-gate found = 0; 418*0Sstevel@tonic-gate for (c = cmdtab; (p = c->c_name) != NULL; c++) { 419*0Sstevel@tonic-gate for (q = name; *q == *p++; q++) 420*0Sstevel@tonic-gate if (*q == 0) /* exact match? */ 421*0Sstevel@tonic-gate return (c); 422*0Sstevel@tonic-gate if (!*q) { /* the name was a prefix */ 423*0Sstevel@tonic-gate if (q - name > longest) { 424*0Sstevel@tonic-gate longest = q - name; 425*0Sstevel@tonic-gate nmatches = 1; 426*0Sstevel@tonic-gate found = c; 427*0Sstevel@tonic-gate } else if (q - name == longest) 428*0Sstevel@tonic-gate nmatches++; 429*0Sstevel@tonic-gate } 430*0Sstevel@tonic-gate } 431*0Sstevel@tonic-gate if (nmatches > 1) 432*0Sstevel@tonic-gate return ((struct cmd *)-1); 433*0Sstevel@tonic-gate return (found); 434*0Sstevel@tonic-gate } 435*0Sstevel@tonic-gate 436*0Sstevel@tonic-gate /* 437*0Sstevel@tonic-gate * Slice a string up into argc/argv. 438*0Sstevel@tonic-gate */ 439*0Sstevel@tonic-gate 440*0Sstevel@tonic-gate static int slrflag; 441*0Sstevel@tonic-gate #define MARGV_INC 20 442*0Sstevel@tonic-gate 443*0Sstevel@tonic-gate void 444*0Sstevel@tonic-gate makeargv(void) 445*0Sstevel@tonic-gate { 446*0Sstevel@tonic-gate char **argp; 447*0Sstevel@tonic-gate static int margv_size; 448*0Sstevel@tonic-gate 449*0Sstevel@tonic-gate margc = 0; 450*0Sstevel@tonic-gate stringbase = line; /* scan from first of buffer */ 451*0Sstevel@tonic-gate argbase = argbuf; /* store from first of buffer */ 452*0Sstevel@tonic-gate slrflag = 0; 453*0Sstevel@tonic-gate 454*0Sstevel@tonic-gate if (!margv) { 455*0Sstevel@tonic-gate margv_size = MARGV_INC; 456*0Sstevel@tonic-gate if ((margv = malloc(margv_size * sizeof (char *))) == NULL) 457*0Sstevel@tonic-gate fatal("Out of memory"); 458*0Sstevel@tonic-gate } 459*0Sstevel@tonic-gate argp = margv; 460*0Sstevel@tonic-gate while (*argp++ = slurpstring()) { 461*0Sstevel@tonic-gate margc++; 462*0Sstevel@tonic-gate if (margc == margv_size) { 463*0Sstevel@tonic-gate margv_size += MARGV_INC; 464*0Sstevel@tonic-gate if ((margv = realloc(margv, 465*0Sstevel@tonic-gate margv_size * sizeof (char *))) == NULL) 466*0Sstevel@tonic-gate fatal("Out of memory"); 467*0Sstevel@tonic-gate argp = margv + margc; 468*0Sstevel@tonic-gate } 469*0Sstevel@tonic-gate } 470*0Sstevel@tonic-gate } 471*0Sstevel@tonic-gate 472*0Sstevel@tonic-gate /* 473*0Sstevel@tonic-gate * Parse string into argbuf; 474*0Sstevel@tonic-gate * implemented with FSM to 475*0Sstevel@tonic-gate * handle quoting and strings 476*0Sstevel@tonic-gate */ 477*0Sstevel@tonic-gate static char * 478*0Sstevel@tonic-gate slurpstring(void) 479*0Sstevel@tonic-gate { 480*0Sstevel@tonic-gate int got_one = 0; 481*0Sstevel@tonic-gate char *sb = stringbase; 482*0Sstevel@tonic-gate char *ap = argbase; 483*0Sstevel@tonic-gate char *tmp = argbase; /* will return this if token found */ 484*0Sstevel@tonic-gate int len; 485*0Sstevel@tonic-gate 486*0Sstevel@tonic-gate if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 487*0Sstevel@tonic-gate switch (slrflag) { /* and $ as token for macro invoke */ 488*0Sstevel@tonic-gate case 0: 489*0Sstevel@tonic-gate slrflag++; 490*0Sstevel@tonic-gate stringbase++; 491*0Sstevel@tonic-gate return ((*sb == '!') ? "!" : "$"); 492*0Sstevel@tonic-gate case 1: 493*0Sstevel@tonic-gate slrflag++; 494*0Sstevel@tonic-gate altarg = stringbase; 495*0Sstevel@tonic-gate break; 496*0Sstevel@tonic-gate default: 497*0Sstevel@tonic-gate break; 498*0Sstevel@tonic-gate } 499*0Sstevel@tonic-gate } 500*0Sstevel@tonic-gate 501*0Sstevel@tonic-gate S0: 502*0Sstevel@tonic-gate switch (*sb) { 503*0Sstevel@tonic-gate 504*0Sstevel@tonic-gate case '\0': 505*0Sstevel@tonic-gate goto OUT; 506*0Sstevel@tonic-gate 507*0Sstevel@tonic-gate case ' ': 508*0Sstevel@tonic-gate case '\t': 509*0Sstevel@tonic-gate sb++; goto S0; 510*0Sstevel@tonic-gate 511*0Sstevel@tonic-gate default: 512*0Sstevel@tonic-gate switch (slrflag) { 513*0Sstevel@tonic-gate case 0: 514*0Sstevel@tonic-gate slrflag++; 515*0Sstevel@tonic-gate break; 516*0Sstevel@tonic-gate case 1: 517*0Sstevel@tonic-gate slrflag++; 518*0Sstevel@tonic-gate altarg = sb; 519*0Sstevel@tonic-gate break; 520*0Sstevel@tonic-gate default: 521*0Sstevel@tonic-gate break; 522*0Sstevel@tonic-gate } 523*0Sstevel@tonic-gate goto S1; 524*0Sstevel@tonic-gate } 525*0Sstevel@tonic-gate 526*0Sstevel@tonic-gate S1: 527*0Sstevel@tonic-gate switch (*sb) { 528*0Sstevel@tonic-gate 529*0Sstevel@tonic-gate case ' ': 530*0Sstevel@tonic-gate case '\t': 531*0Sstevel@tonic-gate case '\0': 532*0Sstevel@tonic-gate goto OUT; /* end of token */ 533*0Sstevel@tonic-gate 534*0Sstevel@tonic-gate case '\\': 535*0Sstevel@tonic-gate sb++; goto S2; /* slurp next character */ 536*0Sstevel@tonic-gate 537*0Sstevel@tonic-gate case '"': 538*0Sstevel@tonic-gate sb++; goto S3; /* slurp quoted string */ 539*0Sstevel@tonic-gate 540*0Sstevel@tonic-gate default: 541*0Sstevel@tonic-gate if ((len = mblen(sb, MB_CUR_MAX)) <= 0) 542*0Sstevel@tonic-gate len = 1; 543*0Sstevel@tonic-gate memcpy(ap, sb, len); 544*0Sstevel@tonic-gate ap += len; 545*0Sstevel@tonic-gate sb += len; 546*0Sstevel@tonic-gate got_one = 1; 547*0Sstevel@tonic-gate goto S1; 548*0Sstevel@tonic-gate } 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate S2: 551*0Sstevel@tonic-gate switch (*sb) { 552*0Sstevel@tonic-gate 553*0Sstevel@tonic-gate case '\0': 554*0Sstevel@tonic-gate goto OUT; 555*0Sstevel@tonic-gate 556*0Sstevel@tonic-gate default: 557*0Sstevel@tonic-gate if ((len = mblen(sb, MB_CUR_MAX)) <= 0) 558*0Sstevel@tonic-gate len = 1; 559*0Sstevel@tonic-gate memcpy(ap, sb, len); 560*0Sstevel@tonic-gate ap += len; 561*0Sstevel@tonic-gate sb += len; 562*0Sstevel@tonic-gate got_one = 1; 563*0Sstevel@tonic-gate goto S1; 564*0Sstevel@tonic-gate } 565*0Sstevel@tonic-gate 566*0Sstevel@tonic-gate S3: 567*0Sstevel@tonic-gate switch (*sb) { 568*0Sstevel@tonic-gate 569*0Sstevel@tonic-gate case '\0': 570*0Sstevel@tonic-gate goto OUT; 571*0Sstevel@tonic-gate 572*0Sstevel@tonic-gate case '"': 573*0Sstevel@tonic-gate sb++; goto S1; 574*0Sstevel@tonic-gate 575*0Sstevel@tonic-gate default: 576*0Sstevel@tonic-gate if ((len = mblen(sb, MB_CUR_MAX)) <= 0) 577*0Sstevel@tonic-gate len = 1; 578*0Sstevel@tonic-gate memcpy(ap, sb, len); 579*0Sstevel@tonic-gate ap += len; 580*0Sstevel@tonic-gate sb += len; 581*0Sstevel@tonic-gate got_one = 1; 582*0Sstevel@tonic-gate goto S3; 583*0Sstevel@tonic-gate } 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate OUT: 586*0Sstevel@tonic-gate if (got_one) 587*0Sstevel@tonic-gate *ap++ = '\0'; 588*0Sstevel@tonic-gate argbase = ap; /* update storage pointer */ 589*0Sstevel@tonic-gate stringbase = sb; /* update scan pointer */ 590*0Sstevel@tonic-gate if (got_one) { 591*0Sstevel@tonic-gate return (tmp); 592*0Sstevel@tonic-gate } 593*0Sstevel@tonic-gate switch (slrflag) { 594*0Sstevel@tonic-gate case 0: 595*0Sstevel@tonic-gate slrflag++; 596*0Sstevel@tonic-gate break; 597*0Sstevel@tonic-gate case 1: 598*0Sstevel@tonic-gate slrflag++; 599*0Sstevel@tonic-gate altarg = (char *)0; 600*0Sstevel@tonic-gate break; 601*0Sstevel@tonic-gate default: 602*0Sstevel@tonic-gate break; 603*0Sstevel@tonic-gate } 604*0Sstevel@tonic-gate return ((char *)0); 605*0Sstevel@tonic-gate } 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate #define HELPINDENT (sizeof ("directory")) 608*0Sstevel@tonic-gate 609*0Sstevel@tonic-gate /* 610*0Sstevel@tonic-gate * Help command. 611*0Sstevel@tonic-gate * Call each command handler with argc == 0 and argv[0] == name. 612*0Sstevel@tonic-gate */ 613*0Sstevel@tonic-gate void 614*0Sstevel@tonic-gate help(int argc, char *argv[]) 615*0Sstevel@tonic-gate { 616*0Sstevel@tonic-gate struct cmd *c; 617*0Sstevel@tonic-gate extern struct cmd cmdtab[]; 618*0Sstevel@tonic-gate 619*0Sstevel@tonic-gate if (argc == 1) { 620*0Sstevel@tonic-gate int i, j, w, k; 621*0Sstevel@tonic-gate int columns, width = 0, lines; 622*0Sstevel@tonic-gate extern int NCMDS; 623*0Sstevel@tonic-gate 624*0Sstevel@tonic-gate (void) printf( 625*0Sstevel@tonic-gate "Commands may be abbreviated. Commands are:\n\n"); 626*0Sstevel@tonic-gate for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 627*0Sstevel@tonic-gate int len = strlen(c->c_name); 628*0Sstevel@tonic-gate 629*0Sstevel@tonic-gate if (len > width) 630*0Sstevel@tonic-gate width = len; 631*0Sstevel@tonic-gate } 632*0Sstevel@tonic-gate width = (width + 8) &~ 7; 633*0Sstevel@tonic-gate columns = 80 / width; 634*0Sstevel@tonic-gate if (columns == 0) 635*0Sstevel@tonic-gate columns = 1; 636*0Sstevel@tonic-gate lines = (NCMDS + columns - 1) / columns; 637*0Sstevel@tonic-gate for (i = 0; i < lines; i++) { 638*0Sstevel@tonic-gate for (j = 0; j < columns; j++) { 639*0Sstevel@tonic-gate c = cmdtab + j * lines + i; 640*0Sstevel@tonic-gate if (c->c_name && (!proxy || c->c_proxy)) { 641*0Sstevel@tonic-gate (void) printf("%s", c->c_name); 642*0Sstevel@tonic-gate } else if (c->c_name) { 643*0Sstevel@tonic-gate for (k = 0; k < strlen(c->c_name); 644*0Sstevel@tonic-gate k++) { 645*0Sstevel@tonic-gate (void) putchar(' '); 646*0Sstevel@tonic-gate } 647*0Sstevel@tonic-gate } 648*0Sstevel@tonic-gate if (c + lines >= &cmdtab[NCMDS]) { 649*0Sstevel@tonic-gate (void) printf("\n"); 650*0Sstevel@tonic-gate break; 651*0Sstevel@tonic-gate } 652*0Sstevel@tonic-gate w = strlen(c->c_name); 653*0Sstevel@tonic-gate while (w < width) { 654*0Sstevel@tonic-gate w = (w + 8) &~ 7; 655*0Sstevel@tonic-gate (void) putchar('\t'); 656*0Sstevel@tonic-gate } 657*0Sstevel@tonic-gate } 658*0Sstevel@tonic-gate } 659*0Sstevel@tonic-gate return; 660*0Sstevel@tonic-gate } 661*0Sstevel@tonic-gate while (--argc > 0) { 662*0Sstevel@tonic-gate char *arg; 663*0Sstevel@tonic-gate arg = *++argv; 664*0Sstevel@tonic-gate c = getcmd(arg); 665*0Sstevel@tonic-gate if (c == (struct cmd *)-1) 666*0Sstevel@tonic-gate (void) printf("?Ambiguous help command %s\n", arg); 667*0Sstevel@tonic-gate else if (c == (struct cmd *)0) 668*0Sstevel@tonic-gate (void) printf("?Invalid help command %s\n", arg); 669*0Sstevel@tonic-gate else 670*0Sstevel@tonic-gate (void) printf("%-*s\t%s\n", HELPINDENT, 671*0Sstevel@tonic-gate c->c_name, c->c_help); 672*0Sstevel@tonic-gate } 673*0Sstevel@tonic-gate } 674*0Sstevel@tonic-gate 675*0Sstevel@tonic-gate /* 676*0Sstevel@tonic-gate * Call routine with argc, argv set from args (terminated by 0). 677*0Sstevel@tonic-gate */ 678*0Sstevel@tonic-gate void 679*0Sstevel@tonic-gate call(void (*routine)(int argc, char *argv[]), ...) 680*0Sstevel@tonic-gate { 681*0Sstevel@tonic-gate va_list ap; 682*0Sstevel@tonic-gate char *argv[10]; 683*0Sstevel@tonic-gate int argc = 0; 684*0Sstevel@tonic-gate 685*0Sstevel@tonic-gate va_start(ap, routine); 686*0Sstevel@tonic-gate while ((argv[argc] = va_arg(ap, char *)) != (char *)0) 687*0Sstevel@tonic-gate argc++; 688*0Sstevel@tonic-gate va_end(ap); 689*0Sstevel@tonic-gate (*routine)(argc, argv); 690*0Sstevel@tonic-gate } 691