xref: /csrg-svn/usr.bin/ftp/main.c (revision 25907)
121740Sdist /*
221740Sdist  * Copyright (c) 1983 Regents of the University of California.
321740Sdist  * All rights reserved.  The Berkeley software License Agreement
421740Sdist  * specifies the terms and conditions for redistribution.
521740Sdist  */
621740Sdist 
710297Ssam #ifndef lint
821740Sdist char copyright[] =
921740Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
1021740Sdist  All rights reserved.\n";
1121740Sdist #endif not lint
1210297Ssam 
1321740Sdist #ifndef lint
14*25907Smckusick static char sccsid[] = "@(#)main.c	5.3 (Berkeley) 01/13/86";
1521740Sdist #endif not lint
1621740Sdist 
1710297Ssam /*
1810297Ssam  * FTP User Program -- Command Interface.
1910297Ssam  */
2011352Ssam #include <sys/param.h>
2110297Ssam #include <sys/socket.h>
2210297Ssam #include <sys/ioctl.h>
2310297Ssam 
2412398Ssam #include <arpa/ftp.h>
2512398Ssam 
2610297Ssam #include <signal.h>
2710297Ssam #include <stdio.h>
2810297Ssam #include <errno.h>
2910297Ssam #include <ctype.h>
3010297Ssam #include <netdb.h>
3111352Ssam #include <pwd.h>
3210297Ssam 
3310297Ssam #include "ftp_var.h"
3410297Ssam 
3510297Ssam int	intr();
3610297Ssam int	lostpeer();
3711352Ssam extern	char *home;
38*25907Smckusick char	*getlogin();
3910297Ssam 
4010297Ssam main(argc, argv)
4110297Ssam 	char *argv[];
4210297Ssam {
4310297Ssam 	register char *cp;
4410297Ssam 	int top;
45*25907Smckusick 	struct passwd *pw = NULL;
4611352Ssam 	char homedir[MAXPATHLEN];
4710297Ssam 
4810297Ssam 	sp = getservbyname("ftp", "tcp");
4910297Ssam 	if (sp == 0) {
5010297Ssam 		fprintf(stderr, "ftp: ftp/tcp: unknown service\n");
5110297Ssam 		exit(1);
5210297Ssam 	}
5311351Ssam 	doglob = 1;
5411654Ssam 	interactive = 1;
5511351Ssam 	autologin = 1;
5610297Ssam 	argc--, argv++;
5710297Ssam 	while (argc > 0 && **argv == '-') {
5810297Ssam 		for (cp = *argv + 1; *cp; cp++)
5910297Ssam 			switch (*cp) {
6010297Ssam 
6110297Ssam 			case 'd':
6210297Ssam 				options |= SO_DEBUG;
6310297Ssam 				debug++;
6410297Ssam 				break;
6510297Ssam 
6610297Ssam 			case 'v':
6710297Ssam 				verbose++;
6810297Ssam 				break;
6910297Ssam 
7010297Ssam 			case 't':
7110297Ssam 				trace++;
7210297Ssam 				break;
7310297Ssam 
7410297Ssam 			case 'i':
7511654Ssam 				interactive = 0;
7610297Ssam 				break;
7710297Ssam 
7810297Ssam 			case 'n':
7910297Ssam 				autologin = 0;
8010297Ssam 				break;
8110297Ssam 
8211351Ssam 			case 'g':
8311351Ssam 				doglob = 0;
8411351Ssam 				break;
8511351Ssam 
8610297Ssam 			default:
8710297Ssam 				fprintf(stderr,
8810297Ssam 				  "ftp: %c: unknown option\n", *cp);
8910297Ssam 				exit(1);
9010297Ssam 			}
9110297Ssam 		argc--, argv++;
9210297Ssam 	}
9310297Ssam 	fromatty = isatty(fileno(stdin));
9410297Ssam 	/*
9510297Ssam 	 * Set up defaults for FTP.
9610297Ssam 	 */
9710297Ssam 	strcpy(typename, "ascii"), type = TYPE_A;
9810297Ssam 	strcpy(formname, "non-print"), form = FORM_N;
9910297Ssam 	strcpy(modename, "stream"), mode = MODE_S;
10010297Ssam 	strcpy(structname, "file"), stru = STRU_F;
10111227Ssam 	strcpy(bytename, "8"), bytesize = 8;
10210297Ssam 	if (fromatty)
10310297Ssam 		verbose++;
10411352Ssam 	/*
10511352Ssam 	 * Set up the home directory in case we're globbing.
10611352Ssam 	 */
107*25907Smckusick 	cp = getlogin();
108*25907Smckusick 	if (cp != NULL)
109*25907Smckusick 		pw = getpwnam(cp);
11011352Ssam 	if (pw == NULL)
11111352Ssam 		pw = getpwuid(getuid());
11211352Ssam 	if (pw != NULL) {
11311352Ssam 		home = homedir;
11411352Ssam 		strcpy(home, pw->pw_dir);
11511352Ssam 	}
11610297Ssam 	if (argc > 0) {
11710297Ssam 		if (setjmp(toplevel))
11810297Ssam 			exit(0);
11912993Ssam 		signal(SIGINT, intr);
12012993Ssam 		signal(SIGPIPE, lostpeer);
12110297Ssam 		setpeer(argc + 1, argv - 1);
12210297Ssam 	}
12310297Ssam 	top = setjmp(toplevel) == 0;
12410297Ssam 	if (top) {
12512993Ssam 		signal(SIGINT, intr);
12612993Ssam 		signal(SIGPIPE, lostpeer);
12710297Ssam 	}
12810297Ssam 	for (;;) {
12910297Ssam 		cmdscanner(top);
13010297Ssam 		top = 1;
13110297Ssam 	}
13210297Ssam }
13310297Ssam 
13410297Ssam intr()
13510297Ssam {
13610297Ssam 
13710297Ssam 	longjmp(toplevel, 1);
13810297Ssam }
13910297Ssam 
14010297Ssam lostpeer()
14110297Ssam {
14210297Ssam 	extern FILE *cout;
14310297Ssam 	extern int data;
14410297Ssam 
14510297Ssam 	if (connected) {
14610297Ssam 		if (cout != NULL) {
14711239Ssam 			shutdown(fileno(cout), 1+1);
14810297Ssam 			fclose(cout);
14910297Ssam 			cout = NULL;
15010297Ssam 		}
15110297Ssam 		if (data >= 0) {
15211239Ssam 			shutdown(data, 1+1);
15310297Ssam 			(void) close(data);
15410297Ssam 			data = -1;
15510297Ssam 		}
15610297Ssam 		connected = 0;
15710297Ssam 	}
15810297Ssam 	longjmp(toplevel, 1);
15910297Ssam }
16010297Ssam 
16110297Ssam char *
16210297Ssam tail(filename)
16310297Ssam 	char *filename;
16410297Ssam {
16510297Ssam 	register char *s;
16610297Ssam 
16710297Ssam 	while (*filename) {
16810297Ssam 		s = rindex(filename, '/');
16910297Ssam 		if (s == NULL)
17010297Ssam 			break;
17110297Ssam 		if (s[1])
17210297Ssam 			return (s + 1);
17310297Ssam 		*s = '\0';
17410297Ssam 	}
17510297Ssam 	return (filename);
17610297Ssam }
17710297Ssam 
17810297Ssam /*
17910297Ssam  * Command parser.
18010297Ssam  */
18110297Ssam cmdscanner(top)
18210297Ssam 	int top;
18310297Ssam {
18410297Ssam 	register struct cmd *c;
18510297Ssam 	struct cmd *getcmd();
18610297Ssam 	extern struct cmd cmdtab[];
18710297Ssam 	extern int help();
18810297Ssam 
18910297Ssam 	if (!top)
19010297Ssam 		putchar('\n');
19110297Ssam 	for (;;) {
19210297Ssam 		if (fromatty) {
19310297Ssam 			printf("ftp> ");
19410297Ssam 			fflush(stdout);
19510297Ssam 		}
19613968Ssam 		if (gets(line) == 0) {
19725802Slepreau 			if (feof(stdin))
19825802Slepreau 				quit();
19910297Ssam 			break;
20013968Ssam 		}
20110297Ssam 		if (line[0] == 0)
20210297Ssam 			break;
20310297Ssam 		makeargv();
204*25907Smckusick 		if (margc == 0)
205*25907Smckusick 			continue;
20610297Ssam 		c = getcmd(margv[0]);
20710297Ssam 		if (c == (struct cmd *)-1) {
20810297Ssam 			printf("?Ambiguous command\n");
20910297Ssam 			continue;
21010297Ssam 		}
21110297Ssam 		if (c == 0) {
21210297Ssam 			printf("?Invalid command\n");
21310297Ssam 			continue;
21410297Ssam 		}
21511654Ssam 		if (c->c_conn && !connected) {
21611654Ssam 			printf ("Not connected.\n");
21711654Ssam 			continue;
21811654Ssam 		}
21910297Ssam 		(*c->c_handler)(margc, margv);
22010297Ssam 		if (bell && c->c_bell)
22110297Ssam 			putchar(CTRL(g));
22210297Ssam 		if (c->c_handler != help)
22310297Ssam 			break;
22410297Ssam 	}
22510297Ssam }
22610297Ssam 
22710297Ssam struct cmd *
22810297Ssam getcmd(name)
22910297Ssam 	register char *name;
23010297Ssam {
23110297Ssam 	register char *p, *q;
23210297Ssam 	register struct cmd *c, *found;
23310297Ssam 	register int nmatches, longest;
23410297Ssam 
23510297Ssam 	longest = 0;
23610297Ssam 	nmatches = 0;
23710297Ssam 	found = 0;
23810297Ssam 	for (c = cmdtab; p = c->c_name; c++) {
23910297Ssam 		for (q = name; *q == *p++; q++)
24010297Ssam 			if (*q == 0)		/* exact match? */
24110297Ssam 				return (c);
24210297Ssam 		if (!*q) {			/* the name was a prefix */
24310297Ssam 			if (q - name > longest) {
24410297Ssam 				longest = q - name;
24510297Ssam 				nmatches = 1;
24610297Ssam 				found = c;
24710297Ssam 			} else if (q - name == longest)
24810297Ssam 				nmatches++;
24910297Ssam 		}
25010297Ssam 	}
25110297Ssam 	if (nmatches > 1)
25210297Ssam 		return ((struct cmd *)-1);
25310297Ssam 	return (found);
25410297Ssam }
25510297Ssam 
25610297Ssam /*
25710297Ssam  * Slice a string up into argc/argv.
25810297Ssam  */
25910297Ssam makeargv()
26010297Ssam {
26110297Ssam 	char **argp;
26210297Ssam 	char *slurpstring();
26310297Ssam 
26410297Ssam 	margc = 0;
26510297Ssam 	argp = margv;
26610297Ssam 	stringbase = line;		/* scan from first of buffer */
26710297Ssam 	argbase = argbuf;		/* store from first of buffer */
268*25907Smckusick 	while (*stringbase == ' ' || *stringbase == '\t')
269*25907Smckusick 		stringbase++;		/* skip initial white space */
270*25907Smckusick 	if (*stringbase == '!') {	/* handle shell escapes specially */
271*25907Smckusick 		stringbase++;
272*25907Smckusick 		*argp++ = "!";		/* command name is "!" */
27310297Ssam 		margc++;
274*25907Smckusick 		while (*stringbase == ' ' || *stringbase == '\t')
275*25907Smckusick 			stringbase++;		/* skip white space */
276*25907Smckusick 		if (*stringbase != '\0') {
277*25907Smckusick 			*argp++ = stringbase;	/* argument is entire command string */
278*25907Smckusick 			margc++;
279*25907Smckusick 		}
280*25907Smckusick 		*argp++ = NULL;
281*25907Smckusick 	} else {
282*25907Smckusick 		while (*argp++ = slurpstring())
283*25907Smckusick 			margc++;
284*25907Smckusick 	}
28510297Ssam }
28610297Ssam 
28710297Ssam /*
28810297Ssam  * Parse string into argbuf;
28910297Ssam  * implemented with FSM to
29010297Ssam  * handle quoting and strings
29110297Ssam  */
29210297Ssam char *
29310297Ssam slurpstring()
29410297Ssam {
29510297Ssam 	int got_one = 0;
29610297Ssam 	register char *sb = stringbase;
29710297Ssam 	register char *ap = argbase;
29810297Ssam 	char *tmp = argbase;		/* will return this if token found */
29910297Ssam 
30010297Ssam S0:
30110297Ssam 	switch (*sb) {
30210297Ssam 
30310297Ssam 	case '\0':
30410297Ssam 		goto OUT;
30510297Ssam 
30610297Ssam 	case ' ':
30710297Ssam 	case '\t':
30810297Ssam 		sb++; goto S0;
30910297Ssam 
31010297Ssam 	default:
31110297Ssam 		goto S1;
31210297Ssam 	}
31310297Ssam 
31410297Ssam S1:
31510297Ssam 	switch (*sb) {
31610297Ssam 
31710297Ssam 	case ' ':
31810297Ssam 	case '\t':
31910297Ssam 	case '\0':
32010297Ssam 		goto OUT;	/* end of token */
32110297Ssam 
32210297Ssam 	case '\\':
32310297Ssam 		sb++; goto S2;	/* slurp next character */
32410297Ssam 
32510297Ssam 	case '"':
32610297Ssam 		sb++; goto S3;	/* slurp quoted string */
32710297Ssam 
32810297Ssam 	default:
32910297Ssam 		*ap++ = *sb++;	/* add character to token */
33010297Ssam 		got_one = 1;
33110297Ssam 		goto S1;
33210297Ssam 	}
33310297Ssam 
33410297Ssam S2:
33510297Ssam 	switch (*sb) {
33610297Ssam 
33710297Ssam 	case '\0':
33810297Ssam 		goto OUT;
33910297Ssam 
34010297Ssam 	default:
34110297Ssam 		*ap++ = *sb++;
34210297Ssam 		got_one = 1;
34310297Ssam 		goto S1;
34410297Ssam 	}
34510297Ssam 
34610297Ssam S3:
34710297Ssam 	switch (*sb) {
34810297Ssam 
34910297Ssam 	case '\0':
35010297Ssam 		goto OUT;
35110297Ssam 
35210297Ssam 	case '"':
35310297Ssam 		sb++; goto S1;
35410297Ssam 
35510297Ssam 	default:
35610297Ssam 		*ap++ = *sb++;
35710297Ssam 		got_one = 1;
35810297Ssam 		goto S3;
35910297Ssam 	}
36010297Ssam 
36110297Ssam OUT:
36210297Ssam 	if (got_one)
36310297Ssam 		*ap++ = '\0';
36410297Ssam 	argbase = ap;			/* update storage pointer */
36510297Ssam 	stringbase = sb;		/* update scan pointer */
36610297Ssam 	if (got_one)
36710297Ssam 		return(tmp);
36810297Ssam 	return((char *)0);
36910297Ssam }
37010297Ssam 
37110297Ssam #define HELPINDENT (sizeof ("directory"))
37210297Ssam 
37310297Ssam /*
37410297Ssam  * Help command.
37510297Ssam  * Call each command handler with argc == 0 and argv[0] == name.
37610297Ssam  */
37710297Ssam help(argc, argv)
37810297Ssam 	int argc;
37910297Ssam 	char *argv[];
38010297Ssam {
38110297Ssam 	register struct cmd *c;
38210297Ssam 
38310297Ssam 	if (argc == 1) {
38410297Ssam 		register int i, j, w;
38510297Ssam 		int columns, width = 0, lines;
38610297Ssam 		extern int NCMDS;
38710297Ssam 
38810297Ssam 		printf("Commands may be abbreviated.  Commands are:\n\n");
38910297Ssam 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
39010297Ssam 			int len = strlen(c->c_name);
39110297Ssam 
39210297Ssam 			if (len > width)
39310297Ssam 				width = len;
39410297Ssam 		}
39510297Ssam 		width = (width + 8) &~ 7;
39610297Ssam 		columns = 80 / width;
39710297Ssam 		if (columns == 0)
39810297Ssam 			columns = 1;
39910297Ssam 		lines = (NCMDS + columns - 1) / columns;
40010297Ssam 		for (i = 0; i < lines; i++) {
40110297Ssam 			for (j = 0; j < columns; j++) {
40210297Ssam 				c = cmdtab + j * lines + i;
403*25907Smckusick 				if (c->c_name)
404*25907Smckusick 					printf("%s", c->c_name);
40510297Ssam 				if (c + lines >= &cmdtab[NCMDS]) {
40610297Ssam 					printf("\n");
40710297Ssam 					break;
40810297Ssam 				}
40910297Ssam 				w = strlen(c->c_name);
41010297Ssam 				while (w < width) {
41110297Ssam 					w = (w + 8) &~ 7;
41210297Ssam 					putchar('\t');
41310297Ssam 				}
41410297Ssam 			}
41510297Ssam 		}
41610297Ssam 		return;
41710297Ssam 	}
41810297Ssam 	while (--argc > 0) {
41910297Ssam 		register char *arg;
42010297Ssam 		arg = *++argv;
42110297Ssam 		c = getcmd(arg);
42210297Ssam 		if (c == (struct cmd *)-1)
42310297Ssam 			printf("?Ambiguous help command %s\n", arg);
42410297Ssam 		else if (c == (struct cmd *)0)
42510297Ssam 			printf("?Invalid help command %s\n", arg);
42610297Ssam 		else
42710297Ssam 			printf("%-*s\t%s\n", HELPINDENT,
42810297Ssam 				c->c_name, c->c_help);
42910297Ssam 	}
43010297Ssam }
43110297Ssam 
43210297Ssam /*
43310297Ssam  * Call routine with argc, argv set from args (terminated by 0).
43410297Ssam  */
43510297Ssam /* VARARGS2 */
43610297Ssam call(routine, args)
43710297Ssam 	int (*routine)();
43810297Ssam 	int args;
43910297Ssam {
44010297Ssam 	register int *argp;
44110297Ssam 	register int argc;
44210297Ssam 
44310297Ssam 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
44410297Ssam 		;
44510297Ssam 	(*routine)(argc, &args);
44610297Ssam }
447