xref: /csrg-svn/usr.bin/ftp/main.c (revision 13968)
110297Ssam #ifndef lint
2*13968Ssam static char sccsid[] = "@(#)main.c	4.9 (Berkeley) 07/18/83";
310297Ssam #endif
410297Ssam 
510297Ssam /*
610297Ssam  * FTP User Program -- Command Interface.
710297Ssam  */
811352Ssam #include <sys/param.h>
910297Ssam #include <sys/socket.h>
1010297Ssam #include <sys/ioctl.h>
1110297Ssam 
1212398Ssam #include <arpa/ftp.h>
1312398Ssam 
1410297Ssam #include <signal.h>
1510297Ssam #include <stdio.h>
1610297Ssam #include <errno.h>
1710297Ssam #include <ctype.h>
1810297Ssam #include <netdb.h>
1911352Ssam #include <pwd.h>
2010297Ssam 
2110297Ssam #include "ftp_var.h"
2210297Ssam 
2310297Ssam int	intr();
2410297Ssam int	lostpeer();
2511352Ssam extern	char *home;
2610297Ssam 
2710297Ssam main(argc, argv)
2810297Ssam 	char *argv[];
2910297Ssam {
3010297Ssam 	register char *cp;
3110297Ssam 	int top;
3211352Ssam 	struct passwd *pw;
3311352Ssam 	char homedir[MAXPATHLEN];
3410297Ssam 
3510297Ssam 	sp = getservbyname("ftp", "tcp");
3610297Ssam 	if (sp == 0) {
3710297Ssam 		fprintf(stderr, "ftp: ftp/tcp: unknown service\n");
3810297Ssam 		exit(1);
3910297Ssam 	}
4011351Ssam 	doglob = 1;
4111654Ssam 	interactive = 1;
4211351Ssam 	autologin = 1;
4310297Ssam 	argc--, argv++;
4410297Ssam 	while (argc > 0 && **argv == '-') {
4510297Ssam 		for (cp = *argv + 1; *cp; cp++)
4610297Ssam 			switch (*cp) {
4710297Ssam 
4810297Ssam 			case 'd':
4910297Ssam 				options |= SO_DEBUG;
5010297Ssam 				debug++;
5110297Ssam 				break;
5210297Ssam 
5310297Ssam 			case 'v':
5410297Ssam 				verbose++;
5510297Ssam 				break;
5610297Ssam 
5710297Ssam 			case 't':
5810297Ssam 				trace++;
5910297Ssam 				break;
6010297Ssam 
6110297Ssam 			case 'i':
6211654Ssam 				interactive = 0;
6310297Ssam 				break;
6410297Ssam 
6510297Ssam 			case 'n':
6610297Ssam 				autologin = 0;
6710297Ssam 				break;
6810297Ssam 
6911351Ssam 			case 'g':
7011351Ssam 				doglob = 0;
7111351Ssam 				break;
7211351Ssam 
7310297Ssam 			default:
7410297Ssam 				fprintf(stderr,
7510297Ssam 				  "ftp: %c: unknown option\n", *cp);
7610297Ssam 				exit(1);
7710297Ssam 			}
7810297Ssam 		argc--, argv++;
7910297Ssam 	}
8010297Ssam 	fromatty = isatty(fileno(stdin));
8110297Ssam 	/*
8210297Ssam 	 * Set up defaults for FTP.
8310297Ssam 	 */
8410297Ssam 	strcpy(typename, "ascii"), type = TYPE_A;
8510297Ssam 	strcpy(formname, "non-print"), form = FORM_N;
8610297Ssam 	strcpy(modename, "stream"), mode = MODE_S;
8710297Ssam 	strcpy(structname, "file"), stru = STRU_F;
8811227Ssam 	strcpy(bytename, "8"), bytesize = 8;
8910297Ssam 	if (fromatty)
9010297Ssam 		verbose++;
9111352Ssam 	/*
9211352Ssam 	 * Set up the home directory in case we're globbing.
9311352Ssam 	 */
9411352Ssam 	pw = getpwnam(getlogin());
9511352Ssam 	if (pw == NULL)
9611352Ssam 		pw = getpwuid(getuid());
9711352Ssam 	if (pw != NULL) {
9811352Ssam 		home = homedir;
9911352Ssam 		strcpy(home, pw->pw_dir);
10011352Ssam 	}
10110297Ssam 	if (argc > 0) {
10210297Ssam 		if (setjmp(toplevel))
10310297Ssam 			exit(0);
10412993Ssam 		signal(SIGINT, intr);
10512993Ssam 		signal(SIGPIPE, lostpeer);
10610297Ssam 		setpeer(argc + 1, argv - 1);
10710297Ssam 	}
10810297Ssam 	top = setjmp(toplevel) == 0;
10910297Ssam 	if (top) {
11012993Ssam 		signal(SIGINT, intr);
11112993Ssam 		signal(SIGPIPE, lostpeer);
11210297Ssam 	}
11310297Ssam 	for (;;) {
11410297Ssam 		cmdscanner(top);
11510297Ssam 		top = 1;
11610297Ssam 	}
11710297Ssam }
11810297Ssam 
11910297Ssam intr()
12010297Ssam {
12110297Ssam 
12210297Ssam 	longjmp(toplevel, 1);
12310297Ssam }
12410297Ssam 
12510297Ssam lostpeer()
12610297Ssam {
12710297Ssam 	extern FILE *cout;
12810297Ssam 	extern int data;
12910297Ssam 
13010297Ssam 	if (connected) {
13110297Ssam 		if (cout != NULL) {
13211239Ssam 			shutdown(fileno(cout), 1+1);
13310297Ssam 			fclose(cout);
13410297Ssam 			cout = NULL;
13510297Ssam 		}
13610297Ssam 		if (data >= 0) {
13711239Ssam 			shutdown(data, 1+1);
13810297Ssam 			(void) close(data);
13910297Ssam 			data = -1;
14010297Ssam 		}
14110297Ssam 		connected = 0;
14210297Ssam 	}
14310297Ssam 	longjmp(toplevel, 1);
14410297Ssam }
14510297Ssam 
14610297Ssam char *
14710297Ssam tail(filename)
14810297Ssam 	char *filename;
14910297Ssam {
15010297Ssam 	register char *s;
15110297Ssam 
15210297Ssam 	while (*filename) {
15310297Ssam 		s = rindex(filename, '/');
15410297Ssam 		if (s == NULL)
15510297Ssam 			break;
15610297Ssam 		if (s[1])
15710297Ssam 			return (s + 1);
15810297Ssam 		*s = '\0';
15910297Ssam 	}
16010297Ssam 	return (filename);
16110297Ssam }
16210297Ssam 
16310297Ssam /*
16410297Ssam  * Command parser.
16510297Ssam  */
16610297Ssam cmdscanner(top)
16710297Ssam 	int top;
16810297Ssam {
16910297Ssam 	register struct cmd *c;
17010297Ssam 	struct cmd *getcmd();
17110297Ssam 	extern struct cmd cmdtab[];
17210297Ssam 	extern int help();
17310297Ssam 
17410297Ssam 	if (!top)
17510297Ssam 		putchar('\n');
17610297Ssam 	for (;;) {
17710297Ssam 		if (fromatty) {
17810297Ssam 			printf("ftp> ");
17910297Ssam 			fflush(stdout);
18010297Ssam 		}
181*13968Ssam 		if (gets(line) == 0) {
182*13968Ssam 			if (feof(stdin)) {
183*13968Ssam 				clearerr(stdin);
184*13968Ssam 				putchar('\n');
185*13968Ssam 			}
18610297Ssam 			break;
187*13968Ssam 		}
18810297Ssam 		if (line[0] == 0)
18910297Ssam 			break;
19010297Ssam 		makeargv();
19110297Ssam 		c = getcmd(margv[0]);
19210297Ssam 		if (c == (struct cmd *)-1) {
19310297Ssam 			printf("?Ambiguous command\n");
19410297Ssam 			continue;
19510297Ssam 		}
19610297Ssam 		if (c == 0) {
19710297Ssam 			printf("?Invalid command\n");
19810297Ssam 			continue;
19910297Ssam 		}
20011654Ssam 		if (c->c_conn && !connected) {
20111654Ssam 			printf ("Not connected.\n");
20211654Ssam 			continue;
20311654Ssam 		}
20410297Ssam 		(*c->c_handler)(margc, margv);
20510297Ssam 		if (bell && c->c_bell)
20610297Ssam 			putchar(CTRL(g));
20710297Ssam 		if (c->c_handler != help)
20810297Ssam 			break;
20910297Ssam 	}
21010297Ssam 	longjmp(toplevel, 0);
21110297Ssam }
21210297Ssam 
21310297Ssam struct cmd *
21410297Ssam getcmd(name)
21510297Ssam 	register char *name;
21610297Ssam {
21710297Ssam 	register char *p, *q;
21810297Ssam 	register struct cmd *c, *found;
21910297Ssam 	register int nmatches, longest;
22010297Ssam 
22110297Ssam 	longest = 0;
22210297Ssam 	nmatches = 0;
22310297Ssam 	found = 0;
22410297Ssam 	for (c = cmdtab; p = c->c_name; c++) {
22510297Ssam 		for (q = name; *q == *p++; q++)
22610297Ssam 			if (*q == 0)		/* exact match? */
22710297Ssam 				return (c);
22810297Ssam 		if (!*q) {			/* the name was a prefix */
22910297Ssam 			if (q - name > longest) {
23010297Ssam 				longest = q - name;
23110297Ssam 				nmatches = 1;
23210297Ssam 				found = c;
23310297Ssam 			} else if (q - name == longest)
23410297Ssam 				nmatches++;
23510297Ssam 		}
23610297Ssam 	}
23710297Ssam 	if (nmatches > 1)
23810297Ssam 		return ((struct cmd *)-1);
23910297Ssam 	return (found);
24010297Ssam }
24110297Ssam 
24210297Ssam /*
24310297Ssam  * Slice a string up into argc/argv.
24410297Ssam  */
24510297Ssam makeargv()
24610297Ssam {
24710297Ssam 	char **argp;
24810297Ssam 	char *slurpstring();
24910297Ssam 
25010297Ssam 	margc = 0;
25110297Ssam 	argp = margv;
25210297Ssam 	stringbase = line;		/* scan from first of buffer */
25310297Ssam 	argbase = argbuf;		/* store from first of buffer */
25410297Ssam 	while (*argp++ = slurpstring())
25510297Ssam 		margc++;
25610297Ssam }
25710297Ssam 
25810297Ssam /*
25910297Ssam  * Parse string into argbuf;
26010297Ssam  * implemented with FSM to
26110297Ssam  * handle quoting and strings
26210297Ssam  */
26310297Ssam char *
26410297Ssam slurpstring()
26510297Ssam {
26610297Ssam 	int got_one = 0;
26710297Ssam 	register char *sb = stringbase;
26810297Ssam 	register char *ap = argbase;
26910297Ssam 	char *tmp = argbase;		/* will return this if token found */
27010297Ssam 
27111654Ssam 	if (*sb == '!') {		/* recognize ! as a token for shell */
27211654Ssam 		stringbase++;
27311654Ssam 		return ("!");
27411654Ssam 	}
27510297Ssam S0:
27610297Ssam 	switch (*sb) {
27710297Ssam 
27810297Ssam 	case '\0':
27910297Ssam 		goto OUT;
28010297Ssam 
28110297Ssam 	case ' ':
28210297Ssam 	case '\t':
28310297Ssam 		sb++; goto S0;
28410297Ssam 
28510297Ssam 	default:
28610297Ssam 		goto S1;
28710297Ssam 	}
28810297Ssam 
28910297Ssam S1:
29010297Ssam 	switch (*sb) {
29110297Ssam 
29210297Ssam 	case ' ':
29310297Ssam 	case '\t':
29410297Ssam 	case '\0':
29510297Ssam 		goto OUT;	/* end of token */
29610297Ssam 
29710297Ssam 	case '\\':
29810297Ssam 		sb++; goto S2;	/* slurp next character */
29910297Ssam 
30010297Ssam 	case '"':
30110297Ssam 		sb++; goto S3;	/* slurp quoted string */
30210297Ssam 
30310297Ssam 	default:
30410297Ssam 		*ap++ = *sb++;	/* add character to token */
30510297Ssam 		got_one = 1;
30610297Ssam 		goto S1;
30710297Ssam 	}
30810297Ssam 
30910297Ssam S2:
31010297Ssam 	switch (*sb) {
31110297Ssam 
31210297Ssam 	case '\0':
31310297Ssam 		goto OUT;
31410297Ssam 
31510297Ssam 	default:
31610297Ssam 		*ap++ = *sb++;
31710297Ssam 		got_one = 1;
31810297Ssam 		goto S1;
31910297Ssam 	}
32010297Ssam 
32110297Ssam S3:
32210297Ssam 	switch (*sb) {
32310297Ssam 
32410297Ssam 	case '\0':
32510297Ssam 		goto OUT;
32610297Ssam 
32710297Ssam 	case '"':
32810297Ssam 		sb++; goto S1;
32910297Ssam 
33010297Ssam 	default:
33110297Ssam 		*ap++ = *sb++;
33210297Ssam 		got_one = 1;
33310297Ssam 		goto S3;
33410297Ssam 	}
33510297Ssam 
33610297Ssam OUT:
33710297Ssam 	if (got_one)
33810297Ssam 		*ap++ = '\0';
33910297Ssam 	argbase = ap;			/* update storage pointer */
34010297Ssam 	stringbase = sb;		/* update scan pointer */
34110297Ssam 	if (got_one)
34210297Ssam 		return(tmp);
34310297Ssam 	return((char *)0);
34410297Ssam }
34510297Ssam 
34610297Ssam #define HELPINDENT (sizeof ("directory"))
34710297Ssam 
34810297Ssam /*
34910297Ssam  * Help command.
35010297Ssam  * Call each command handler with argc == 0 and argv[0] == name.
35110297Ssam  */
35210297Ssam help(argc, argv)
35310297Ssam 	int argc;
35410297Ssam 	char *argv[];
35510297Ssam {
35610297Ssam 	register struct cmd *c;
35710297Ssam 
35810297Ssam 	if (argc == 1) {
35910297Ssam 		register int i, j, w;
36010297Ssam 		int columns, width = 0, lines;
36110297Ssam 		extern int NCMDS;
36210297Ssam 
36310297Ssam 		printf("Commands may be abbreviated.  Commands are:\n\n");
36410297Ssam 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
36510297Ssam 			int len = strlen(c->c_name);
36610297Ssam 
36710297Ssam 			if (len > width)
36810297Ssam 				width = len;
36910297Ssam 		}
37010297Ssam 		width = (width + 8) &~ 7;
37110297Ssam 		columns = 80 / width;
37210297Ssam 		if (columns == 0)
37310297Ssam 			columns = 1;
37410297Ssam 		lines = (NCMDS + columns - 1) / columns;
37510297Ssam 		for (i = 0; i < lines; i++) {
37610297Ssam 			for (j = 0; j < columns; j++) {
37710297Ssam 				c = cmdtab + j * lines + i;
37810297Ssam 				printf("%s", c->c_name);
37910297Ssam 				if (c + lines >= &cmdtab[NCMDS]) {
38010297Ssam 					printf("\n");
38110297Ssam 					break;
38210297Ssam 				}
38310297Ssam 				w = strlen(c->c_name);
38410297Ssam 				while (w < width) {
38510297Ssam 					w = (w + 8) &~ 7;
38610297Ssam 					putchar('\t');
38710297Ssam 				}
38810297Ssam 			}
38910297Ssam 		}
39010297Ssam 		return;
39110297Ssam 	}
39210297Ssam 	while (--argc > 0) {
39310297Ssam 		register char *arg;
39410297Ssam 		arg = *++argv;
39510297Ssam 		c = getcmd(arg);
39610297Ssam 		if (c == (struct cmd *)-1)
39710297Ssam 			printf("?Ambiguous help command %s\n", arg);
39810297Ssam 		else if (c == (struct cmd *)0)
39910297Ssam 			printf("?Invalid help command %s\n", arg);
40010297Ssam 		else
40110297Ssam 			printf("%-*s\t%s\n", HELPINDENT,
40210297Ssam 				c->c_name, c->c_help);
40310297Ssam 	}
40410297Ssam }
40510297Ssam 
40610297Ssam /*
40710297Ssam  * Call routine with argc, argv set from args (terminated by 0).
40810297Ssam  */
40910297Ssam /* VARARGS2 */
41010297Ssam call(routine, args)
41110297Ssam 	int (*routine)();
41210297Ssam 	int args;
41310297Ssam {
41410297Ssam 	register int *argp;
41510297Ssam 	register int argc;
41610297Ssam 
41710297Ssam 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
41810297Ssam 		;
41910297Ssam 	(*routine)(argc, &args);
42010297Ssam }
421