Home | History | Annotate | Line # | Download | only in ftp
main.c revision 1.17
      1  1.17    lukem /*	$NetBSD: main.c,v 1.17 1997/02/01 10:45:07 lukem Exp $	*/
      2  1.12    lukem 
      3   1.1      cgd /*
      4   1.3      cgd  * Copyright (c) 1985, 1989, 1993, 1994
      5   1.3      cgd  *	The Regents of the University of California.  All rights reserved.
      6   1.1      cgd  *
      7   1.1      cgd  * Redistribution and use in source and binary forms, with or without
      8   1.1      cgd  * modification, are permitted provided that the following conditions
      9   1.1      cgd  * are met:
     10   1.1      cgd  * 1. Redistributions of source code must retain the above copyright
     11   1.1      cgd  *    notice, this list of conditions and the following disclaimer.
     12   1.1      cgd  * 2. Redistributions in binary form must reproduce the above copyright
     13   1.1      cgd  *    notice, this list of conditions and the following disclaimer in the
     14   1.1      cgd  *    documentation and/or other materials provided with the distribution.
     15   1.1      cgd  * 3. All advertising materials mentioning features or use of this software
     16   1.1      cgd  *    must display the following acknowledgement:
     17   1.1      cgd  *	This product includes software developed by the University of
     18   1.1      cgd  *	California, Berkeley and its contributors.
     19   1.1      cgd  * 4. Neither the name of the University nor the names of its contributors
     20   1.1      cgd  *    may be used to endorse or promote products derived from this software
     21   1.1      cgd  *    without specific prior written permission.
     22   1.1      cgd  *
     23   1.1      cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24   1.1      cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25   1.1      cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26   1.1      cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27   1.1      cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28   1.1      cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29   1.1      cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30   1.1      cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31   1.1      cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32   1.1      cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33   1.1      cgd  * SUCH DAMAGE.
     34   1.1      cgd  */
     35   1.1      cgd 
     36   1.1      cgd #ifndef lint
     37   1.3      cgd static char copyright[] =
     38   1.3      cgd "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
     39   1.3      cgd 	The Regents of the University of California.  All rights reserved.\n";
     40   1.1      cgd #endif /* not lint */
     41   1.1      cgd 
     42   1.1      cgd #ifndef lint
     43   1.8      tls #if 0
     44   1.8      tls static char sccsid[] = "@(#)main.c	8.6 (Berkeley) 10/9/94";
     45   1.8      tls #else
     46  1.17    lukem static char rcsid[] = "$NetBSD: main.c,v 1.17 1997/02/01 10:45:07 lukem Exp $";
     47   1.8      tls #endif
     48   1.1      cgd #endif /* not lint */
     49   1.1      cgd 
     50   1.1      cgd /*
     51   1.1      cgd  * FTP User Program -- Command Interface.
     52   1.1      cgd  */
     53   1.3      cgd #include <sys/types.h>
     54   1.1      cgd #include <sys/socket.h>
     55   1.1      cgd 
     56   1.3      cgd #include <err.h>
     57   1.1      cgd #include <netdb.h>
     58   1.1      cgd #include <pwd.h>
     59   1.3      cgd #include <stdio.h>
     60   1.9      cgd #include <string.h>
     61   1.3      cgd #include <unistd.h>
     62   1.1      cgd 
     63   1.3      cgd #include "ftp_var.h"
     64   1.1      cgd 
     65   1.3      cgd int
     66   1.1      cgd main(argc, argv)
     67   1.3      cgd 	int argc;
     68   1.1      cgd 	char *argv[];
     69   1.1      cgd {
     70  1.16    lukem 	struct servent *sp;
     71  1.16    lukem 	int ch, top, port, rval;
     72   1.1      cgd 	struct passwd *pw = NULL;
     73   1.3      cgd 	char *cp, homedir[MAXPATHLEN];
     74   1.1      cgd 
     75   1.1      cgd 	sp = getservbyname("ftp", "tcp");
     76   1.3      cgd 	if (sp == 0)
     77  1.16    lukem 		ftpport = htons(FTP_PORT);	/* good fallback */
     78  1.16    lukem 	else
     79  1.16    lukem 		ftpport = sp->s_port;
     80  1.16    lukem 	sp = getservbyname("http", "tcp");
     81  1.16    lukem 	if (sp == 0)
     82  1.16    lukem 		httpport = htons(HTTP_PORT);	/* good fallback */
     83  1.16    lukem 	else
     84  1.16    lukem 		httpport = sp->s_port;
     85   1.1      cgd 	doglob = 1;
     86   1.1      cgd 	interactive = 1;
     87   1.1      cgd 	autologin = 1;
     88  1.12    lukem 	passivemode = 0;
     89  1.13    lukem 	preserve = 1;
     90  1.17    lukem 	verbose = 0;
     91  1.17    lukem 	progress = 0;
     92  1.12    lukem 	mark = HASHBYTES;
     93  1.16    lukem 	marg_sl = sl_init();
     94   1.3      cgd 
     95  1.15    lukem 	cp = strrchr(argv[0], '/');
     96  1.15    lukem 	cp = (cp == NULL) ? argv[0] : cp + 1;
     97  1.15    lukem 	if (strcmp(cp, "pftp") == 0)
     98  1.15    lukem 		passivemode = 1;
     99  1.15    lukem 
    100  1.17    lukem 	fromatty = isatty(fileno(stdin));
    101  1.17    lukem 	if (fromatty)
    102  1.17    lukem 		verbose = 1;		/* verbose if from a tty */
    103  1.17    lukem 	if (isatty(fileno(stdout)))
    104  1.17    lukem 		progress = 1;		/* progress bar on if going to a tty */
    105  1.17    lukem 
    106  1.17    lukem 	while ((ch = getopt(argc, argv, "adginpP:tvV")) != EOF) {
    107   1.6  mycroft 		switch (ch) {
    108  1.12    lukem 		case 'a':
    109  1.12    lukem 			anonftp = 1;
    110  1.12    lukem 			break;
    111  1.12    lukem 
    112   1.3      cgd 		case 'd':
    113   1.3      cgd 			options |= SO_DEBUG;
    114   1.3      cgd 			debug++;
    115   1.3      cgd 			break;
    116  1.12    lukem 
    117   1.3      cgd 		case 'g':
    118   1.3      cgd 			doglob = 0;
    119   1.3      cgd 			break;
    120   1.1      cgd 
    121   1.3      cgd 		case 'i':
    122   1.3      cgd 			interactive = 0;
    123   1.3      cgd 			break;
    124   1.1      cgd 
    125   1.3      cgd 		case 'n':
    126   1.3      cgd 			autologin = 0;
    127   1.3      cgd 			break;
    128   1.1      cgd 
    129  1.12    lukem 		case 'p':
    130  1.12    lukem 			passivemode = 1;
    131  1.12    lukem 			break;
    132  1.12    lukem 
    133  1.12    lukem 		case 'P':
    134  1.16    lukem 			port = atoi(optarg);
    135  1.16    lukem 			if (port <= 0)
    136  1.16    lukem 				warnx("bad port number: %s", optarg);
    137  1.16    lukem 			else
    138  1.16    lukem 				ftpport = htons(port);
    139  1.12    lukem 			break;
    140  1.12    lukem 
    141   1.3      cgd 		case 't':
    142  1.17    lukem 			trace = 1;
    143   1.3      cgd 			break;
    144   1.1      cgd 
    145   1.3      cgd 		case 'v':
    146  1.17    lukem 			verbose = 1;
    147  1.17    lukem 			break;
    148  1.17    lukem 
    149  1.17    lukem 		case 'V':
    150  1.17    lukem 			verbose = 0;
    151   1.3      cgd 			break;
    152   1.1      cgd 
    153   1.3      cgd 		default:
    154  1.14    lukem 			usage();
    155   1.3      cgd 		}
    156   1.1      cgd 	}
    157   1.3      cgd 	argc -= optind;
    158   1.3      cgd 	argv += optind;
    159   1.3      cgd 
    160   1.1      cgd 	cpend = 0;	/* no pending replies */
    161   1.1      cgd 	proxy = 0;	/* proxy not active */
    162   1.1      cgd 	crflag = 1;	/* strip c.r. on ascii gets */
    163   1.1      cgd 	sendport = -1;	/* not using ports */
    164   1.1      cgd 	/*
    165   1.1      cgd 	 * Set up the home directory in case we're globbing.
    166   1.1      cgd 	 */
    167   1.1      cgd 	cp = getlogin();
    168   1.1      cgd 	if (cp != NULL) {
    169   1.1      cgd 		pw = getpwnam(cp);
    170   1.1      cgd 	}
    171   1.1      cgd 	if (pw == NULL)
    172   1.1      cgd 		pw = getpwuid(getuid());
    173   1.1      cgd 	if (pw != NULL) {
    174   1.1      cgd 		home = homedir;
    175   1.1      cgd 		(void) strcpy(home, pw->pw_dir);
    176   1.1      cgd 	}
    177  1.12    lukem 
    178  1.16    lukem #ifndef SMALLFTP
    179  1.16    lukem 	editing = 0;			/* command line editing off */
    180  1.16    lukem 	if (fromatty) {
    181  1.16    lukem 		editing = 1;		/* editing mode on if a tty */
    182  1.16    lukem 		el = el_init(__progname, stdin, stdout); /* init editline */
    183  1.16    lukem 
    184  1.16    lukem 		hist = history_init();		/* init the builtin history */
    185  1.16    lukem 		history(hist, H_EVENT, 100);	/* remember 100 events */
    186  1.16    lukem 		el_set(el, EL_HIST, history, hist);	/* use history */
    187  1.16    lukem 
    188  1.16    lukem 		el_set(el, EL_EDITOR, "emacs");	/* default editor is emacs */
    189  1.16    lukem 		el_set(el, EL_PROMPT, prompt);	/* set the prompt function */
    190  1.16    lukem 
    191  1.16    lukem 		/* add local file completion, bind to TAB */
    192  1.16    lukem 		el_set(el, EL_ADDFN, "ftp-complete",
    193  1.16    lukem 		    "Context sensitive argument completion",
    194  1.16    lukem 		    complete);
    195  1.16    lukem 		el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
    196  1.16    lukem 
    197  1.16    lukem 		el_source(el, NULL);	/* read ~/.editrc */
    198  1.12    lukem 	}
    199  1.16    lukem #endif /* !SMALLFTP */
    200  1.12    lukem 
    201  1.17    lukem 	setttywidth(0);
    202  1.17    lukem 	(void) signal(SIGWINCH, setttywidth);
    203  1.17    lukem 
    204   1.1      cgd 	if (argc > 0) {
    205  1.16    lukem 		if (strchr(argv[0], ':') != NULL) {
    206  1.16    lukem 			anonftp = 1;	/* Handle "automatic" transfers. */
    207  1.16    lukem 			rval = auto_fetch(argc, argv);
    208  1.16    lukem 			if (rval >= 0)		/* -1 == connected and cd-ed */
    209  1.16    lukem 				exit(rval);
    210  1.16    lukem 		} else {
    211  1.16    lukem 			char *xargv[5];
    212   1.3      cgd 
    213  1.16    lukem 			if (setjmp(toplevel))
    214  1.16    lukem 				exit(0);
    215  1.16    lukem 			(void) signal(SIGINT, intr);
    216  1.16    lukem 			(void) signal(SIGPIPE, lostpeer);
    217  1.16    lukem 			xargv[0] = __progname;
    218  1.16    lukem 			xargv[1] = argv[0];
    219  1.16    lukem 			xargv[2] = argv[1];
    220  1.16    lukem 			xargv[3] = argv[2];
    221  1.16    lukem 			xargv[4] = NULL;
    222  1.16    lukem 			setpeer(argc+1, xargv);
    223  1.16    lukem 		}
    224   1.1      cgd 	}
    225   1.1      cgd 	top = setjmp(toplevel) == 0;
    226   1.1      cgd 	if (top) {
    227   1.1      cgd 		(void) signal(SIGINT, intr);
    228   1.1      cgd 		(void) signal(SIGPIPE, lostpeer);
    229   1.1      cgd 	}
    230   1.1      cgd 	for (;;) {
    231   1.1      cgd 		cmdscanner(top);
    232   1.1      cgd 		top = 1;
    233   1.1      cgd 	}
    234   1.1      cgd }
    235   1.1      cgd 
    236   1.1      cgd void
    237   1.1      cgd intr()
    238   1.1      cgd {
    239   1.1      cgd 
    240  1.17    lukem 	alarmtimer(0);
    241   1.1      cgd 	longjmp(toplevel, 1);
    242   1.1      cgd }
    243   1.1      cgd 
    244   1.1      cgd void
    245   1.1      cgd lostpeer()
    246   1.1      cgd {
    247   1.1      cgd 
    248  1.17    lukem 	alarmtimer(0);
    249   1.1      cgd 	if (connected) {
    250   1.1      cgd 		if (cout != NULL) {
    251   1.1      cgd 			(void) shutdown(fileno(cout), 1+1);
    252   1.1      cgd 			(void) fclose(cout);
    253   1.1      cgd 			cout = NULL;
    254   1.1      cgd 		}
    255   1.1      cgd 		if (data >= 0) {
    256   1.1      cgd 			(void) shutdown(data, 1+1);
    257   1.1      cgd 			(void) close(data);
    258   1.1      cgd 			data = -1;
    259   1.1      cgd 		}
    260   1.1      cgd 		connected = 0;
    261   1.1      cgd 	}
    262   1.1      cgd 	pswitch(1);
    263   1.1      cgd 	if (connected) {
    264   1.1      cgd 		if (cout != NULL) {
    265   1.1      cgd 			(void) shutdown(fileno(cout), 1+1);
    266   1.1      cgd 			(void) fclose(cout);
    267   1.1      cgd 			cout = NULL;
    268   1.1      cgd 		}
    269   1.1      cgd 		connected = 0;
    270   1.1      cgd 	}
    271   1.1      cgd 	proxflag = 0;
    272   1.1      cgd 	pswitch(0);
    273   1.1      cgd }
    274   1.1      cgd 
    275   1.3      cgd /*
    276  1.16    lukem  * Generate a prompt
    277  1.16    lukem  */
    278   1.3      cgd char *
    279  1.16    lukem prompt()
    280   1.1      cgd {
    281  1.16    lukem 	return ("ftp> ");
    282   1.1      cgd }
    283   1.3      cgd 
    284   1.1      cgd /*
    285   1.1      cgd  * Command parser.
    286   1.1      cgd  */
    287   1.3      cgd void
    288   1.1      cgd cmdscanner(top)
    289   1.1      cgd 	int top;
    290   1.1      cgd {
    291   1.3      cgd 	struct cmd *c;
    292  1.16    lukem 	int num;
    293   1.1      cgd 
    294  1.16    lukem 	if (!top
    295  1.16    lukem #ifndef SMALLFTP
    296  1.16    lukem 	    && !editing
    297  1.16    lukem #endif /* !SMALLFTP */
    298  1.16    lukem 	    )
    299   1.1      cgd 		(void) putchar('\n');
    300   1.1      cgd 	for (;;) {
    301  1.16    lukem #ifndef SMALLFTP
    302  1.16    lukem 		if (!editing) {
    303  1.16    lukem #endif /* !SMALLFTP */
    304  1.16    lukem 			if (fromatty) {
    305  1.16    lukem 				printf("%s", prompt());
    306  1.16    lukem 				(void) fflush(stdout);
    307  1.16    lukem 			}
    308  1.16    lukem 			if (fgets(line, sizeof(line), stdin) == NULL)
    309  1.16    lukem 				quit(0, 0);
    310  1.16    lukem 			num = strlen(line);
    311  1.16    lukem 			if (num == 0)
    312  1.16    lukem 				break;
    313  1.16    lukem 			if (line[--num] == '\n') {
    314  1.16    lukem 				if (num == 0)
    315  1.16    lukem 					break;
    316  1.16    lukem 				line[num] = '\0';
    317  1.16    lukem 			} else if (num == sizeof(line) - 2) {
    318  1.16    lukem 				printf("sorry, input line too long\n");
    319  1.16    lukem 				while ((num = getchar()) != '\n' && num != EOF)
    320  1.16    lukem 					/* void */;
    321  1.16    lukem 				break;
    322  1.16    lukem 			} /* else it was a line without a newline */
    323  1.16    lukem #ifndef SMALLFTP
    324  1.17    lukem 		} else {
    325  1.16    lukem 			const char *buf;
    326  1.16    lukem 			cursor_pos = NULL;
    327  1.16    lukem 
    328  1.16    lukem 			if ((buf = el_gets(el, &num)) == NULL || num == 0)
    329  1.17    lukem 				quit(0, 0);
    330  1.16    lukem 			if (line[--num] == '\n') {
    331  1.16    lukem 				if (num == 0)
    332  1.16    lukem 					break;
    333  1.16    lukem 			} else if (num >= sizeof(line)) {
    334  1.16    lukem 				printf("sorry, input line too long\n");
    335   1.1      cgd 				break;
    336  1.16    lukem 			}
    337  1.16    lukem 			memcpy(line, buf, num);
    338  1.16    lukem 			line[num] = '\0';
    339  1.16    lukem 			history(hist, H_ENTER, buf);
    340  1.16    lukem 		}
    341  1.16    lukem #endif /* !SMALLFTP */
    342  1.16    lukem 
    343   1.1      cgd 		makeargv();
    344  1.16    lukem 		if (margc == 0)
    345   1.1      cgd 			continue;
    346  1.17    lukem #if 0 && !defined(SMALLFTP)	/* XXX: don't want el_parse */
    347  1.16    lukem 		/*
    348  1.16    lukem 		 * el_parse returns -1 to signal that it's not been handled
    349  1.16    lukem 		 * internally.
    350  1.16    lukem 		 */
    351  1.16    lukem 		if (el_parse(el, margc, margv) != -1)
    352  1.16    lukem 			continue;
    353  1.16    lukem #endif /* !SMALLFTP */
    354   1.1      cgd 		c = getcmd(margv[0]);
    355   1.1      cgd 		if (c == (struct cmd *)-1) {
    356   1.1      cgd 			printf("?Ambiguous command\n");
    357   1.1      cgd 			continue;
    358   1.1      cgd 		}
    359   1.1      cgd 		if (c == 0) {
    360   1.1      cgd 			printf("?Invalid command\n");
    361   1.1      cgd 			continue;
    362   1.1      cgd 		}
    363   1.1      cgd 		if (c->c_conn && !connected) {
    364   1.1      cgd 			printf("Not connected.\n");
    365   1.1      cgd 			continue;
    366   1.1      cgd 		}
    367  1.13    lukem 		confirmrest = 0;
    368   1.1      cgd 		(*c->c_handler)(margc, margv);
    369   1.1      cgd 		if (bell && c->c_bell)
    370   1.1      cgd 			(void) putchar('\007');
    371   1.1      cgd 		if (c->c_handler != help)
    372   1.1      cgd 			break;
    373   1.1      cgd 	}
    374   1.1      cgd 	(void) signal(SIGINT, intr);
    375   1.1      cgd 	(void) signal(SIGPIPE, lostpeer);
    376   1.1      cgd }
    377   1.1      cgd 
    378   1.1      cgd struct cmd *
    379   1.1      cgd getcmd(name)
    380  1.13    lukem 	const char *name;
    381   1.1      cgd {
    382  1.13    lukem 	const char *p, *q;
    383   1.3      cgd 	struct cmd *c, *found;
    384   1.3      cgd 	int nmatches, longest;
    385  1.11       pk 
    386  1.11       pk 	if (name == NULL)
    387  1.11       pk 		return (0);
    388   1.1      cgd 
    389   1.1      cgd 	longest = 0;
    390   1.1      cgd 	nmatches = 0;
    391   1.1      cgd 	found = 0;
    392  1.12    lukem 	for (c = cmdtab; (p = c->c_name) != NULL; c++) {
    393   1.1      cgd 		for (q = name; *q == *p++; q++)
    394   1.1      cgd 			if (*q == 0)		/* exact match? */
    395   1.1      cgd 				return (c);
    396   1.1      cgd 		if (!*q) {			/* the name was a prefix */
    397   1.1      cgd 			if (q - name > longest) {
    398   1.1      cgd 				longest = q - name;
    399   1.1      cgd 				nmatches = 1;
    400   1.1      cgd 				found = c;
    401   1.1      cgd 			} else if (q - name == longest)
    402   1.1      cgd 				nmatches++;
    403   1.1      cgd 		}
    404   1.1      cgd 	}
    405   1.1      cgd 	if (nmatches > 1)
    406   1.1      cgd 		return ((struct cmd *)-1);
    407   1.1      cgd 	return (found);
    408   1.1      cgd }
    409   1.1      cgd 
    410   1.1      cgd /*
    411   1.1      cgd  * Slice a string up into argc/argv.
    412   1.1      cgd  */
    413   1.1      cgd 
    414   1.1      cgd int slrflag;
    415   1.1      cgd 
    416   1.3      cgd void
    417   1.1      cgd makeargv()
    418   1.1      cgd {
    419  1.16    lukem 	char *argp;
    420   1.1      cgd 
    421   1.1      cgd 	stringbase = line;		/* scan from first of buffer */
    422   1.1      cgd 	argbase = argbuf;		/* store from first of buffer */
    423   1.1      cgd 	slrflag = 0;
    424  1.16    lukem 	marg_sl->sl_cur = 0;		/* reset to start of marg_sl */
    425  1.10       pk 	for (margc = 0; ; margc++) {
    426  1.16    lukem 		argp = slurpstring();
    427  1.16    lukem 		sl_add(marg_sl, argp);
    428  1.16    lukem 		if (argp == NULL)
    429  1.10       pk 			break;
    430  1.10       pk 	}
    431  1.16    lukem #ifndef SMALLFTP
    432  1.16    lukem 	if (cursor_pos == line) {
    433  1.16    lukem 		cursor_argc = 0;
    434  1.16    lukem 		cursor_argo = 0;
    435  1.16    lukem 	} else if (cursor_pos != NULL) {
    436  1.16    lukem 		cursor_argc = margc;
    437  1.16    lukem 		cursor_argo = strlen(margv[margc-1]);
    438  1.16    lukem 	}
    439  1.16    lukem #endif /* !SMALLFTP */
    440  1.16    lukem }
    441  1.10       pk 
    442  1.16    lukem #ifdef SMALLFTP
    443  1.16    lukem #define INC_CHKCURSOR(x)	(x)++
    444  1.16    lukem #else  /* !SMALLFTP */
    445  1.16    lukem #define INC_CHKCURSOR(x)	{ (x)++ ; \
    446  1.16    lukem 				if (x == cursor_pos) { \
    447  1.16    lukem 					cursor_argc = margc; \
    448  1.16    lukem 					cursor_argo = ap-argbase; \
    449  1.16    lukem 					cursor_pos = NULL; \
    450  1.16    lukem 				} }
    451  1.16    lukem 
    452  1.16    lukem #endif /* !SMALLFTP */
    453   1.1      cgd 
    454   1.1      cgd /*
    455   1.1      cgd  * Parse string into argbuf;
    456   1.1      cgd  * implemented with FSM to
    457   1.1      cgd  * handle quoting and strings
    458   1.1      cgd  */
    459   1.1      cgd char *
    460   1.1      cgd slurpstring()
    461   1.1      cgd {
    462   1.1      cgd 	int got_one = 0;
    463   1.3      cgd 	char *sb = stringbase;
    464   1.3      cgd 	char *ap = argbase;
    465   1.1      cgd 	char *tmp = argbase;		/* will return this if token found */
    466   1.1      cgd 
    467   1.1      cgd 	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
    468   1.1      cgd 		switch (slrflag) {	/* and $ as token for macro invoke */
    469   1.1      cgd 			case 0:
    470   1.1      cgd 				slrflag++;
    471  1.16    lukem 				INC_CHKCURSOR(stringbase);
    472   1.1      cgd 				return ((*sb == '!') ? "!" : "$");
    473   1.1      cgd 				/* NOTREACHED */
    474   1.1      cgd 			case 1:
    475   1.1      cgd 				slrflag++;
    476   1.1      cgd 				altarg = stringbase;
    477   1.1      cgd 				break;
    478   1.1      cgd 			default:
    479   1.1      cgd 				break;
    480   1.1      cgd 		}
    481   1.1      cgd 	}
    482   1.1      cgd 
    483   1.1      cgd S0:
    484   1.1      cgd 	switch (*sb) {
    485   1.1      cgd 
    486   1.1      cgd 	case '\0':
    487   1.1      cgd 		goto OUT;
    488   1.1      cgd 
    489   1.1      cgd 	case ' ':
    490   1.1      cgd 	case '\t':
    491  1.16    lukem 		INC_CHKCURSOR(sb);
    492  1.16    lukem 		goto S0;
    493   1.1      cgd 
    494   1.1      cgd 	default:
    495   1.1      cgd 		switch (slrflag) {
    496   1.1      cgd 			case 0:
    497   1.1      cgd 				slrflag++;
    498   1.1      cgd 				break;
    499   1.1      cgd 			case 1:
    500   1.1      cgd 				slrflag++;
    501   1.1      cgd 				altarg = sb;
    502   1.1      cgd 				break;
    503   1.1      cgd 			default:
    504   1.1      cgd 				break;
    505   1.1      cgd 		}
    506   1.1      cgd 		goto S1;
    507   1.1      cgd 	}
    508   1.1      cgd 
    509   1.1      cgd S1:
    510   1.1      cgd 	switch (*sb) {
    511   1.1      cgd 
    512   1.1      cgd 	case ' ':
    513   1.1      cgd 	case '\t':
    514   1.1      cgd 	case '\0':
    515   1.1      cgd 		goto OUT;	/* end of token */
    516   1.1      cgd 
    517   1.1      cgd 	case '\\':
    518  1.16    lukem 		INC_CHKCURSOR(sb);
    519  1.16    lukem 		goto S2;	/* slurp next character */
    520   1.1      cgd 
    521   1.1      cgd 	case '"':
    522  1.16    lukem 		INC_CHKCURSOR(sb);
    523  1.16    lukem 		goto S3;	/* slurp quoted string */
    524   1.1      cgd 
    525   1.1      cgd 	default:
    526  1.16    lukem 		*ap = *sb;	/* add character to token */
    527  1.16    lukem 		ap++;
    528  1.16    lukem 		INC_CHKCURSOR(sb);
    529   1.1      cgd 		got_one = 1;
    530   1.1      cgd 		goto S1;
    531   1.1      cgd 	}
    532   1.1      cgd 
    533   1.1      cgd S2:
    534   1.1      cgd 	switch (*sb) {
    535   1.1      cgd 
    536   1.1      cgd 	case '\0':
    537   1.1      cgd 		goto OUT;
    538   1.1      cgd 
    539   1.1      cgd 	default:
    540  1.16    lukem 		*ap = *sb;
    541  1.16    lukem 		ap++;
    542  1.16    lukem 		INC_CHKCURSOR(sb);
    543   1.1      cgd 		got_one = 1;
    544   1.1      cgd 		goto S1;
    545   1.1      cgd 	}
    546   1.1      cgd 
    547   1.1      cgd S3:
    548   1.1      cgd 	switch (*sb) {
    549   1.1      cgd 
    550   1.1      cgd 	case '\0':
    551   1.1      cgd 		goto OUT;
    552   1.1      cgd 
    553   1.1      cgd 	case '"':
    554  1.16    lukem 		INC_CHKCURSOR(sb);
    555  1.16    lukem 		goto S1;
    556   1.1      cgd 
    557   1.1      cgd 	default:
    558  1.16    lukem 		*ap = *sb;
    559  1.16    lukem 		ap++;
    560  1.16    lukem 		INC_CHKCURSOR(sb);
    561   1.1      cgd 		got_one = 1;
    562   1.1      cgd 		goto S3;
    563   1.1      cgd 	}
    564   1.1      cgd 
    565   1.1      cgd OUT:
    566   1.1      cgd 	if (got_one)
    567   1.1      cgd 		*ap++ = '\0';
    568   1.1      cgd 	argbase = ap;			/* update storage pointer */
    569   1.1      cgd 	stringbase = sb;		/* update scan pointer */
    570   1.1      cgd 	if (got_one) {
    571   1.3      cgd 		return (tmp);
    572   1.1      cgd 	}
    573   1.1      cgd 	switch (slrflag) {
    574   1.1      cgd 		case 0:
    575   1.1      cgd 			slrflag++;
    576   1.1      cgd 			break;
    577   1.1      cgd 		case 1:
    578   1.1      cgd 			slrflag++;
    579   1.1      cgd 			altarg = (char *) 0;
    580   1.1      cgd 			break;
    581   1.1      cgd 		default:
    582   1.1      cgd 			break;
    583   1.1      cgd 	}
    584   1.3      cgd 	return ((char *)0);
    585   1.1      cgd }
    586   1.1      cgd 
    587   1.1      cgd /*
    588   1.1      cgd  * Help command.
    589   1.1      cgd  * Call each command handler with argc == 0 and argv[0] == name.
    590   1.1      cgd  */
    591   1.3      cgd void
    592   1.1      cgd help(argc, argv)
    593   1.1      cgd 	int argc;
    594   1.1      cgd 	char *argv[];
    595   1.1      cgd {
    596   1.3      cgd 	struct cmd *c;
    597   1.1      cgd 
    598   1.1      cgd 	if (argc == 1) {
    599  1.16    lukem 		StringList *buf;
    600   1.1      cgd 
    601  1.16    lukem 		buf = sl_init();
    602  1.16    lukem 		printf("%sommands may be abbreviated.  Commands are:\n\n",
    603  1.16    lukem 		    proxy ? "Proxy c" : "C");
    604  1.16    lukem 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
    605  1.16    lukem 			if (c->c_name && (!proxy || c->c_proxy))
    606  1.16    lukem 				sl_add(buf, c->c_name);
    607  1.16    lukem 		list_vertical(buf);
    608  1.16    lukem 		sl_free(buf, 0);
    609   1.1      cgd 		return;
    610   1.1      cgd 	}
    611  1.16    lukem 
    612  1.16    lukem #define HELPINDENT ((int) sizeof ("disconnect"))
    613  1.16    lukem 
    614   1.1      cgd 	while (--argc > 0) {
    615   1.3      cgd 		char *arg;
    616  1.16    lukem 
    617   1.1      cgd 		arg = *++argv;
    618   1.1      cgd 		c = getcmd(arg);
    619   1.1      cgd 		if (c == (struct cmd *)-1)
    620   1.1      cgd 			printf("?Ambiguous help command %s\n", arg);
    621   1.1      cgd 		else if (c == (struct cmd *)0)
    622   1.1      cgd 			printf("?Invalid help command %s\n", arg);
    623   1.1      cgd 		else
    624   1.1      cgd 			printf("%-*s\t%s\n", HELPINDENT,
    625   1.1      cgd 				c->c_name, c->c_help);
    626   1.1      cgd 	}
    627  1.12    lukem }
    628  1.12    lukem 
    629  1.14    lukem void
    630  1.14    lukem usage()
    631  1.14    lukem {
    632  1.14    lukem 	(void)fprintf(stderr,
    633  1.17    lukem 	    "usage: %s [-adginptvV] [host [port]]\n"
    634  1.16    lukem 	    "       %s host:path[/]\n"
    635  1.16    lukem 	    "       %s ftp://host[:port]/path[/]\n"
    636  1.16    lukem 	    "       %s http://host[:port]/file\n",
    637  1.16    lukem 	    __progname, __progname, __progname, __progname);
    638  1.14    lukem 	exit(1);
    639   1.1      cgd }
    640