xref: /csrg-svn/usr.bin/ftp/main.c (revision 67794)
121740Sdist /*
266673Spendry  * Copyright (c) 1985, 1989, 1993, 1994
362012Sbostic  *	The Regents of the University of California.  All rights reserved.
433737Sbostic  *
542666Sbostic  * %sccs.include.redist.c%
621740Sdist  */
721740Sdist 
810297Ssam #ifndef lint
962012Sbostic static char copyright[] =
1066673Spendry "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
1162012Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1233737Sbostic #endif /* not lint */
1310297Ssam 
1421740Sdist #ifndef lint
15*67794Smckusick static char sccsid[] = "@(#)main.c	8.6 (Berkeley) 10/09/94";
1633737Sbostic #endif /* not lint */
1721740Sdist 
1810297Ssam /*
1910297Ssam  * FTP User Program -- Command Interface.
2010297Ssam  */
2166670Spendry /*#include <sys/ioctl.h>*/
2266670Spendry #include <sys/types.h>
2310297Ssam #include <sys/socket.h>
2410297Ssam 
2512398Ssam #include <arpa/ftp.h>
2612398Ssam 
2710297Ssam #include <ctype.h>
2866670Spendry #include <err.h>
2910297Ssam #include <netdb.h>
3011352Ssam #include <pwd.h>
3166670Spendry #include <signal.h>
3266670Spendry #include <stdio.h>
3366670Spendry #include <stdlib.h>
3466670Spendry #include <unistd.h>
3510297Ssam 
3666670Spendry #include "ftp_var.h"
3710297Ssam 
3866670Spendry int
main(argc,argv)3910297Ssam main(argc, argv)
4066670Spendry 	int argc;
4110297Ssam 	char *argv[];
4210297Ssam {
4366670Spendry 	int ch, top;
4425907Smckusick 	struct passwd *pw = NULL;
4566670Spendry 	char *cp, homedir[MAXPATHLEN];
4610297Ssam 
4710297Ssam 	sp = getservbyname("ftp", "tcp");
4866670Spendry 	if (sp == 0)
4966670Spendry 		errx(1, "ftp/tcp: unknown service");
5011351Ssam 	doglob = 1;
5111654Ssam 	interactive = 1;
5211351Ssam 	autologin = 1;
5310297Ssam 
5466670Spendry 	while ((ch = getopt(argc, argv, "dgintv")) != EOF) {
5567754Sbostic 		switch (ch) {
5666670Spendry 		case 'd':
5766670Spendry 			options |= SO_DEBUG;
5866670Spendry 			debug++;
5966670Spendry 			break;
6010297Ssam 
6166670Spendry 		case 'g':
6266670Spendry 			doglob = 0;
6366670Spendry 			break;
6410297Ssam 
6566670Spendry 		case 'i':
6666670Spendry 			interactive = 0;
6766670Spendry 			break;
6810297Ssam 
6966670Spendry 		case 'n':
7066670Spendry 			autologin = 0;
7166670Spendry 			break;
7210297Ssam 
7366670Spendry 		case 't':
7466670Spendry 			trace++;
7566670Spendry 			break;
7610297Ssam 
7766670Spendry 		case 'v':
7866670Spendry 			verbose++;
7966670Spendry 			break;
8011351Ssam 
8166670Spendry 		default:
8266670Spendry 			(void)fprintf(stderr,
8366670Spendry 				"usage: ftp [-dgintv] [host [port]]\n");
8466670Spendry 			exit(1);
8566670Spendry 		}
8610297Ssam 	}
8766670Spendry 	argc -= optind;
8866670Spendry 	argv += optind;
8966670Spendry 
9010297Ssam 	fromatty = isatty(fileno(stdin));
9110297Ssam 	if (fromatty)
9210297Ssam 		verbose++;
9338131Srick 	cpend = 0;	/* no pending replies */
9426047Sminshall 	proxy = 0;	/* proxy not active */
95*67794Smckusick 	passivemode = 0; /* passive mode not active */
9638131Srick 	crflag = 1;	/* strip c.r. on ascii gets */
9738131Srick 	sendport = -1;	/* not using ports */
9811352Ssam 	/*
9911352Ssam 	 * Set up the home directory in case we're globbing.
10011352Ssam 	 */
10125907Smckusick 	cp = getlogin();
10226047Sminshall 	if (cp != NULL) {
10325907Smckusick 		pw = getpwnam(cp);
10426047Sminshall 	}
10511352Ssam 	if (pw == NULL)
10611352Ssam 		pw = getpwuid(getuid());
10711352Ssam 	if (pw != NULL) {
10811352Ssam 		home = homedir;
10926495Sminshall 		(void) strcpy(home, pw->pw_dir);
11011352Ssam 	}
11110297Ssam 	if (argc > 0) {
11266691Spendry 		char *xargv[5];
11366670Spendry 		extern char *__progname;
11466670Spendry 
11510297Ssam 		if (setjmp(toplevel))
11610297Ssam 			exit(0);
11726495Sminshall 		(void) signal(SIGINT, intr);
11826495Sminshall 		(void) signal(SIGPIPE, lostpeer);
11966670Spendry 		xargv[0] = __progname;
12066670Spendry 		xargv[1] = argv[0];
12166670Spendry 		xargv[2] = argv[1];
12266670Spendry 		xargv[3] = argv[2];
12366691Spendry 		xargv[4] = NULL;
12466670Spendry 		setpeer(argc+1, xargv);
12510297Ssam 	}
12610297Ssam 	top = setjmp(toplevel) == 0;
12710297Ssam 	if (top) {
12826495Sminshall 		(void) signal(SIGINT, intr);
12926495Sminshall 		(void) signal(SIGPIPE, lostpeer);
13010297Ssam 	}
13110297Ssam 	for (;;) {
13210297Ssam 		cmdscanner(top);
13310297Ssam 		top = 1;
13410297Ssam 	}
13510297Ssam }
13610297Ssam 
13746828Sbostic void
intr()13810297Ssam intr()
13910297Ssam {
14010297Ssam 
14110297Ssam 	longjmp(toplevel, 1);
14210297Ssam }
14310297Ssam 
14446828Sbostic void
lostpeer()14510297Ssam lostpeer()
14610297Ssam {
14710297Ssam 
14810297Ssam 	if (connected) {
14910297Ssam 		if (cout != NULL) {
15026495Sminshall 			(void) shutdown(fileno(cout), 1+1);
15126495Sminshall 			(void) fclose(cout);
15210297Ssam 			cout = NULL;
15310297Ssam 		}
15410297Ssam 		if (data >= 0) {
15526495Sminshall 			(void) shutdown(data, 1+1);
15610297Ssam 			(void) close(data);
15710297Ssam 			data = -1;
15810297Ssam 		}
15910297Ssam 		connected = 0;
16010297Ssam 	}
16126047Sminshall 	pswitch(1);
16226047Sminshall 	if (connected) {
16326047Sminshall 		if (cout != NULL) {
16426495Sminshall 			(void) shutdown(fileno(cout), 1+1);
16526495Sminshall 			(void) fclose(cout);
16626047Sminshall 			cout = NULL;
16726047Sminshall 		}
16826047Sminshall 		connected = 0;
16926047Sminshall 	}
17026047Sminshall 	proxflag = 0;
17126047Sminshall 	pswitch(0);
17210297Ssam }
17310297Ssam 
17466670Spendry /*
17566670Spendry char *
17610297Ssam tail(filename)
17710297Ssam 	char *filename;
17810297Ssam {
17966670Spendry 	char *s;
18010297Ssam 
18110297Ssam 	while (*filename) {
18266670Spendry 		s = strrchr(filename, '/');
18310297Ssam 		if (s == NULL)
18410297Ssam 			break;
18510297Ssam 		if (s[1])
18610297Ssam 			return (s + 1);
18710297Ssam 		*s = '\0';
18810297Ssam 	}
18910297Ssam 	return (filename);
19010297Ssam }
19126495Sminshall */
19266670Spendry 
19310297Ssam /*
19410297Ssam  * Command parser.
19510297Ssam  */
19666670Spendry void
cmdscanner(top)19710297Ssam cmdscanner(top)
19810297Ssam 	int top;
19910297Ssam {
20066670Spendry 	struct cmd *c;
20166670Spendry 	int l;
20210297Ssam 
20310297Ssam 	if (!top)
20426495Sminshall 		(void) putchar('\n');
20510297Ssam 	for (;;) {
20610297Ssam 		if (fromatty) {
20710297Ssam 			printf("ftp> ");
20826495Sminshall 			(void) fflush(stdout);
20910297Ssam 		}
21045260Sbostic 		if (fgets(line, sizeof line, stdin) == NULL)
21166670Spendry 			quit(0, 0);
21245260Sbostic 		l = strlen(line);
21345260Sbostic 		if (l == 0)
21410297Ssam 			break;
21545260Sbostic 		if (line[--l] == '\n') {
21645260Sbostic 			if (l == 0)
21745260Sbostic 				break;
21845260Sbostic 			line[l] = '\0';
21945260Sbostic 		} else if (l == sizeof(line) - 2) {
22045260Sbostic 			printf("sorry, input line too long\n");
22145260Sbostic 			while ((l = getchar()) != '\n' && l != EOF)
22245260Sbostic 				/* void */;
22310297Ssam 			break;
22445260Sbostic 		} /* else it was a line without a newline */
22510297Ssam 		makeargv();
22626047Sminshall 		if (margc == 0) {
22725907Smckusick 			continue;
22826047Sminshall 		}
22910297Ssam 		c = getcmd(margv[0]);
23010297Ssam 		if (c == (struct cmd *)-1) {
23110297Ssam 			printf("?Ambiguous command\n");
23210297Ssam 			continue;
23310297Ssam 		}
23410297Ssam 		if (c == 0) {
23510297Ssam 			printf("?Invalid command\n");
23610297Ssam 			continue;
23710297Ssam 		}
23811654Ssam 		if (c->c_conn && !connected) {
23945260Sbostic 			printf("Not connected.\n");
24011654Ssam 			continue;
24111654Ssam 		}
24210297Ssam 		(*c->c_handler)(margc, margv);
24310297Ssam 		if (bell && c->c_bell)
24436935Skarels 			(void) putchar('\007');
24510297Ssam 		if (c->c_handler != help)
24610297Ssam 			break;
24710297Ssam 	}
24826495Sminshall 	(void) signal(SIGINT, intr);
24926495Sminshall 	(void) signal(SIGPIPE, lostpeer);
25010297Ssam }
25110297Ssam 
25210297Ssam struct cmd *
getcmd(name)25310297Ssam getcmd(name)
25466670Spendry 	char *name;
25510297Ssam {
25666670Spendry 	char *p, *q;
25766670Spendry 	struct cmd *c, *found;
25866670Spendry 	int nmatches, longest;
25910297Ssam 
26010297Ssam 	longest = 0;
26110297Ssam 	nmatches = 0;
26210297Ssam 	found = 0;
26310297Ssam 	for (c = cmdtab; p = c->c_name; c++) {
26410297Ssam 		for (q = name; *q == *p++; q++)
26510297Ssam 			if (*q == 0)		/* exact match? */
26610297Ssam 				return (c);
26710297Ssam 		if (!*q) {			/* the name was a prefix */
26810297Ssam 			if (q - name > longest) {
26910297Ssam 				longest = q - name;
27010297Ssam 				nmatches = 1;
27110297Ssam 				found = c;
27210297Ssam 			} else if (q - name == longest)
27310297Ssam 				nmatches++;
27410297Ssam 		}
27510297Ssam 	}
27610297Ssam 	if (nmatches > 1)
27710297Ssam 		return ((struct cmd *)-1);
27810297Ssam 	return (found);
27910297Ssam }
28010297Ssam 
28110297Ssam /*
28210297Ssam  * Slice a string up into argc/argv.
28310297Ssam  */
28426047Sminshall 
28526047Sminshall int slrflag;
28626047Sminshall 
28766670Spendry void
makeargv()28810297Ssam makeargv()
28910297Ssam {
29010297Ssam 	char **argp;
29110297Ssam 
29210297Ssam 	margc = 0;
29310297Ssam 	argp = margv;
29410297Ssam 	stringbase = line;		/* scan from first of buffer */
29510297Ssam 	argbase = argbuf;		/* store from first of buffer */
29626047Sminshall 	slrflag = 0;
29726047Sminshall 	while (*argp++ = slurpstring())
29810297Ssam 		margc++;
29910297Ssam }
30010297Ssam 
30110297Ssam /*
30210297Ssam  * Parse string into argbuf;
30310297Ssam  * implemented with FSM to
30410297Ssam  * handle quoting and strings
30510297Ssam  */
30610297Ssam char *
slurpstring()30710297Ssam slurpstring()
30810297Ssam {
30910297Ssam 	int got_one = 0;
31066670Spendry 	char *sb = stringbase;
31166670Spendry 	char *ap = argbase;
31210297Ssam 	char *tmp = argbase;		/* will return this if token found */
31310297Ssam 
31426047Sminshall 	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
31526047Sminshall 		switch (slrflag) {	/* and $ as token for macro invoke */
31626047Sminshall 			case 0:
31726047Sminshall 				slrflag++;
31826047Sminshall 				stringbase++;
31926047Sminshall 				return ((*sb == '!') ? "!" : "$");
32036935Skarels 				/* NOTREACHED */
32126047Sminshall 			case 1:
32226047Sminshall 				slrflag++;
32326047Sminshall 				altarg = stringbase;
32426047Sminshall 				break;
32526047Sminshall 			default:
32626047Sminshall 				break;
32726047Sminshall 		}
32826047Sminshall 	}
32926047Sminshall 
33010297Ssam S0:
33110297Ssam 	switch (*sb) {
33210297Ssam 
33310297Ssam 	case '\0':
33410297Ssam 		goto OUT;
33510297Ssam 
33610297Ssam 	case ' ':
33710297Ssam 	case '\t':
33810297Ssam 		sb++; goto S0;
33910297Ssam 
34010297Ssam 	default:
34126047Sminshall 		switch (slrflag) {
34226047Sminshall 			case 0:
34326047Sminshall 				slrflag++;
34426047Sminshall 				break;
34526047Sminshall 			case 1:
34626047Sminshall 				slrflag++;
34726047Sminshall 				altarg = sb;
34826047Sminshall 				break;
34926047Sminshall 			default:
35026047Sminshall 				break;
35126047Sminshall 		}
35210297Ssam 		goto S1;
35310297Ssam 	}
35410297Ssam 
35510297Ssam S1:
35610297Ssam 	switch (*sb) {
35710297Ssam 
35810297Ssam 	case ' ':
35910297Ssam 	case '\t':
36010297Ssam 	case '\0':
36110297Ssam 		goto OUT;	/* end of token */
36210297Ssam 
36310297Ssam 	case '\\':
36410297Ssam 		sb++; goto S2;	/* slurp next character */
36510297Ssam 
36610297Ssam 	case '"':
36710297Ssam 		sb++; goto S3;	/* slurp quoted string */
36810297Ssam 
36910297Ssam 	default:
37010297Ssam 		*ap++ = *sb++;	/* add character to token */
37110297Ssam 		got_one = 1;
37210297Ssam 		goto S1;
37310297Ssam 	}
37410297Ssam 
37510297Ssam S2:
37610297Ssam 	switch (*sb) {
37710297Ssam 
37810297Ssam 	case '\0':
37910297Ssam 		goto OUT;
38010297Ssam 
38110297Ssam 	default:
38210297Ssam 		*ap++ = *sb++;
38310297Ssam 		got_one = 1;
38410297Ssam 		goto S1;
38510297Ssam 	}
38610297Ssam 
38710297Ssam S3:
38810297Ssam 	switch (*sb) {
38910297Ssam 
39010297Ssam 	case '\0':
39110297Ssam 		goto OUT;
39210297Ssam 
39310297Ssam 	case '"':
39410297Ssam 		sb++; goto S1;
39510297Ssam 
39610297Ssam 	default:
39710297Ssam 		*ap++ = *sb++;
39810297Ssam 		got_one = 1;
39910297Ssam 		goto S3;
40010297Ssam 	}
40110297Ssam 
40210297Ssam OUT:
40310297Ssam 	if (got_one)
40410297Ssam 		*ap++ = '\0';
40510297Ssam 	argbase = ap;			/* update storage pointer */
40610297Ssam 	stringbase = sb;		/* update scan pointer */
40726047Sminshall 	if (got_one) {
40866670Spendry 		return (tmp);
40926047Sminshall 	}
41026047Sminshall 	switch (slrflag) {
41126047Sminshall 		case 0:
41226047Sminshall 			slrflag++;
41326047Sminshall 			break;
41426047Sminshall 		case 1:
41526047Sminshall 			slrflag++;
41626047Sminshall 			altarg = (char *) 0;
41726047Sminshall 			break;
41826047Sminshall 		default:
41926047Sminshall 			break;
42026047Sminshall 	}
42166670Spendry 	return ((char *)0);
42210297Ssam }
42310297Ssam 
42466670Spendry #define HELPINDENT ((int) sizeof ("directory"))
42510297Ssam 
42610297Ssam /*
42710297Ssam  * Help command.
42810297Ssam  * Call each command handler with argc == 0 and argv[0] == name.
42910297Ssam  */
43066670Spendry void
help(argc,argv)43110297Ssam help(argc, argv)
43210297Ssam 	int argc;
43310297Ssam 	char *argv[];
43410297Ssam {
43566670Spendry 	struct cmd *c;
43610297Ssam 
43710297Ssam 	if (argc == 1) {
43866670Spendry 		int i, j, w, k;
43910297Ssam 		int columns, width = 0, lines;
44010297Ssam 
44110297Ssam 		printf("Commands may be abbreviated.  Commands are:\n\n");
44210297Ssam 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
44310297Ssam 			int len = strlen(c->c_name);
44410297Ssam 
44510297Ssam 			if (len > width)
44610297Ssam 				width = len;
44710297Ssam 		}
44810297Ssam 		width = (width + 8) &~ 7;
44910297Ssam 		columns = 80 / width;
45010297Ssam 		if (columns == 0)
45110297Ssam 			columns = 1;
45210297Ssam 		lines = (NCMDS + columns - 1) / columns;
45310297Ssam 		for (i = 0; i < lines; i++) {
45410297Ssam 			for (j = 0; j < columns; j++) {
45510297Ssam 				c = cmdtab + j * lines + i;
45626047Sminshall 				if (c->c_name && (!proxy || c->c_proxy)) {
45725907Smckusick 					printf("%s", c->c_name);
45826047Sminshall 				}
45926047Sminshall 				else if (c->c_name) {
46026047Sminshall 					for (k=0; k < strlen(c->c_name); k++) {
46126495Sminshall 						(void) putchar(' ');
46226047Sminshall 					}
46326047Sminshall 				}
46410297Ssam 				if (c + lines >= &cmdtab[NCMDS]) {
46510297Ssam 					printf("\n");
46610297Ssam 					break;
46710297Ssam 				}
46810297Ssam 				w = strlen(c->c_name);
46910297Ssam 				while (w < width) {
47010297Ssam 					w = (w + 8) &~ 7;
47126495Sminshall 					(void) putchar('\t');
47210297Ssam 				}
47310297Ssam 			}
47410297Ssam 		}
47510297Ssam 		return;
47610297Ssam 	}
47710297Ssam 	while (--argc > 0) {
47866670Spendry 		char *arg;
47910297Ssam 		arg = *++argv;
48010297Ssam 		c = getcmd(arg);
48110297Ssam 		if (c == (struct cmd *)-1)
48210297Ssam 			printf("?Ambiguous help command %s\n", arg);
48310297Ssam 		else if (c == (struct cmd *)0)
48410297Ssam 			printf("?Invalid help command %s\n", arg);
48510297Ssam 		else
48610297Ssam 			printf("%-*s\t%s\n", HELPINDENT,
48710297Ssam 				c->c_name, c->c_help);
48810297Ssam 	}
48910297Ssam }
490