xref: /csrg-svn/usr.bin/ftp/main.c (revision 42666)
121740Sdist /*
237173Skarels  * Copyright (c) 1985, 1989 Regents of the University of California.
333737Sbostic  * All rights reserved.
433737Sbostic  *
5*42666Sbostic  * %sccs.include.redist.c%
621740Sdist  */
721740Sdist 
810297Ssam #ifndef lint
921740Sdist char copyright[] =
1037173Skarels "@(#) Copyright (c) 1985, 1989 Regents of the University of California.\n\
1121740Sdist  All rights reserved.\n";
1233737Sbostic #endif /* not lint */
1310297Ssam 
1421740Sdist #ifndef lint
15*42666Sbostic static char sccsid[] = "@(#)main.c	5.16 (Berkeley) 06/01/90";
1633737Sbostic #endif /* not lint */
1721740Sdist 
1810297Ssam /*
1910297Ssam  * FTP User Program -- Command Interface.
2010297Ssam  */
2126047Sminshall #include "ftp_var.h"
2210297Ssam #include <sys/socket.h>
2310297Ssam #include <sys/ioctl.h>
2426495Sminshall #include <sys/types.h>
2510297Ssam 
2612398Ssam #include <arpa/ftp.h>
2712398Ssam 
2810297Ssam #include <signal.h>
2910297Ssam #include <stdio.h>
3010297Ssam #include <errno.h>
3110297Ssam #include <ctype.h>
3210297Ssam #include <netdb.h>
3311352Ssam #include <pwd.h>
3410297Ssam 
3526495Sminshall uid_t	getuid();
3638131Srick sig_t	intr();
3738131Srick sig_t	lostpeer();
3811352Ssam extern	char *home;
3925907Smckusick char	*getlogin();
4010297Ssam 
4110297Ssam main(argc, argv)
4210297Ssam 	char *argv[];
4310297Ssam {
4410297Ssam 	register char *cp;
4510297Ssam 	int top;
4625907Smckusick 	struct passwd *pw = NULL;
4711352Ssam 	char homedir[MAXPATHLEN];
4810297Ssam 
4910297Ssam 	sp = getservbyname("ftp", "tcp");
5010297Ssam 	if (sp == 0) {
5110297Ssam 		fprintf(stderr, "ftp: ftp/tcp: unknown service\n");
5210297Ssam 		exit(1);
5310297Ssam 	}
5411351Ssam 	doglob = 1;
5511654Ssam 	interactive = 1;
5611351Ssam 	autologin = 1;
5710297Ssam 	argc--, argv++;
5810297Ssam 	while (argc > 0 && **argv == '-') {
5910297Ssam 		for (cp = *argv + 1; *cp; cp++)
6010297Ssam 			switch (*cp) {
6110297Ssam 
6210297Ssam 			case 'd':
6310297Ssam 				options |= SO_DEBUG;
6410297Ssam 				debug++;
6510297Ssam 				break;
6610297Ssam 
6710297Ssam 			case 'v':
6810297Ssam 				verbose++;
6910297Ssam 				break;
7010297Ssam 
7110297Ssam 			case 't':
7210297Ssam 				trace++;
7310297Ssam 				break;
7410297Ssam 
7510297Ssam 			case 'i':
7611654Ssam 				interactive = 0;
7710297Ssam 				break;
7810297Ssam 
7910297Ssam 			case 'n':
8010297Ssam 				autologin = 0;
8110297Ssam 				break;
8210297Ssam 
8311351Ssam 			case 'g':
8411351Ssam 				doglob = 0;
8511351Ssam 				break;
8611351Ssam 
8710297Ssam 			default:
8826495Sminshall 				fprintf(stdout,
8910297Ssam 				  "ftp: %c: unknown option\n", *cp);
9010297Ssam 				exit(1);
9110297Ssam 			}
9210297Ssam 		argc--, argv++;
9310297Ssam 	}
9410297Ssam 	fromatty = isatty(fileno(stdin));
9510297Ssam 	if (fromatty)
9610297Ssam 		verbose++;
9738131Srick 	cpend = 0;	/* no pending replies */
9826047Sminshall 	proxy = 0;	/* proxy not active */
9938131Srick 	crflag = 1;	/* strip c.r. on ascii gets */
10038131Srick 	sendport = -1;	/* not using ports */
10111352Ssam 	/*
10211352Ssam 	 * Set up the home directory in case we're globbing.
10311352Ssam 	 */
10425907Smckusick 	cp = getlogin();
10526047Sminshall 	if (cp != NULL) {
10625907Smckusick 		pw = getpwnam(cp);
10726047Sminshall 	}
10811352Ssam 	if (pw == NULL)
10911352Ssam 		pw = getpwuid(getuid());
11011352Ssam 	if (pw != NULL) {
11111352Ssam 		home = homedir;
11226495Sminshall 		(void) strcpy(home, pw->pw_dir);
11311352Ssam 	}
11410297Ssam 	if (argc > 0) {
11510297Ssam 		if (setjmp(toplevel))
11610297Ssam 			exit(0);
11726495Sminshall 		(void) signal(SIGINT, intr);
11826495Sminshall 		(void) signal(SIGPIPE, lostpeer);
11910297Ssam 		setpeer(argc + 1, argv - 1);
12010297Ssam 	}
12110297Ssam 	top = setjmp(toplevel) == 0;
12210297Ssam 	if (top) {
12326495Sminshall 		(void) signal(SIGINT, intr);
12426495Sminshall 		(void) signal(SIGPIPE, lostpeer);
12510297Ssam 	}
12610297Ssam 	for (;;) {
12710297Ssam 		cmdscanner(top);
12810297Ssam 		top = 1;
12910297Ssam 	}
13010297Ssam }
13110297Ssam 
13238131Srick sig_t
13310297Ssam intr()
13410297Ssam {
13510297Ssam 
13610297Ssam 	longjmp(toplevel, 1);
13710297Ssam }
13810297Ssam 
13938131Srick sig_t
14010297Ssam lostpeer()
14110297Ssam {
14210297Ssam 	extern FILE *cout;
14310297Ssam 	extern int data;
14410297Ssam 
14510297Ssam 	if (connected) {
14610297Ssam 		if (cout != NULL) {
14726495Sminshall 			(void) shutdown(fileno(cout), 1+1);
14826495Sminshall 			(void) fclose(cout);
14910297Ssam 			cout = NULL;
15010297Ssam 		}
15110297Ssam 		if (data >= 0) {
15226495Sminshall 			(void) shutdown(data, 1+1);
15310297Ssam 			(void) close(data);
15410297Ssam 			data = -1;
15510297Ssam 		}
15610297Ssam 		connected = 0;
15710297Ssam 	}
15826047Sminshall 	pswitch(1);
15926047Sminshall 	if (connected) {
16026047Sminshall 		if (cout != NULL) {
16126495Sminshall 			(void) shutdown(fileno(cout), 1+1);
16226495Sminshall 			(void) fclose(cout);
16326047Sminshall 			cout = NULL;
16426047Sminshall 		}
16526047Sminshall 		connected = 0;
16626047Sminshall 	}
16726047Sminshall 	proxflag = 0;
16826047Sminshall 	pswitch(0);
16910297Ssam }
17010297Ssam 
17126495Sminshall /*char *
17210297Ssam tail(filename)
17310297Ssam 	char *filename;
17410297Ssam {
17510297Ssam 	register char *s;
17610297Ssam 
17710297Ssam 	while (*filename) {
17810297Ssam 		s = rindex(filename, '/');
17910297Ssam 		if (s == NULL)
18010297Ssam 			break;
18110297Ssam 		if (s[1])
18210297Ssam 			return (s + 1);
18310297Ssam 		*s = '\0';
18410297Ssam 	}
18510297Ssam 	return (filename);
18610297Ssam }
18726495Sminshall */
18810297Ssam /*
18910297Ssam  * Command parser.
19010297Ssam  */
19110297Ssam cmdscanner(top)
19210297Ssam 	int top;
19310297Ssam {
19410297Ssam 	register struct cmd *c;
19510297Ssam 	struct cmd *getcmd();
19610297Ssam 	extern int help();
19710297Ssam 
19810297Ssam 	if (!top)
19926495Sminshall 		(void) putchar('\n');
20010297Ssam 	for (;;) {
20110297Ssam 		if (fromatty) {
20210297Ssam 			printf("ftp> ");
20326495Sminshall 			(void) fflush(stdout);
20410297Ssam 		}
20513968Ssam 		if (gets(line) == 0) {
20633374Stef 			if (feof(stdin) || ferror(stdin))
20726082Slepreau 				quit();
20810297Ssam 			break;
20913968Ssam 		}
21010297Ssam 		if (line[0] == 0)
21110297Ssam 			break;
21210297Ssam 		makeargv();
21326047Sminshall 		if (margc == 0) {
21425907Smckusick 			continue;
21526047Sminshall 		}
21610297Ssam 		c = getcmd(margv[0]);
21710297Ssam 		if (c == (struct cmd *)-1) {
21810297Ssam 			printf("?Ambiguous command\n");
21910297Ssam 			continue;
22010297Ssam 		}
22110297Ssam 		if (c == 0) {
22210297Ssam 			printf("?Invalid command\n");
22310297Ssam 			continue;
22410297Ssam 		}
22511654Ssam 		if (c->c_conn && !connected) {
22611654Ssam 			printf ("Not connected.\n");
22711654Ssam 			continue;
22811654Ssam 		}
22910297Ssam 		(*c->c_handler)(margc, margv);
23010297Ssam 		if (bell && c->c_bell)
23136935Skarels 			(void) putchar('\007');
23210297Ssam 		if (c->c_handler != help)
23310297Ssam 			break;
23410297Ssam 	}
23526495Sminshall 	(void) signal(SIGINT, intr);
23626495Sminshall 	(void) signal(SIGPIPE, lostpeer);
23710297Ssam }
23810297Ssam 
23910297Ssam struct cmd *
24010297Ssam getcmd(name)
24110297Ssam 	register char *name;
24210297Ssam {
24333224Sbostic 	extern struct cmd cmdtab[];
24410297Ssam 	register char *p, *q;
24510297Ssam 	register struct cmd *c, *found;
24610297Ssam 	register int nmatches, longest;
24710297Ssam 
24810297Ssam 	longest = 0;
24910297Ssam 	nmatches = 0;
25010297Ssam 	found = 0;
25110297Ssam 	for (c = cmdtab; p = c->c_name; c++) {
25210297Ssam 		for (q = name; *q == *p++; q++)
25310297Ssam 			if (*q == 0)		/* exact match? */
25410297Ssam 				return (c);
25510297Ssam 		if (!*q) {			/* the name was a prefix */
25610297Ssam 			if (q - name > longest) {
25710297Ssam 				longest = q - name;
25810297Ssam 				nmatches = 1;
25910297Ssam 				found = c;
26010297Ssam 			} else if (q - name == longest)
26110297Ssam 				nmatches++;
26210297Ssam 		}
26310297Ssam 	}
26410297Ssam 	if (nmatches > 1)
26510297Ssam 		return ((struct cmd *)-1);
26610297Ssam 	return (found);
26710297Ssam }
26810297Ssam 
26910297Ssam /*
27010297Ssam  * Slice a string up into argc/argv.
27110297Ssam  */
27226047Sminshall 
27326047Sminshall int slrflag;
27426047Sminshall 
27510297Ssam makeargv()
27610297Ssam {
27710297Ssam 	char **argp;
27810297Ssam 	char *slurpstring();
27910297Ssam 
28010297Ssam 	margc = 0;
28110297Ssam 	argp = margv;
28210297Ssam 	stringbase = line;		/* scan from first of buffer */
28310297Ssam 	argbase = argbuf;		/* store from first of buffer */
28426047Sminshall 	slrflag = 0;
28526047Sminshall 	while (*argp++ = slurpstring())
28610297Ssam 		margc++;
28710297Ssam }
28810297Ssam 
28910297Ssam /*
29010297Ssam  * Parse string into argbuf;
29110297Ssam  * implemented with FSM to
29210297Ssam  * handle quoting and strings
29310297Ssam  */
29410297Ssam char *
29510297Ssam slurpstring()
29610297Ssam {
29710297Ssam 	int got_one = 0;
29810297Ssam 	register char *sb = stringbase;
29910297Ssam 	register char *ap = argbase;
30010297Ssam 	char *tmp = argbase;		/* will return this if token found */
30110297Ssam 
30226047Sminshall 	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
30326047Sminshall 		switch (slrflag) {	/* and $ as token for macro invoke */
30426047Sminshall 			case 0:
30526047Sminshall 				slrflag++;
30626047Sminshall 				stringbase++;
30726047Sminshall 				return ((*sb == '!') ? "!" : "$");
30836935Skarels 				/* NOTREACHED */
30926047Sminshall 			case 1:
31026047Sminshall 				slrflag++;
31126047Sminshall 				altarg = stringbase;
31226047Sminshall 				break;
31326047Sminshall 			default:
31426047Sminshall 				break;
31526047Sminshall 		}
31626047Sminshall 	}
31726047Sminshall 
31810297Ssam S0:
31910297Ssam 	switch (*sb) {
32010297Ssam 
32110297Ssam 	case '\0':
32210297Ssam 		goto OUT;
32310297Ssam 
32410297Ssam 	case ' ':
32510297Ssam 	case '\t':
32610297Ssam 		sb++; goto S0;
32710297Ssam 
32810297Ssam 	default:
32926047Sminshall 		switch (slrflag) {
33026047Sminshall 			case 0:
33126047Sminshall 				slrflag++;
33226047Sminshall 				break;
33326047Sminshall 			case 1:
33426047Sminshall 				slrflag++;
33526047Sminshall 				altarg = sb;
33626047Sminshall 				break;
33726047Sminshall 			default:
33826047Sminshall 				break;
33926047Sminshall 		}
34010297Ssam 		goto S1;
34110297Ssam 	}
34210297Ssam 
34310297Ssam S1:
34410297Ssam 	switch (*sb) {
34510297Ssam 
34610297Ssam 	case ' ':
34710297Ssam 	case '\t':
34810297Ssam 	case '\0':
34910297Ssam 		goto OUT;	/* end of token */
35010297Ssam 
35110297Ssam 	case '\\':
35210297Ssam 		sb++; goto S2;	/* slurp next character */
35310297Ssam 
35410297Ssam 	case '"':
35510297Ssam 		sb++; goto S3;	/* slurp quoted string */
35610297Ssam 
35710297Ssam 	default:
35810297Ssam 		*ap++ = *sb++;	/* add character to token */
35910297Ssam 		got_one = 1;
36010297Ssam 		goto S1;
36110297Ssam 	}
36210297Ssam 
36310297Ssam S2:
36410297Ssam 	switch (*sb) {
36510297Ssam 
36610297Ssam 	case '\0':
36710297Ssam 		goto OUT;
36810297Ssam 
36910297Ssam 	default:
37010297Ssam 		*ap++ = *sb++;
37110297Ssam 		got_one = 1;
37210297Ssam 		goto S1;
37310297Ssam 	}
37410297Ssam 
37510297Ssam S3:
37610297Ssam 	switch (*sb) {
37710297Ssam 
37810297Ssam 	case '\0':
37910297Ssam 		goto OUT;
38010297Ssam 
38110297Ssam 	case '"':
38210297Ssam 		sb++; goto S1;
38310297Ssam 
38410297Ssam 	default:
38510297Ssam 		*ap++ = *sb++;
38610297Ssam 		got_one = 1;
38710297Ssam 		goto S3;
38810297Ssam 	}
38910297Ssam 
39010297Ssam OUT:
39110297Ssam 	if (got_one)
39210297Ssam 		*ap++ = '\0';
39310297Ssam 	argbase = ap;			/* update storage pointer */
39410297Ssam 	stringbase = sb;		/* update scan pointer */
39526047Sminshall 	if (got_one) {
39610297Ssam 		return(tmp);
39726047Sminshall 	}
39826047Sminshall 	switch (slrflag) {
39926047Sminshall 		case 0:
40026047Sminshall 			slrflag++;
40126047Sminshall 			break;
40226047Sminshall 		case 1:
40326047Sminshall 			slrflag++;
40426047Sminshall 			altarg = (char *) 0;
40526047Sminshall 			break;
40626047Sminshall 		default:
40726047Sminshall 			break;
40826047Sminshall 	}
40910297Ssam 	return((char *)0);
41010297Ssam }
41110297Ssam 
41210297Ssam #define HELPINDENT (sizeof ("directory"))
41310297Ssam 
41410297Ssam /*
41510297Ssam  * Help command.
41610297Ssam  * Call each command handler with argc == 0 and argv[0] == name.
41710297Ssam  */
41810297Ssam help(argc, argv)
41910297Ssam 	int argc;
42010297Ssam 	char *argv[];
42110297Ssam {
42233224Sbostic 	extern struct cmd cmdtab[];
42310297Ssam 	register struct cmd *c;
42410297Ssam 
42510297Ssam 	if (argc == 1) {
42626047Sminshall 		register int i, j, w, k;
42710297Ssam 		int columns, width = 0, lines;
42810297Ssam 		extern int NCMDS;
42910297Ssam 
43010297Ssam 		printf("Commands may be abbreviated.  Commands are:\n\n");
43110297Ssam 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
43210297Ssam 			int len = strlen(c->c_name);
43310297Ssam 
43410297Ssam 			if (len > width)
43510297Ssam 				width = len;
43610297Ssam 		}
43710297Ssam 		width = (width + 8) &~ 7;
43810297Ssam 		columns = 80 / width;
43910297Ssam 		if (columns == 0)
44010297Ssam 			columns = 1;
44110297Ssam 		lines = (NCMDS + columns - 1) / columns;
44210297Ssam 		for (i = 0; i < lines; i++) {
44310297Ssam 			for (j = 0; j < columns; j++) {
44410297Ssam 				c = cmdtab + j * lines + i;
44526047Sminshall 				if (c->c_name && (!proxy || c->c_proxy)) {
44625907Smckusick 					printf("%s", c->c_name);
44726047Sminshall 				}
44826047Sminshall 				else if (c->c_name) {
44926047Sminshall 					for (k=0; k < strlen(c->c_name); k++) {
45026495Sminshall 						(void) putchar(' ');
45126047Sminshall 					}
45226047Sminshall 				}
45310297Ssam 				if (c + lines >= &cmdtab[NCMDS]) {
45410297Ssam 					printf("\n");
45510297Ssam 					break;
45610297Ssam 				}
45710297Ssam 				w = strlen(c->c_name);
45810297Ssam 				while (w < width) {
45910297Ssam 					w = (w + 8) &~ 7;
46026495Sminshall 					(void) putchar('\t');
46110297Ssam 				}
46210297Ssam 			}
46310297Ssam 		}
46410297Ssam 		return;
46510297Ssam 	}
46610297Ssam 	while (--argc > 0) {
46710297Ssam 		register char *arg;
46810297Ssam 		arg = *++argv;
46910297Ssam 		c = getcmd(arg);
47010297Ssam 		if (c == (struct cmd *)-1)
47110297Ssam 			printf("?Ambiguous help command %s\n", arg);
47210297Ssam 		else if (c == (struct cmd *)0)
47310297Ssam 			printf("?Invalid help command %s\n", arg);
47410297Ssam 		else
47510297Ssam 			printf("%-*s\t%s\n", HELPINDENT,
47610297Ssam 				c->c_name, c->c_help);
47710297Ssam 	}
47810297Ssam }
479