xref: /csrg-svn/usr.bin/ftp/main.c (revision 36935)
121740Sdist /*
226047Sminshall  * Copyright (c) 1985 Regents of the University of California.
333737Sbostic  * All rights reserved.
433737Sbostic  *
533737Sbostic  * Redistribution and use in source and binary forms are permitted
634901Sbostic  * provided that the above copyright notice and this paragraph are
734901Sbostic  * duplicated in all such forms and that any documentation,
834901Sbostic  * advertising materials, and other materials related to such
934901Sbostic  * distribution and use acknowledge that the software was developed
1034901Sbostic  * by the University of California, Berkeley.  The name of the
1134901Sbostic  * University may not be used to endorse or promote products derived
1234901Sbostic  * from this software without specific prior written permission.
1334901Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434901Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*36935Skarels  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621740Sdist  */
1721740Sdist 
1810297Ssam #ifndef lint
1921740Sdist char copyright[] =
2026047Sminshall "@(#) Copyright (c) 1985 Regents of the University of California.\n\
2121740Sdist  All rights reserved.\n";
2233737Sbostic #endif /* not lint */
2310297Ssam 
2421740Sdist #ifndef lint
25*36935Skarels static char sccsid[] = "@(#)main.c	5.12 (Berkeley) 02/28/89";
2633737Sbostic #endif /* not lint */
2721740Sdist 
2810297Ssam /*
2910297Ssam  * FTP User Program -- Command Interface.
3010297Ssam  */
3126047Sminshall #include "ftp_var.h"
3210297Ssam #include <sys/socket.h>
3310297Ssam #include <sys/ioctl.h>
3426495Sminshall #include <sys/types.h>
3510297Ssam 
3612398Ssam #include <arpa/ftp.h>
3712398Ssam 
3810297Ssam #include <signal.h>
3910297Ssam #include <stdio.h>
4010297Ssam #include <errno.h>
4110297Ssam #include <ctype.h>
4210297Ssam #include <netdb.h>
4311352Ssam #include <pwd.h>
4410297Ssam 
4510297Ssam 
4626495Sminshall uid_t	getuid();
4710297Ssam int	intr();
4810297Ssam int	lostpeer();
4911352Ssam extern	char *home;
5025907Smckusick char	*getlogin();
5110297Ssam 
5210297Ssam main(argc, argv)
5310297Ssam 	char *argv[];
5410297Ssam {
5510297Ssam 	register char *cp;
5610297Ssam 	int top;
5725907Smckusick 	struct passwd *pw = NULL;
5811352Ssam 	char homedir[MAXPATHLEN];
5910297Ssam 
6010297Ssam 	sp = getservbyname("ftp", "tcp");
6110297Ssam 	if (sp == 0) {
6210297Ssam 		fprintf(stderr, "ftp: ftp/tcp: unknown service\n");
6310297Ssam 		exit(1);
6410297Ssam 	}
6511351Ssam 	doglob = 1;
6611654Ssam 	interactive = 1;
6711351Ssam 	autologin = 1;
6810297Ssam 	argc--, argv++;
6910297Ssam 	while (argc > 0 && **argv == '-') {
7010297Ssam 		for (cp = *argv + 1; *cp; cp++)
7110297Ssam 			switch (*cp) {
7210297Ssam 
7310297Ssam 			case 'd':
7410297Ssam 				options |= SO_DEBUG;
7510297Ssam 				debug++;
7610297Ssam 				break;
7710297Ssam 
7810297Ssam 			case 'v':
7910297Ssam 				verbose++;
8010297Ssam 				break;
8110297Ssam 
8210297Ssam 			case 't':
8310297Ssam 				trace++;
8410297Ssam 				break;
8510297Ssam 
8610297Ssam 			case 'i':
8711654Ssam 				interactive = 0;
8810297Ssam 				break;
8910297Ssam 
9010297Ssam 			case 'n':
9110297Ssam 				autologin = 0;
9210297Ssam 				break;
9310297Ssam 
9411351Ssam 			case 'g':
9511351Ssam 				doglob = 0;
9611351Ssam 				break;
9711351Ssam 
9810297Ssam 			default:
9926495Sminshall 				fprintf(stdout,
10010297Ssam 				  "ftp: %c: unknown option\n", *cp);
10110297Ssam 				exit(1);
10210297Ssam 			}
10310297Ssam 		argc--, argv++;
10410297Ssam 	}
10510297Ssam 	fromatty = isatty(fileno(stdin));
10610297Ssam 	/*
10710297Ssam 	 * Set up defaults for FTP.
10810297Ssam 	 */
10926495Sminshall 	(void) strcpy(typename, "ascii"), type = TYPE_A;
11026495Sminshall 	(void) strcpy(formname, "non-print"), form = FORM_N;
11126495Sminshall 	(void) strcpy(modename, "stream"), mode = MODE_S;
11226495Sminshall 	(void) strcpy(structname, "file"), stru = STRU_F;
11326495Sminshall 	(void) strcpy(bytename, "8"), bytesize = 8;
11410297Ssam 	if (fromatty)
11510297Ssam 		verbose++;
11626047Sminshall 	cpend = 0;           /* no pending replies */
11726047Sminshall 	proxy = 0;	/* proxy not active */
11826047Sminshall 	crflag = 1;    /* strip c.r. on ascii gets */
11911352Ssam 	/*
12011352Ssam 	 * Set up the home directory in case we're globbing.
12111352Ssam 	 */
12225907Smckusick 	cp = getlogin();
12326047Sminshall 	if (cp != NULL) {
12425907Smckusick 		pw = getpwnam(cp);
12526047Sminshall 	}
12611352Ssam 	if (pw == NULL)
12711352Ssam 		pw = getpwuid(getuid());
12811352Ssam 	if (pw != NULL) {
12911352Ssam 		home = homedir;
13026495Sminshall 		(void) strcpy(home, pw->pw_dir);
13111352Ssam 	}
13210297Ssam 	if (argc > 0) {
13310297Ssam 		if (setjmp(toplevel))
13410297Ssam 			exit(0);
13526495Sminshall 		(void) signal(SIGINT, intr);
13626495Sminshall 		(void) signal(SIGPIPE, lostpeer);
13710297Ssam 		setpeer(argc + 1, argv - 1);
13810297Ssam 	}
13910297Ssam 	top = setjmp(toplevel) == 0;
14010297Ssam 	if (top) {
14126495Sminshall 		(void) signal(SIGINT, intr);
14226495Sminshall 		(void) signal(SIGPIPE, lostpeer);
14310297Ssam 	}
14410297Ssam 	for (;;) {
14510297Ssam 		cmdscanner(top);
14610297Ssam 		top = 1;
14710297Ssam 	}
14810297Ssam }
14910297Ssam 
15010297Ssam intr()
15110297Ssam {
15210297Ssam 
15310297Ssam 	longjmp(toplevel, 1);
15410297Ssam }
15510297Ssam 
15610297Ssam lostpeer()
15710297Ssam {
15810297Ssam 	extern FILE *cout;
15910297Ssam 	extern int data;
16010297Ssam 
16110297Ssam 	if (connected) {
16210297Ssam 		if (cout != NULL) {
16326495Sminshall 			(void) shutdown(fileno(cout), 1+1);
16426495Sminshall 			(void) fclose(cout);
16510297Ssam 			cout = NULL;
16610297Ssam 		}
16710297Ssam 		if (data >= 0) {
16826495Sminshall 			(void) shutdown(data, 1+1);
16910297Ssam 			(void) close(data);
17010297Ssam 			data = -1;
17110297Ssam 		}
17210297Ssam 		connected = 0;
17310297Ssam 	}
17426047Sminshall 	pswitch(1);
17526047Sminshall 	if (connected) {
17626047Sminshall 		if (cout != NULL) {
17726495Sminshall 			(void) shutdown(fileno(cout), 1+1);
17826495Sminshall 			(void) fclose(cout);
17926047Sminshall 			cout = NULL;
18026047Sminshall 		}
18126047Sminshall 		connected = 0;
18226047Sminshall 	}
18326047Sminshall 	proxflag = 0;
18426047Sminshall 	pswitch(0);
18510297Ssam }
18610297Ssam 
18726495Sminshall /*char *
18810297Ssam tail(filename)
18910297Ssam 	char *filename;
19010297Ssam {
19110297Ssam 	register char *s;
19210297Ssam 
19310297Ssam 	while (*filename) {
19410297Ssam 		s = rindex(filename, '/');
19510297Ssam 		if (s == NULL)
19610297Ssam 			break;
19710297Ssam 		if (s[1])
19810297Ssam 			return (s + 1);
19910297Ssam 		*s = '\0';
20010297Ssam 	}
20110297Ssam 	return (filename);
20210297Ssam }
20326495Sminshall */
20410297Ssam /*
20510297Ssam  * Command parser.
20610297Ssam  */
20710297Ssam cmdscanner(top)
20810297Ssam 	int top;
20910297Ssam {
21010297Ssam 	register struct cmd *c;
21110297Ssam 	struct cmd *getcmd();
21210297Ssam 	extern int help();
21310297Ssam 
21410297Ssam 	if (!top)
21526495Sminshall 		(void) putchar('\n');
21610297Ssam 	for (;;) {
21710297Ssam 		if (fromatty) {
21810297Ssam 			printf("ftp> ");
21926495Sminshall 			(void) fflush(stdout);
22010297Ssam 		}
22113968Ssam 		if (gets(line) == 0) {
22233374Stef 			if (feof(stdin) || ferror(stdin))
22326082Slepreau 				quit();
22410297Ssam 			break;
22513968Ssam 		}
22610297Ssam 		if (line[0] == 0)
22710297Ssam 			break;
22810297Ssam 		makeargv();
22926047Sminshall 		if (margc == 0) {
23025907Smckusick 			continue;
23126047Sminshall 		}
23210297Ssam 		c = getcmd(margv[0]);
23310297Ssam 		if (c == (struct cmd *)-1) {
23410297Ssam 			printf("?Ambiguous command\n");
23510297Ssam 			continue;
23610297Ssam 		}
23710297Ssam 		if (c == 0) {
23810297Ssam 			printf("?Invalid command\n");
23910297Ssam 			continue;
24010297Ssam 		}
24111654Ssam 		if (c->c_conn && !connected) {
24211654Ssam 			printf ("Not connected.\n");
24311654Ssam 			continue;
24411654Ssam 		}
24510297Ssam 		(*c->c_handler)(margc, margv);
24610297Ssam 		if (bell && c->c_bell)
247*36935Skarels 			(void) putchar('\007');
24810297Ssam 		if (c->c_handler != help)
24910297Ssam 			break;
25010297Ssam 	}
25126495Sminshall 	(void) signal(SIGINT, intr);
25226495Sminshall 	(void) signal(SIGPIPE, lostpeer);
25310297Ssam }
25410297Ssam 
25510297Ssam struct cmd *
25610297Ssam getcmd(name)
25710297Ssam 	register char *name;
25810297Ssam {
25933224Sbostic 	extern struct cmd cmdtab[];
26010297Ssam 	register char *p, *q;
26110297Ssam 	register struct cmd *c, *found;
26210297Ssam 	register int nmatches, longest;
26310297Ssam 
26410297Ssam 	longest = 0;
26510297Ssam 	nmatches = 0;
26610297Ssam 	found = 0;
26710297Ssam 	for (c = cmdtab; p = c->c_name; c++) {
26810297Ssam 		for (q = name; *q == *p++; q++)
26910297Ssam 			if (*q == 0)		/* exact match? */
27010297Ssam 				return (c);
27110297Ssam 		if (!*q) {			/* the name was a prefix */
27210297Ssam 			if (q - name > longest) {
27310297Ssam 				longest = q - name;
27410297Ssam 				nmatches = 1;
27510297Ssam 				found = c;
27610297Ssam 			} else if (q - name == longest)
27710297Ssam 				nmatches++;
27810297Ssam 		}
27910297Ssam 	}
28010297Ssam 	if (nmatches > 1)
28110297Ssam 		return ((struct cmd *)-1);
28210297Ssam 	return (found);
28310297Ssam }
28410297Ssam 
28510297Ssam /*
28610297Ssam  * Slice a string up into argc/argv.
28710297Ssam  */
28826047Sminshall 
28926047Sminshall int slrflag;
29026047Sminshall 
29110297Ssam makeargv()
29210297Ssam {
29310297Ssam 	char **argp;
29410297Ssam 	char *slurpstring();
29510297Ssam 
29610297Ssam 	margc = 0;
29710297Ssam 	argp = margv;
29810297Ssam 	stringbase = line;		/* scan from first of buffer */
29910297Ssam 	argbase = argbuf;		/* store from first of buffer */
30026047Sminshall 	slrflag = 0;
30126047Sminshall 	while (*argp++ = slurpstring())
30210297Ssam 		margc++;
30310297Ssam }
30410297Ssam 
30510297Ssam /*
30610297Ssam  * Parse string into argbuf;
30710297Ssam  * implemented with FSM to
30810297Ssam  * handle quoting and strings
30910297Ssam  */
31010297Ssam char *
31110297Ssam slurpstring()
31210297Ssam {
31310297Ssam 	int got_one = 0;
31410297Ssam 	register char *sb = stringbase;
31510297Ssam 	register char *ap = argbase;
31610297Ssam 	char *tmp = argbase;		/* will return this if token found */
31710297Ssam 
31826047Sminshall 	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
31926047Sminshall 		switch (slrflag) {	/* and $ as token for macro invoke */
32026047Sminshall 			case 0:
32126047Sminshall 				slrflag++;
32226047Sminshall 				stringbase++;
32326047Sminshall 				return ((*sb == '!') ? "!" : "$");
324*36935Skarels 				/* NOTREACHED */
32526047Sminshall 			case 1:
32626047Sminshall 				slrflag++;
32726047Sminshall 				altarg = stringbase;
32826047Sminshall 				break;
32926047Sminshall 			default:
33026047Sminshall 				break;
33126047Sminshall 		}
33226047Sminshall 	}
33326047Sminshall 
33410297Ssam S0:
33510297Ssam 	switch (*sb) {
33610297Ssam 
33710297Ssam 	case '\0':
33810297Ssam 		goto OUT;
33910297Ssam 
34010297Ssam 	case ' ':
34110297Ssam 	case '\t':
34210297Ssam 		sb++; goto S0;
34310297Ssam 
34410297Ssam 	default:
34526047Sminshall 		switch (slrflag) {
34626047Sminshall 			case 0:
34726047Sminshall 				slrflag++;
34826047Sminshall 				break;
34926047Sminshall 			case 1:
35026047Sminshall 				slrflag++;
35126047Sminshall 				altarg = sb;
35226047Sminshall 				break;
35326047Sminshall 			default:
35426047Sminshall 				break;
35526047Sminshall 		}
35610297Ssam 		goto S1;
35710297Ssam 	}
35810297Ssam 
35910297Ssam S1:
36010297Ssam 	switch (*sb) {
36110297Ssam 
36210297Ssam 	case ' ':
36310297Ssam 	case '\t':
36410297Ssam 	case '\0':
36510297Ssam 		goto OUT;	/* end of token */
36610297Ssam 
36710297Ssam 	case '\\':
36810297Ssam 		sb++; goto S2;	/* slurp next character */
36910297Ssam 
37010297Ssam 	case '"':
37110297Ssam 		sb++; goto S3;	/* slurp quoted string */
37210297Ssam 
37310297Ssam 	default:
37410297Ssam 		*ap++ = *sb++;	/* add character to token */
37510297Ssam 		got_one = 1;
37610297Ssam 		goto S1;
37710297Ssam 	}
37810297Ssam 
37910297Ssam S2:
38010297Ssam 	switch (*sb) {
38110297Ssam 
38210297Ssam 	case '\0':
38310297Ssam 		goto OUT;
38410297Ssam 
38510297Ssam 	default:
38610297Ssam 		*ap++ = *sb++;
38710297Ssam 		got_one = 1;
38810297Ssam 		goto S1;
38910297Ssam 	}
39010297Ssam 
39110297Ssam S3:
39210297Ssam 	switch (*sb) {
39310297Ssam 
39410297Ssam 	case '\0':
39510297Ssam 		goto OUT;
39610297Ssam 
39710297Ssam 	case '"':
39810297Ssam 		sb++; goto S1;
39910297Ssam 
40010297Ssam 	default:
40110297Ssam 		*ap++ = *sb++;
40210297Ssam 		got_one = 1;
40310297Ssam 		goto S3;
40410297Ssam 	}
40510297Ssam 
40610297Ssam OUT:
40710297Ssam 	if (got_one)
40810297Ssam 		*ap++ = '\0';
40910297Ssam 	argbase = ap;			/* update storage pointer */
41010297Ssam 	stringbase = sb;		/* update scan pointer */
41126047Sminshall 	if (got_one) {
41210297Ssam 		return(tmp);
41326047Sminshall 	}
41426047Sminshall 	switch (slrflag) {
41526047Sminshall 		case 0:
41626047Sminshall 			slrflag++;
41726047Sminshall 			break;
41826047Sminshall 		case 1:
41926047Sminshall 			slrflag++;
42026047Sminshall 			altarg = (char *) 0;
42126047Sminshall 			break;
42226047Sminshall 		default:
42326047Sminshall 			break;
42426047Sminshall 	}
42510297Ssam 	return((char *)0);
42610297Ssam }
42710297Ssam 
42810297Ssam #define HELPINDENT (sizeof ("directory"))
42910297Ssam 
43010297Ssam /*
43110297Ssam  * Help command.
43210297Ssam  * Call each command handler with argc == 0 and argv[0] == name.
43310297Ssam  */
43410297Ssam help(argc, argv)
43510297Ssam 	int argc;
43610297Ssam 	char *argv[];
43710297Ssam {
43833224Sbostic 	extern struct cmd cmdtab[];
43910297Ssam 	register struct cmd *c;
44010297Ssam 
44110297Ssam 	if (argc == 1) {
44226047Sminshall 		register int i, j, w, k;
44310297Ssam 		int columns, width = 0, lines;
44410297Ssam 		extern int NCMDS;
44510297Ssam 
44610297Ssam 		printf("Commands may be abbreviated.  Commands are:\n\n");
44710297Ssam 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
44810297Ssam 			int len = strlen(c->c_name);
44910297Ssam 
45010297Ssam 			if (len > width)
45110297Ssam 				width = len;
45210297Ssam 		}
45310297Ssam 		width = (width + 8) &~ 7;
45410297Ssam 		columns = 80 / width;
45510297Ssam 		if (columns == 0)
45610297Ssam 			columns = 1;
45710297Ssam 		lines = (NCMDS + columns - 1) / columns;
45810297Ssam 		for (i = 0; i < lines; i++) {
45910297Ssam 			for (j = 0; j < columns; j++) {
46010297Ssam 				c = cmdtab + j * lines + i;
46126047Sminshall 				if (c->c_name && (!proxy || c->c_proxy)) {
46225907Smckusick 					printf("%s", c->c_name);
46326047Sminshall 				}
46426047Sminshall 				else if (c->c_name) {
46526047Sminshall 					for (k=0; k < strlen(c->c_name); k++) {
46626495Sminshall 						(void) putchar(' ');
46726047Sminshall 					}
46826047Sminshall 				}
46910297Ssam 				if (c + lines >= &cmdtab[NCMDS]) {
47010297Ssam 					printf("\n");
47110297Ssam 					break;
47210297Ssam 				}
47310297Ssam 				w = strlen(c->c_name);
47410297Ssam 				while (w < width) {
47510297Ssam 					w = (w + 8) &~ 7;
47626495Sminshall 					(void) putchar('\t');
47710297Ssam 				}
47810297Ssam 			}
47910297Ssam 		}
48010297Ssam 		return;
48110297Ssam 	}
48210297Ssam 	while (--argc > 0) {
48310297Ssam 		register char *arg;
48410297Ssam 		arg = *++argv;
48510297Ssam 		c = getcmd(arg);
48610297Ssam 		if (c == (struct cmd *)-1)
48710297Ssam 			printf("?Ambiguous help command %s\n", arg);
48810297Ssam 		else if (c == (struct cmd *)0)
48910297Ssam 			printf("?Invalid help command %s\n", arg);
49010297Ssam 		else
49110297Ssam 			printf("%-*s\t%s\n", HELPINDENT,
49210297Ssam 				c->c_name, c->c_help);
49310297Ssam 	}
49410297Ssam }
49510297Ssam 
49610297Ssam /*
49710297Ssam  * Call routine with argc, argv set from args (terminated by 0).
49810297Ssam  */
49926495Sminshall /*VARARGS1*/
50010297Ssam call(routine, args)
50110297Ssam 	int (*routine)();
50210297Ssam 	int args;
50310297Ssam {
50410297Ssam 	register int *argp;
50510297Ssam 	register int argc;
50610297Ssam 
50710297Ssam 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
50810297Ssam 		;
50910297Ssam 	(*routine)(argc, &args);
51010297Ssam }
511