xref: /csrg-svn/usr.bin/ftp/main.c (revision 33737)
121740Sdist /*
226047Sminshall  * Copyright (c) 1985 Regents of the University of California.
3*33737Sbostic  * All rights reserved.
4*33737Sbostic  *
5*33737Sbostic  * Redistribution and use in source and binary forms are permitted
6*33737Sbostic  * provided that this notice is preserved and that due credit is given
7*33737Sbostic  * to the University of California at Berkeley. The name of the University
8*33737Sbostic  * may not be used to endorse or promote products derived from this
9*33737Sbostic  * software without specific prior written permission. This software
10*33737Sbostic  * is provided ``as is'' without express or implied warranty.
1121740Sdist  */
1221740Sdist 
1310297Ssam #ifndef lint
1421740Sdist char copyright[] =
1526047Sminshall "@(#) Copyright (c) 1985 Regents of the University of California.\n\
1621740Sdist  All rights reserved.\n";
17*33737Sbostic #endif /* not lint */
1810297Ssam 
1921740Sdist #ifndef lint
20*33737Sbostic static char sccsid[] = "@(#)main.c	5.10 (Berkeley) 03/14/88";
21*33737Sbostic #endif /* not lint */
2221740Sdist 
2310297Ssam /*
2410297Ssam  * FTP User Program -- Command Interface.
2510297Ssam  */
2626047Sminshall #include "ftp_var.h"
2710297Ssam #include <sys/socket.h>
2810297Ssam #include <sys/ioctl.h>
2926495Sminshall #include <sys/types.h>
3010297Ssam 
3112398Ssam #include <arpa/ftp.h>
3212398Ssam 
3310297Ssam #include <signal.h>
3410297Ssam #include <stdio.h>
3510297Ssam #include <errno.h>
3610297Ssam #include <ctype.h>
3710297Ssam #include <netdb.h>
3811352Ssam #include <pwd.h>
3910297Ssam 
4010297Ssam 
4126495Sminshall uid_t	getuid();
4210297Ssam int	intr();
4310297Ssam int	lostpeer();
4411352Ssam extern	char *home;
4525907Smckusick char	*getlogin();
4610297Ssam 
4710297Ssam main(argc, argv)
4810297Ssam 	char *argv[];
4910297Ssam {
5010297Ssam 	register char *cp;
5110297Ssam 	int top;
5225907Smckusick 	struct passwd *pw = NULL;
5311352Ssam 	char homedir[MAXPATHLEN];
5410297Ssam 
5510297Ssam 	sp = getservbyname("ftp", "tcp");
5610297Ssam 	if (sp == 0) {
5710297Ssam 		fprintf(stderr, "ftp: ftp/tcp: unknown service\n");
5810297Ssam 		exit(1);
5910297Ssam 	}
6011351Ssam 	doglob = 1;
6111654Ssam 	interactive = 1;
6211351Ssam 	autologin = 1;
6310297Ssam 	argc--, argv++;
6410297Ssam 	while (argc > 0 && **argv == '-') {
6510297Ssam 		for (cp = *argv + 1; *cp; cp++)
6610297Ssam 			switch (*cp) {
6710297Ssam 
6810297Ssam 			case 'd':
6910297Ssam 				options |= SO_DEBUG;
7010297Ssam 				debug++;
7110297Ssam 				break;
7210297Ssam 
7310297Ssam 			case 'v':
7410297Ssam 				verbose++;
7510297Ssam 				break;
7610297Ssam 
7710297Ssam 			case 't':
7810297Ssam 				trace++;
7910297Ssam 				break;
8010297Ssam 
8110297Ssam 			case 'i':
8211654Ssam 				interactive = 0;
8310297Ssam 				break;
8410297Ssam 
8510297Ssam 			case 'n':
8610297Ssam 				autologin = 0;
8710297Ssam 				break;
8810297Ssam 
8911351Ssam 			case 'g':
9011351Ssam 				doglob = 0;
9111351Ssam 				break;
9211351Ssam 
9310297Ssam 			default:
9426495Sminshall 				fprintf(stdout,
9510297Ssam 				  "ftp: %c: unknown option\n", *cp);
9610297Ssam 				exit(1);
9710297Ssam 			}
9810297Ssam 		argc--, argv++;
9910297Ssam 	}
10010297Ssam 	fromatty = isatty(fileno(stdin));
10110297Ssam 	/*
10210297Ssam 	 * Set up defaults for FTP.
10310297Ssam 	 */
10426495Sminshall 	(void) strcpy(typename, "ascii"), type = TYPE_A;
10526495Sminshall 	(void) strcpy(formname, "non-print"), form = FORM_N;
10626495Sminshall 	(void) strcpy(modename, "stream"), mode = MODE_S;
10726495Sminshall 	(void) strcpy(structname, "file"), stru = STRU_F;
10826495Sminshall 	(void) strcpy(bytename, "8"), bytesize = 8;
10910297Ssam 	if (fromatty)
11010297Ssam 		verbose++;
11126047Sminshall 	cpend = 0;           /* no pending replies */
11226047Sminshall 	proxy = 0;	/* proxy not active */
11326047Sminshall 	crflag = 1;    /* strip c.r. on ascii gets */
11411352Ssam 	/*
11511352Ssam 	 * Set up the home directory in case we're globbing.
11611352Ssam 	 */
11725907Smckusick 	cp = getlogin();
11826047Sminshall 	if (cp != NULL) {
11925907Smckusick 		pw = getpwnam(cp);
12026047Sminshall 	}
12111352Ssam 	if (pw == NULL)
12211352Ssam 		pw = getpwuid(getuid());
12311352Ssam 	if (pw != NULL) {
12411352Ssam 		home = homedir;
12526495Sminshall 		(void) strcpy(home, pw->pw_dir);
12611352Ssam 	}
12710297Ssam 	if (argc > 0) {
12810297Ssam 		if (setjmp(toplevel))
12910297Ssam 			exit(0);
13026495Sminshall 		(void) signal(SIGINT, intr);
13126495Sminshall 		(void) signal(SIGPIPE, lostpeer);
13210297Ssam 		setpeer(argc + 1, argv - 1);
13310297Ssam 	}
13410297Ssam 	top = setjmp(toplevel) == 0;
13510297Ssam 	if (top) {
13626495Sminshall 		(void) signal(SIGINT, intr);
13726495Sminshall 		(void) signal(SIGPIPE, lostpeer);
13810297Ssam 	}
13910297Ssam 	for (;;) {
14010297Ssam 		cmdscanner(top);
14110297Ssam 		top = 1;
14210297Ssam 	}
14310297Ssam }
14410297Ssam 
14510297Ssam intr()
14610297Ssam {
14710297Ssam 
14810297Ssam 	longjmp(toplevel, 1);
14910297Ssam }
15010297Ssam 
15110297Ssam lostpeer()
15210297Ssam {
15310297Ssam 	extern FILE *cout;
15410297Ssam 	extern int data;
15510297Ssam 
15610297Ssam 	if (connected) {
15710297Ssam 		if (cout != NULL) {
15826495Sminshall 			(void) shutdown(fileno(cout), 1+1);
15926495Sminshall 			(void) fclose(cout);
16010297Ssam 			cout = NULL;
16110297Ssam 		}
16210297Ssam 		if (data >= 0) {
16326495Sminshall 			(void) shutdown(data, 1+1);
16410297Ssam 			(void) close(data);
16510297Ssam 			data = -1;
16610297Ssam 		}
16710297Ssam 		connected = 0;
16810297Ssam 	}
16926047Sminshall 	pswitch(1);
17026047Sminshall 	if (connected) {
17126047Sminshall 		if (cout != NULL) {
17226495Sminshall 			(void) shutdown(fileno(cout), 1+1);
17326495Sminshall 			(void) fclose(cout);
17426047Sminshall 			cout = NULL;
17526047Sminshall 		}
17626047Sminshall 		connected = 0;
17726047Sminshall 	}
17826047Sminshall 	proxflag = 0;
17926047Sminshall 	pswitch(0);
18010297Ssam }
18110297Ssam 
18226495Sminshall /*char *
18310297Ssam tail(filename)
18410297Ssam 	char *filename;
18510297Ssam {
18610297Ssam 	register char *s;
18710297Ssam 
18810297Ssam 	while (*filename) {
18910297Ssam 		s = rindex(filename, '/');
19010297Ssam 		if (s == NULL)
19110297Ssam 			break;
19210297Ssam 		if (s[1])
19310297Ssam 			return (s + 1);
19410297Ssam 		*s = '\0';
19510297Ssam 	}
19610297Ssam 	return (filename);
19710297Ssam }
19826495Sminshall */
19910297Ssam /*
20010297Ssam  * Command parser.
20110297Ssam  */
20210297Ssam cmdscanner(top)
20310297Ssam 	int top;
20410297Ssam {
20510297Ssam 	register struct cmd *c;
20610297Ssam 	struct cmd *getcmd();
20710297Ssam 	extern int help();
20810297Ssam 
20910297Ssam 	if (!top)
21026495Sminshall 		(void) putchar('\n');
21110297Ssam 	for (;;) {
21210297Ssam 		if (fromatty) {
21310297Ssam 			printf("ftp> ");
21426495Sminshall 			(void) fflush(stdout);
21510297Ssam 		}
21613968Ssam 		if (gets(line) == 0) {
21733374Stef 			if (feof(stdin) || ferror(stdin))
21826082Slepreau 				quit();
21910297Ssam 			break;
22013968Ssam 		}
22110297Ssam 		if (line[0] == 0)
22210297Ssam 			break;
22310297Ssam 		makeargv();
22426047Sminshall 		if (margc == 0) {
22525907Smckusick 			continue;
22626047Sminshall 		}
22710297Ssam 		c = getcmd(margv[0]);
22810297Ssam 		if (c == (struct cmd *)-1) {
22910297Ssam 			printf("?Ambiguous command\n");
23010297Ssam 			continue;
23110297Ssam 		}
23210297Ssam 		if (c == 0) {
23310297Ssam 			printf("?Invalid command\n");
23410297Ssam 			continue;
23510297Ssam 		}
23611654Ssam 		if (c->c_conn && !connected) {
23711654Ssam 			printf ("Not connected.\n");
23811654Ssam 			continue;
23911654Ssam 		}
24010297Ssam 		(*c->c_handler)(margc, margv);
24110297Ssam 		if (bell && c->c_bell)
24233116Sbostic 			(void) putchar(CTRL('g'));
24310297Ssam 		if (c->c_handler != help)
24410297Ssam 			break;
24510297Ssam 	}
24626495Sminshall 	(void) signal(SIGINT, intr);
24726495Sminshall 	(void) signal(SIGPIPE, lostpeer);
24810297Ssam }
24910297Ssam 
25010297Ssam struct cmd *
25110297Ssam getcmd(name)
25210297Ssam 	register char *name;
25310297Ssam {
25433224Sbostic 	extern struct cmd cmdtab[];
25510297Ssam 	register char *p, *q;
25610297Ssam 	register struct cmd *c, *found;
25710297Ssam 	register int nmatches, longest;
25810297Ssam 
25910297Ssam 	longest = 0;
26010297Ssam 	nmatches = 0;
26110297Ssam 	found = 0;
26210297Ssam 	for (c = cmdtab; p = c->c_name; c++) {
26310297Ssam 		for (q = name; *q == *p++; q++)
26410297Ssam 			if (*q == 0)		/* exact match? */
26510297Ssam 				return (c);
26610297Ssam 		if (!*q) {			/* the name was a prefix */
26710297Ssam 			if (q - name > longest) {
26810297Ssam 				longest = q - name;
26910297Ssam 				nmatches = 1;
27010297Ssam 				found = c;
27110297Ssam 			} else if (q - name == longest)
27210297Ssam 				nmatches++;
27310297Ssam 		}
27410297Ssam 	}
27510297Ssam 	if (nmatches > 1)
27610297Ssam 		return ((struct cmd *)-1);
27710297Ssam 	return (found);
27810297Ssam }
27910297Ssam 
28010297Ssam /*
28110297Ssam  * Slice a string up into argc/argv.
28210297Ssam  */
28326047Sminshall 
28426047Sminshall int slrflag;
28526047Sminshall 
28610297Ssam makeargv()
28710297Ssam {
28810297Ssam 	char **argp;
28910297Ssam 	char *slurpstring();
29010297Ssam 
29110297Ssam 	margc = 0;
29210297Ssam 	argp = margv;
29310297Ssam 	stringbase = line;		/* scan from first of buffer */
29410297Ssam 	argbase = argbuf;		/* store from first of buffer */
29526047Sminshall 	slrflag = 0;
29626047Sminshall 	while (*argp++ = slurpstring())
29710297Ssam 		margc++;
29810297Ssam }
29910297Ssam 
30010297Ssam /*
30110297Ssam  * Parse string into argbuf;
30210297Ssam  * implemented with FSM to
30310297Ssam  * handle quoting and strings
30410297Ssam  */
30510297Ssam char *
30610297Ssam slurpstring()
30710297Ssam {
30810297Ssam 	int got_one = 0;
30910297Ssam 	register char *sb = stringbase;
31010297Ssam 	register char *ap = argbase;
31110297Ssam 	char *tmp = argbase;		/* will return this if token found */
31210297Ssam 
31326047Sminshall 	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
31426047Sminshall 		switch (slrflag) {	/* and $ as token for macro invoke */
31526047Sminshall 			case 0:
31626047Sminshall 				slrflag++;
31726047Sminshall 				stringbase++;
31826047Sminshall 				return ((*sb == '!') ? "!" : "$");
31926047Sminshall 				break;
32026047Sminshall 			case 1:
32126047Sminshall 				slrflag++;
32226047Sminshall 				altarg = stringbase;
32326047Sminshall 				break;
32426047Sminshall 			default:
32526047Sminshall 				break;
32626047Sminshall 		}
32726047Sminshall 	}
32826047Sminshall 
32910297Ssam S0:
33010297Ssam 	switch (*sb) {
33110297Ssam 
33210297Ssam 	case '\0':
33310297Ssam 		goto OUT;
33410297Ssam 
33510297Ssam 	case ' ':
33610297Ssam 	case '\t':
33710297Ssam 		sb++; goto S0;
33810297Ssam 
33910297Ssam 	default:
34026047Sminshall 		switch (slrflag) {
34126047Sminshall 			case 0:
34226047Sminshall 				slrflag++;
34326047Sminshall 				break;
34426047Sminshall 			case 1:
34526047Sminshall 				slrflag++;
34626047Sminshall 				altarg = sb;
34726047Sminshall 				break;
34826047Sminshall 			default:
34926047Sminshall 				break;
35026047Sminshall 		}
35110297Ssam 		goto S1;
35210297Ssam 	}
35310297Ssam 
35410297Ssam S1:
35510297Ssam 	switch (*sb) {
35610297Ssam 
35710297Ssam 	case ' ':
35810297Ssam 	case '\t':
35910297Ssam 	case '\0':
36010297Ssam 		goto OUT;	/* end of token */
36110297Ssam 
36210297Ssam 	case '\\':
36310297Ssam 		sb++; goto S2;	/* slurp next character */
36410297Ssam 
36510297Ssam 	case '"':
36610297Ssam 		sb++; goto S3;	/* slurp quoted string */
36710297Ssam 
36810297Ssam 	default:
36910297Ssam 		*ap++ = *sb++;	/* add character to token */
37010297Ssam 		got_one = 1;
37110297Ssam 		goto S1;
37210297Ssam 	}
37310297Ssam 
37410297Ssam S2:
37510297Ssam 	switch (*sb) {
37610297Ssam 
37710297Ssam 	case '\0':
37810297Ssam 		goto OUT;
37910297Ssam 
38010297Ssam 	default:
38110297Ssam 		*ap++ = *sb++;
38210297Ssam 		got_one = 1;
38310297Ssam 		goto S1;
38410297Ssam 	}
38510297Ssam 
38610297Ssam S3:
38710297Ssam 	switch (*sb) {
38810297Ssam 
38910297Ssam 	case '\0':
39010297Ssam 		goto OUT;
39110297Ssam 
39210297Ssam 	case '"':
39310297Ssam 		sb++; goto S1;
39410297Ssam 
39510297Ssam 	default:
39610297Ssam 		*ap++ = *sb++;
39710297Ssam 		got_one = 1;
39810297Ssam 		goto S3;
39910297Ssam 	}
40010297Ssam 
40110297Ssam OUT:
40210297Ssam 	if (got_one)
40310297Ssam 		*ap++ = '\0';
40410297Ssam 	argbase = ap;			/* update storage pointer */
40510297Ssam 	stringbase = sb;		/* update scan pointer */
40626047Sminshall 	if (got_one) {
40710297Ssam 		return(tmp);
40826047Sminshall 	}
40926047Sminshall 	switch (slrflag) {
41026047Sminshall 		case 0:
41126047Sminshall 			slrflag++;
41226047Sminshall 			break;
41326047Sminshall 		case 1:
41426047Sminshall 			slrflag++;
41526047Sminshall 			altarg = (char *) 0;
41626047Sminshall 			break;
41726047Sminshall 		default:
41826047Sminshall 			break;
41926047Sminshall 	}
42010297Ssam 	return((char *)0);
42110297Ssam }
42210297Ssam 
42310297Ssam #define HELPINDENT (sizeof ("directory"))
42410297Ssam 
42510297Ssam /*
42610297Ssam  * Help command.
42710297Ssam  * Call each command handler with argc == 0 and argv[0] == name.
42810297Ssam  */
42910297Ssam help(argc, argv)
43010297Ssam 	int argc;
43110297Ssam 	char *argv[];
43210297Ssam {
43333224Sbostic 	extern struct cmd cmdtab[];
43410297Ssam 	register struct cmd *c;
43510297Ssam 
43610297Ssam 	if (argc == 1) {
43726047Sminshall 		register int i, j, w, k;
43810297Ssam 		int columns, width = 0, lines;
43910297Ssam 		extern int NCMDS;
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) {
47810297Ssam 		register 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 }
49010297Ssam 
49110297Ssam /*
49210297Ssam  * Call routine with argc, argv set from args (terminated by 0).
49310297Ssam  */
49426495Sminshall /*VARARGS1*/
49510297Ssam call(routine, args)
49610297Ssam 	int (*routine)();
49710297Ssam 	int args;
49810297Ssam {
49910297Ssam 	register int *argp;
50010297Ssam 	register int argc;
50110297Ssam 
50210297Ssam 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
50310297Ssam 		;
50410297Ssam 	(*routine)(argc, &args);
50510297Ssam }
506