xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 13033)
110276Ssam /*
210276Ssam  * Grammar for FTP commands.
310276Ssam  * See RFC 765.
410276Ssam  */
510276Ssam 
610276Ssam %{
710276Ssam 
810276Ssam #ifndef lint
9*13033Ssam static char sccsid[] = "@(#)ftpcmd.y	4.10 83/06/12";
1010276Ssam #endif
1110276Ssam 
1210276Ssam #include <sys/types.h>
1310276Ssam #include <sys/socket.h>
1410276Ssam 
1510276Ssam #include <netinet/in.h>
1610276Ssam 
17*13033Ssam #include <arpa/ftp.h>
18*13033Ssam 
1910276Ssam #include <stdio.h>
2011652Ssam #include <signal.h>
2110276Ssam #include <ctype.h>
2210276Ssam #include <pwd.h>
2310276Ssam #include <setjmp.h>
2410276Ssam 
2510276Ssam extern	struct sockaddr_in data_dest;
2610276Ssam extern	int logged_in;
2710276Ssam extern	struct passwd *pw;
2810276Ssam extern	int guest;
2910276Ssam extern	int logging;
3010276Ssam extern	int type;
3110276Ssam extern	int form;
3210276Ssam extern	int debug;
3311652Ssam extern	int timeout;
3410276Ssam extern	char hostname[];
3510276Ssam extern	char *globerr;
3610320Ssam extern	int usedefault;
3710276Ssam char	**glob();
3810276Ssam 
3910276Ssam static	int cmd_type;
4010276Ssam static	int cmd_form;
4110276Ssam static	int cmd_bytesz;
4210276Ssam 
4310276Ssam char	*index();
4410276Ssam %}
4510276Ssam 
4610276Ssam %token
4710276Ssam 	A	B	C	E	F	I
4810276Ssam 	L	N	P	R	S	T
4910276Ssam 
5010276Ssam 	SP	CRLF	COMMA	STRING	NUMBER
5110276Ssam 
5210276Ssam 	USER	PASS	ACCT	REIN	QUIT	PORT
5310276Ssam 	PASV	TYPE	STRU	MODE	RETR	STOR
5410276Ssam 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
5510276Ssam 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
5610276Ssam 	ABOR	DELE	CWD	LIST	NLST	SITE
5710276Ssam 	STAT	HELP	NOOP	XMKD	XRMD	XPWD
5810276Ssam 	XCUP
5910276Ssam 
6010276Ssam 	LEXERR
6110276Ssam 
6210276Ssam %start	cmd_list
6310276Ssam 
6410276Ssam %%
6510276Ssam 
6610276Ssam cmd_list:	/* empty */
6710276Ssam 	|	cmd_list cmd
6810276Ssam 	;
6910276Ssam 
7010276Ssam cmd:		USER SP username CRLF
7110276Ssam 		= {
7210276Ssam 			extern struct passwd *getpwnam();
7310276Ssam 
7410324Ssam 			if (strcmp($3, "ftp") == 0 ||
7510324Ssam 			  strcmp($3, "anonymous") == 0) {
7610324Ssam 				if ((pw = getpwnam("ftp")) != NULL) {
7710324Ssam 					guest = 1;
7810324Ssam 					reply(331,
7910276Ssam 				  "Guest login ok, send ident as password.");
8010324Ssam 				}
8110694Ssam 			} else if (checkuser($3)) {
8210276Ssam 				guest = 0;
8310276Ssam 				pw = getpwnam($3);
8410276Ssam 				reply(331, "Password required for %s.", $3);
8510276Ssam 			}
8610319Ssam 			if (pw == NULL)
8710319Ssam 				reply(530, "User %s unknown.", $3);
8810276Ssam 			free($3);
8910276Ssam 		}
9010276Ssam 	|	PASS SP password CRLF
9110276Ssam 		= {
9210276Ssam 			pass($3);
9310276Ssam 			free($3);
9410276Ssam 		}
9510276Ssam 	|	PORT SP host_port CRLF
9610276Ssam 		= {
9710320Ssam 			usedefault = 0;
9810276Ssam 			ack($1);
9910276Ssam 		}
10010276Ssam 	|	TYPE SP type_code CRLF
10110276Ssam 		= {
10210276Ssam 			switch (cmd_type) {
10310276Ssam 
10410276Ssam 			case TYPE_A:
10510276Ssam 				if (cmd_form == FORM_N) {
10610276Ssam 					reply(200, "Type set to A.");
10710276Ssam 					type = cmd_type;
10810276Ssam 					form = cmd_form;
10910276Ssam 				} else
11010276Ssam 					reply(504, "Form must be N.");
11110276Ssam 				break;
11210276Ssam 
11310276Ssam 			case TYPE_E:
11410276Ssam 				reply(504, "Type E not implemented.");
11510276Ssam 				break;
11610276Ssam 
11710276Ssam 			case TYPE_I:
11810276Ssam 				reply(200, "Type set to I.");
11910276Ssam 				type = cmd_type;
12010276Ssam 				break;
12110276Ssam 
12210276Ssam 			case TYPE_L:
12310276Ssam 				if (cmd_bytesz == 8) {
12410276Ssam 					reply(200,
12510276Ssam 					    "Type set to L (byte size 8).");
12610276Ssam 					type = cmd_type;
12710276Ssam 				} else
12810276Ssam 					reply(504, "Byte size must be 8.");
12910276Ssam 			}
13010276Ssam 		}
13110276Ssam 	|	STRU SP struct_code CRLF
13210276Ssam 		= {
13310276Ssam 			switch ($3) {
13410276Ssam 
13510276Ssam 			case STRU_F:
13610276Ssam 				reply(200, "STRU F ok.");
13710276Ssam 				break;
13810276Ssam 
13910276Ssam 			default:
14010276Ssam 				reply(502, "Unimplemented STRU type.");
14110276Ssam 			}
14210276Ssam 		}
14310276Ssam 	|	MODE SP mode_code CRLF
14410276Ssam 		= {
14510276Ssam 			switch ($3) {
14610276Ssam 
14710276Ssam 			case MODE_S:
14810276Ssam 				reply(200, "MODE S ok.");
14910276Ssam 				break;
15010276Ssam 
15110276Ssam 			default:
15210276Ssam 				reply(502, "Unimplemented MODE type.");
15310276Ssam 			}
15410276Ssam 		}
15510276Ssam 	|	ALLO SP NUMBER CRLF
15610276Ssam 		= {
15710276Ssam 			ack($1);
15810276Ssam 		}
15910276Ssam 	|	RETR check_login SP pathname CRLF
16010276Ssam 		= {
16110302Ssam 			if ($2 && $4 != NULL)
16210276Ssam 				retrieve(0, $4);
16310302Ssam 			if ($4 != NULL)
16410302Ssam 				free($4);
16510276Ssam 		}
16610276Ssam 	|	STOR check_login SP pathname CRLF
16710276Ssam 		= {
16810302Ssam 			if ($2 && $4 != NULL)
16910276Ssam 				store($4, "w");
17010302Ssam 			if ($4 != NULL)
17110302Ssam 				free($4);
17210276Ssam 		}
17310276Ssam 	|	APPE check_login SP pathname CRLF
17410276Ssam 		= {
17510302Ssam 			if ($2 && $4 != NULL)
17610276Ssam 				store($4, "a");
17710302Ssam 			if ($4 != NULL)
17810302Ssam 				free($4);
17910276Ssam 		}
18010276Ssam 	|	NLST check_login CRLF
18110276Ssam 		= {
18210276Ssam 			if ($2)
18311217Ssam 				retrieve("/bin/ls", "");
18410276Ssam 		}
18510276Ssam 	|	NLST check_login SP pathname CRLF
18610276Ssam 		= {
18710302Ssam 			if ($2 && $4 != NULL)
18811217Ssam 				retrieve("/bin/ls %s", $4);
18910302Ssam 			if ($4 != NULL)
19010302Ssam 				free($4);
19110276Ssam 		}
19210276Ssam 	|	LIST check_login CRLF
19310276Ssam 		= {
19410276Ssam 			if ($2)
19510318Ssam 				retrieve("/bin/ls -lg", "");
19610276Ssam 		}
19710276Ssam 	|	LIST check_login SP pathname CRLF
19810276Ssam 		= {
19910302Ssam 			if ($2 && $4 != NULL)
20010318Ssam 				retrieve("/bin/ls -lg %s", $4);
20110302Ssam 			if ($4 != NULL)
20210302Ssam 				free($4);
20310276Ssam 		}
20410276Ssam 	|	DELE check_login SP pathname CRLF
20510276Ssam 		= {
20610302Ssam 			if ($2 && $4 != NULL)
20710276Ssam 				delete($4);
20810302Ssam 			if ($4 != NULL)
20910302Ssam 				free($4);
21010276Ssam 		}
21110276Ssam 	|	CWD check_login CRLF
21210276Ssam 		= {
21310276Ssam 			if ($2)
21410276Ssam 				cwd(pw->pw_dir);
21510276Ssam 		}
21610276Ssam 	|	CWD check_login SP pathname CRLF
21710276Ssam 		= {
21810302Ssam 			if ($2 && $4 != NULL)
21910276Ssam 				cwd($4);
22010302Ssam 			if ($4 != NULL)
22110302Ssam 				free($4);
22210276Ssam 		}
22310276Ssam 	|	rename_cmd
22410276Ssam 	|	HELP CRLF
22510276Ssam 		= {
22610276Ssam 			help(0);
22710276Ssam 		}
22810276Ssam 	|	HELP SP STRING CRLF
22910276Ssam 		= {
23010276Ssam 			help($3);
23110276Ssam 		}
23210276Ssam 	|	NOOP CRLF
23310276Ssam 		= {
23410276Ssam 			ack($1);
23510276Ssam 		}
23610276Ssam 	|	XMKD check_login SP pathname CRLF
23710276Ssam 		= {
23810302Ssam 			if ($2 && $4 != NULL)
23910302Ssam 				makedir($4);
24010302Ssam 			if ($4 != NULL)
24110302Ssam 				free($4);
24210276Ssam 		}
24310276Ssam 	|	XRMD check_login SP pathname CRLF
24410276Ssam 		= {
24510302Ssam 			if ($2 && $4 != NULL)
24610302Ssam 				removedir($4);
24710302Ssam 			if ($4 != NULL)
24810302Ssam 				free($4);
24910276Ssam 		}
25010276Ssam 	|	XPWD check_login CRLF
25110276Ssam 		= {
25210276Ssam 			if ($2)
25310302Ssam 				pwd();
25410276Ssam 		}
25510276Ssam 	|	XCUP check_login CRLF
25610276Ssam 		= {
25710276Ssam 			if ($2)
25810276Ssam 				cwd("..");
25910276Ssam 		}
26010276Ssam 	|	QUIT CRLF
26110276Ssam 		= {
26210276Ssam 			reply(221, "Goodbye.");
26310276Ssam 			exit(0);
26410276Ssam 		}
26510276Ssam 	|	error CRLF
26610276Ssam 		= {
26710276Ssam 			yyerrok;
26810276Ssam 		}
26910276Ssam 	;
27010276Ssam 
27110276Ssam username:	STRING
27210276Ssam 	;
27310276Ssam 
27410276Ssam password:	STRING
27510276Ssam 	;
27610276Ssam 
27710276Ssam byte_size:	NUMBER
27810276Ssam 	;
27910276Ssam 
28010276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
28110276Ssam 		NUMBER COMMA NUMBER
28210276Ssam 		= {
28310276Ssam 			register char *a, *p;
28410276Ssam 
28510276Ssam 			a = (char *)&data_dest.sin_addr;
28610276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
28710276Ssam 			p = (char *)&data_dest.sin_port;
28810276Ssam 			p[0] = $9; p[1] = $11;
28910324Ssam 			data_dest.sin_family = AF_INET;
29010276Ssam 		}
29110276Ssam 	;
29210276Ssam 
29310276Ssam form_code:	N
29410276Ssam 	= {
29510276Ssam 		$$ = FORM_N;
29610276Ssam 	}
29710276Ssam 	|	T
29810276Ssam 	= {
29910276Ssam 		$$ = FORM_T;
30010276Ssam 	}
30110276Ssam 	|	C
30210276Ssam 	= {
30310276Ssam 		$$ = FORM_C;
30410276Ssam 	}
30510276Ssam 	;
30610276Ssam 
30710276Ssam type_code:	A
30810276Ssam 	= {
30910276Ssam 		cmd_type = TYPE_A;
31010276Ssam 		cmd_form = FORM_N;
31110276Ssam 	}
31210276Ssam 	|	A SP form_code
31310276Ssam 	= {
31410276Ssam 		cmd_type = TYPE_A;
31510276Ssam 		cmd_form = $3;
31610276Ssam 	}
31710276Ssam 	|	E
31810276Ssam 	= {
31910276Ssam 		cmd_type = TYPE_E;
32010276Ssam 		cmd_form = FORM_N;
32110276Ssam 	}
32210276Ssam 	|	E SP form_code
32310276Ssam 	= {
32410276Ssam 		cmd_type = TYPE_E;
32510276Ssam 		cmd_form = $3;
32610276Ssam 	}
32710276Ssam 	|	I
32810276Ssam 	= {
32910276Ssam 		cmd_type = TYPE_I;
33010276Ssam 	}
33110276Ssam 	|	L
33210276Ssam 	= {
33310276Ssam 		cmd_type = TYPE_L;
33410276Ssam 		cmd_bytesz = 8;
33510276Ssam 	}
33610276Ssam 	|	L SP byte_size
33710276Ssam 	= {
33810276Ssam 		cmd_type = TYPE_L;
33910276Ssam 		cmd_bytesz = $3;
34010276Ssam 	}
34110276Ssam 	/* this is for a bug in the BBN ftp */
34210276Ssam 	|	L byte_size
34310276Ssam 	= {
34410276Ssam 		cmd_type = TYPE_L;
34510276Ssam 		cmd_bytesz = $2;
34610276Ssam 	}
34710276Ssam 	;
34810276Ssam 
34910276Ssam struct_code:	F
35010276Ssam 	= {
35110276Ssam 		$$ = STRU_F;
35210276Ssam 	}
35310276Ssam 	|	R
35410276Ssam 	= {
35510276Ssam 		$$ = STRU_R;
35610276Ssam 	}
35710276Ssam 	|	P
35810276Ssam 	= {
35910276Ssam 		$$ = STRU_P;
36010276Ssam 	}
36110276Ssam 	;
36210276Ssam 
36310276Ssam mode_code:	S
36410276Ssam 	= {
36510276Ssam 		$$ = MODE_S;
36610276Ssam 	}
36710276Ssam 	|	B
36810276Ssam 	= {
36910276Ssam 		$$ = MODE_B;
37010276Ssam 	}
37110276Ssam 	|	C
37210276Ssam 	= {
37310276Ssam 		$$ = MODE_C;
37410276Ssam 	}
37510276Ssam 	;
37610276Ssam 
37710276Ssam pathname:	pathstring
37810276Ssam 	= {
37910276Ssam 		if ($1 && strncmp($1, "~", 1) == 0) {
38010276Ssam 			$$ = (int)*glob($1);
38110302Ssam 			if (globerr != NULL) {
38210276Ssam 				reply(550, globerr);
38310302Ssam 				$$ = NULL;
38410302Ssam 			}
38510276Ssam 			free($1);
38610276Ssam 		} else
38710276Ssam 			$$ = $1;
38810276Ssam 	}
38910276Ssam 	;
39010276Ssam 
39110276Ssam pathstring:	STRING
39210276Ssam 	;
39310276Ssam 
39410276Ssam rename_cmd:	rename_from rename_to
39510276Ssam 	= {
39610276Ssam 		if ($1 && $2)
39710276Ssam 			renamecmd($1, $2);
39810276Ssam 		else
39910276Ssam 			reply(503, "Bad sequence of commands.");
40010276Ssam 		if ($1)
40110276Ssam 			free($1);
40210276Ssam 		if ($2)
40310276Ssam 			free($2);
40410276Ssam 	}
40510276Ssam 	;
40610276Ssam 
40710276Ssam rename_from:	RNFR check_login SP pathname CRLF
40810276Ssam 	= {
40910276Ssam 		char *from = 0, *renamefrom();
41010276Ssam 
41110302Ssam 		if ($2 && $4)
41210276Ssam 			from = renamefrom($4);
41310302Ssam 		if (from == 0 && $4)
41410276Ssam 			free($4);
41510276Ssam 		$$ = (int)from;
41610276Ssam 	}
41710276Ssam 	;
41810276Ssam 
41910276Ssam rename_to:	RNTO SP pathname CRLF
42010276Ssam 	= {
42110276Ssam 		$$ = $3;
42210276Ssam 	}
42310276Ssam 	;
42410276Ssam 
42510276Ssam check_login:	/* empty */
42610276Ssam 	= {
42710276Ssam 		if (logged_in)
42810276Ssam 			$$ = 1;
42910276Ssam 		else {
43010276Ssam 			reply(530, "Please login with USER and PASS.");
43110276Ssam 			$$ = 0;
43210276Ssam 		}
43310276Ssam 	}
43410276Ssam 	;
43510276Ssam 
43610276Ssam %%
43710276Ssam 
43810276Ssam extern jmp_buf errcatch;
43910276Ssam 
44010276Ssam #define	CMD	0	/* beginning of command */
44110276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
44210276Ssam #define	STR1	2	/* expect SP followed by STRING */
44310276Ssam #define	STR2	3	/* expect STRING */
44410276Ssam #define	OSTR	4	/* optional STRING */
44510276Ssam 
44610276Ssam struct tab {
44710276Ssam 	char	*name;
44810276Ssam 	short	token;
44910276Ssam 	short	state;
45010276Ssam 	short	implemented;	/* 1 if command is implemented */
45110276Ssam 	char	*help;
45210276Ssam };
45310276Ssam 
45410276Ssam struct tab cmdtab[] = {		/* In order defined in RFC 765 */
45510276Ssam 	{ "USER", USER, STR1, 1,	"<sp> username" },
45610276Ssam 	{ "PASS", PASS, STR1, 1,	"<sp> password" },
45710276Ssam 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
45810276Ssam 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
45910276Ssam 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
46010276Ssam 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
46110276Ssam 	{ "PASV", PASV, ARGS, 0,	"(set server in passive mode)" },
46210276Ssam 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
46310276Ssam 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
46410276Ssam 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
46510276Ssam 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
46610276Ssam 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
46710276Ssam 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
46810276Ssam 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
46910276Ssam 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
47010276Ssam 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
47110276Ssam 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
47210276Ssam 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
47310276Ssam 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
47410276Ssam 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
47510276Ssam 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
47610276Ssam 	{ "REST", REST, STR1, 0,	"(restart command)" },
47710276Ssam 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
47810276Ssam 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
47910276Ssam 	{ "ABOR", ABOR, ARGS, 0,	"(abort operation)" },
48010276Ssam 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
48110276Ssam 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name]" },
48210276Ssam 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
48310276Ssam 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
48410276Ssam 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
48510276Ssam 	{ "SITE", SITE, STR1, 0,	"(get site parameters)" },
48610276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
48710276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
48810276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
48910276Ssam 	{ "XMKD", XMKD, STR1, 1,	"<sp> path-name" },
49010276Ssam 	{ "XRMD", XRMD, STR1, 1,	"<sp> path-name" },
49110276Ssam 	{ "XPWD", XPWD, ARGS, 1,	"(return current directory)" },
49210276Ssam 	{ "XCUP", XCUP, ARGS, 1,	"(change to parent directory)" },
49310276Ssam 	{ NULL,   0,    0,    0,	0 }
49410276Ssam };
49510276Ssam 
49610276Ssam struct tab *
49710276Ssam lookup(cmd)
49810276Ssam 	char *cmd;
49910276Ssam {
50010276Ssam 	register struct tab *p;
50110276Ssam 
50210276Ssam 	for (p = cmdtab; p->name != NULL; p++)
50310276Ssam 		if (strcmp(cmd, p->name) == 0)
50410276Ssam 			return (p);
50510276Ssam 	return (0);
50610276Ssam }
50710276Ssam 
508*13033Ssam #include <arpa/telnet.h>
50910276Ssam 
51010276Ssam /*
51110276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
51210276Ssam  */
51310276Ssam char *
51410276Ssam getline(s, n, iop)
51510276Ssam 	char *s;
51610276Ssam 	register FILE *iop;
51710276Ssam {
51810276Ssam 	register c;
51910276Ssam 	register char *cs;
52010276Ssam 
52110276Ssam 	cs = s;
52210276Ssam 	while (--n > 0 && (c = getc(iop)) >= 0) {
52310276Ssam 		while (c == IAC) {
52410276Ssam 			c = getc(iop);	/* skip command */
52510276Ssam 			c = getc(iop);	/* try next char */
52610276Ssam 		}
52710276Ssam 		*cs++ = c;
52810276Ssam 		if (c=='\n')
52910276Ssam 			break;
53010276Ssam 	}
53110276Ssam 	if (c < 0 && cs == s)
53210276Ssam 		return (NULL);
53310276Ssam 	*cs++ = '\0';
53411652Ssam 	if (debug) {
53511652Ssam 		fprintf(stderr, "FTPD: command: %s", s);
53611652Ssam 		if (c != '\n')
53711652Ssam 			putc('\n', stderr);
53811652Ssam 		fflush(stderr);
53911652Ssam 	}
54010276Ssam 	return (s);
54110276Ssam }
54210276Ssam 
54311652Ssam static int
54411652Ssam toolong()
54511652Ssam {
54611652Ssam 	long now;
54711652Ssam 	extern char *ctime();
54811652Ssam 
54911652Ssam 	reply(421,
55011652Ssam 	  "Timeout (%d seconds): closing control connection.", timeout);
55111652Ssam 	time(&now);
55211652Ssam 	if (logging) {
55311652Ssam 		fprintf(stderr,
55411652Ssam 			"FTPD: User %s timed out after %d seconds at %s",
55511652Ssam 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
55611652Ssam 		fflush(stderr);
55711652Ssam 	}
55811652Ssam 	exit(1);
55911652Ssam }
56011652Ssam 
56110276Ssam yylex()
56210276Ssam {
56310276Ssam 	static char cbuf[512];
56410276Ssam 	static int cpos, state;
56510276Ssam 	register char *cp;
56610276Ssam 	register struct tab *p;
56710276Ssam 	int n;
56810276Ssam 	char c;
56910276Ssam 
57010276Ssam 	for (;;) {
57110276Ssam 		switch (state) {
57210276Ssam 
57310276Ssam 		case CMD:
57411652Ssam 			signal(SIGALRM, toolong);
57511652Ssam 			alarm(timeout);
57610276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
57710276Ssam 				reply(221, "You could at least say goodbye.");
57810276Ssam 				exit(0);
57910276Ssam 			}
58011652Ssam 			alarm(0);
58110276Ssam 			if (index(cbuf, '\r')) {
58210276Ssam 				cp = index(cbuf, '\r');
58310276Ssam 				cp[0] = '\n'; cp[1] = 0;
58410276Ssam 			}
58510276Ssam 			if (index(cbuf, ' '))
58610276Ssam 				cpos = index(cbuf, ' ') - cbuf;
58710276Ssam 			else
58810276Ssam 				cpos = 4;
58910276Ssam 			c = cbuf[cpos];
59010276Ssam 			cbuf[cpos] = '\0';
59110276Ssam 			upper(cbuf);
59210276Ssam 			p = lookup(cbuf);
59310276Ssam 			cbuf[cpos] = c;
59410276Ssam 			if (p != 0) {
59510276Ssam 				if (p->implemented == 0) {
59610276Ssam 					nack(p->name);
59710276Ssam 					longjmp(errcatch);
59810276Ssam 					/* NOTREACHED */
59910276Ssam 				}
60010276Ssam 				state = p->state;
60110276Ssam 				yylval = (int) p->name;
60210276Ssam 				return (p->token);
60310276Ssam 			}
60410276Ssam 			break;
60510276Ssam 
60610276Ssam 		case OSTR:
60710276Ssam 			if (cbuf[cpos] == '\n') {
60810276Ssam 				state = CMD;
60910276Ssam 				return (CRLF);
61010276Ssam 			}
61110276Ssam 			/* FALL THRU */
61210276Ssam 
61310276Ssam 		case STR1:
61410276Ssam 			if (cbuf[cpos] == ' ') {
61510276Ssam 				cpos++;
61610276Ssam 				state = STR2;
61710276Ssam 				return (SP);
61810276Ssam 			}
61910276Ssam 			break;
62010276Ssam 
62110276Ssam 		case STR2:
62210276Ssam 			cp = &cbuf[cpos];
62310276Ssam 			n = strlen(cp);
62410276Ssam 			cpos += n - 1;
62510276Ssam 			/*
62610276Ssam 			 * Make sure the string is nonempty and \n terminated.
62710276Ssam 			 */
62810276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
62910276Ssam 				cbuf[cpos] = '\0';
63010276Ssam 				yylval = copy(cp);
63110276Ssam 				cbuf[cpos] = '\n';
63210276Ssam 				state = ARGS;
63310276Ssam 				return (STRING);
63410276Ssam 			}
63510276Ssam 			break;
63610276Ssam 
63710276Ssam 		case ARGS:
63810276Ssam 			if (isdigit(cbuf[cpos])) {
63910276Ssam 				cp = &cbuf[cpos];
64010276Ssam 				while (isdigit(cbuf[++cpos]))
64110276Ssam 					;
64210276Ssam 				c = cbuf[cpos];
64310276Ssam 				cbuf[cpos] = '\0';
64410276Ssam 				yylval = atoi(cp);
64510276Ssam 				cbuf[cpos] = c;
64610276Ssam 				return (NUMBER);
64710276Ssam 			}
64810276Ssam 			switch (cbuf[cpos++]) {
64910276Ssam 
65010276Ssam 			case '\n':
65110276Ssam 				state = CMD;
65210276Ssam 				return (CRLF);
65310276Ssam 
65410276Ssam 			case ' ':
65510276Ssam 				return (SP);
65610276Ssam 
65710276Ssam 			case ',':
65810276Ssam 				return (COMMA);
65910276Ssam 
66010276Ssam 			case 'A':
66110276Ssam 			case 'a':
66210276Ssam 				return (A);
66310276Ssam 
66410276Ssam 			case 'B':
66510276Ssam 			case 'b':
66610276Ssam 				return (B);
66710276Ssam 
66810276Ssam 			case 'C':
66910276Ssam 			case 'c':
67010276Ssam 				return (C);
67110276Ssam 
67210276Ssam 			case 'E':
67310276Ssam 			case 'e':
67410276Ssam 				return (E);
67510276Ssam 
67610276Ssam 			case 'F':
67710276Ssam 			case 'f':
67810276Ssam 				return (F);
67910276Ssam 
68010276Ssam 			case 'I':
68110276Ssam 			case 'i':
68210276Ssam 				return (I);
68310276Ssam 
68410276Ssam 			case 'L':
68510276Ssam 			case 'l':
68610276Ssam 				return (L);
68710276Ssam 
68810276Ssam 			case 'N':
68910276Ssam 			case 'n':
69010276Ssam 				return (N);
69110276Ssam 
69210276Ssam 			case 'P':
69310276Ssam 			case 'p':
69410276Ssam 				return (P);
69510276Ssam 
69610276Ssam 			case 'R':
69710276Ssam 			case 'r':
69810276Ssam 				return (R);
69910276Ssam 
70010276Ssam 			case 'S':
70110276Ssam 			case 's':
70210276Ssam 				return (S);
70310276Ssam 
70410276Ssam 			case 'T':
70510276Ssam 			case 't':
70610276Ssam 				return (T);
70710276Ssam 
70810276Ssam 			}
70910276Ssam 			break;
71010276Ssam 
71110276Ssam 		default:
71210276Ssam 			fatal("Unknown state in scanner.");
71310276Ssam 		}
71410276Ssam 		yyerror();
71510276Ssam 		state = CMD;
71610276Ssam 		longjmp(errcatch);
71710276Ssam 	}
71810276Ssam }
71910276Ssam 
72010276Ssam upper(s)
72110276Ssam 	char *s;
72210276Ssam {
72310276Ssam 	while (*s != '\0') {
72410276Ssam 		if (islower(*s))
72510276Ssam 			*s = toupper(*s);
72610276Ssam 		s++;
72710276Ssam 	}
72810276Ssam }
72910276Ssam 
73010276Ssam copy(s)
73110276Ssam 	char *s;
73210276Ssam {
73310276Ssam 	char *p;
73410276Ssam 	extern char *malloc();
73510276Ssam 
73610276Ssam 	p = malloc(strlen(s) + 1);
73710276Ssam 	if (p == NULL)
73810276Ssam 		fatal("Ran out of memory.");
73910276Ssam 	strcpy(p, s);
74010276Ssam 	return ((int)p);
74110276Ssam }
74210276Ssam 
74310276Ssam help(s)
74410276Ssam 	char *s;
74510276Ssam {
74610276Ssam 	register struct tab *c;
74710276Ssam 	register int width, NCMDS;
74810276Ssam 
74910276Ssam 	width = 0, NCMDS = 0;
75010276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
75110276Ssam 		int len = strlen(c->name);
75210276Ssam 
75310276Ssam 		if (c->implemented == 0)
75410276Ssam 			len++;
75510276Ssam 		if (len > width)
75610276Ssam 			width = len;
75710276Ssam 		NCMDS++;
75810276Ssam 	}
75910276Ssam 	width = (width + 8) &~ 7;
76010276Ssam 	if (s == 0) {
76110276Ssam 		register int i, j, w;
76210276Ssam 		int columns, lines;
76310276Ssam 
76410276Ssam 		lreply(214,
76510276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
76610276Ssam 		columns = 76 / width;
76710276Ssam 		if (columns == 0)
76810276Ssam 			columns = 1;
76910276Ssam 		lines = (NCMDS + columns - 1) / columns;
77010276Ssam 		for (i = 0; i < lines; i++) {
77110276Ssam 			printf("    ");
77210276Ssam 			for (j = 0; j < columns; j++) {
77310276Ssam 				c = cmdtab + j * lines + i;
77410276Ssam 				printf("%s%c", c->name,
77510276Ssam 					c->implemented ? ' ' : '*');
77610302Ssam 				if (c + lines >= &cmdtab[NCMDS])
77710276Ssam 					break;
77810276Ssam 				w = strlen(c->name);
77910276Ssam 				while (w < width) {
78010276Ssam 					putchar(' ');
78110276Ssam 					w++;
78210276Ssam 				}
78310276Ssam 			}
78410276Ssam 			printf("\r\n");
78510276Ssam 		}
78610276Ssam 		fflush(stdout);
78710276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
78810276Ssam 		return;
78910276Ssam 	}
79010276Ssam 	upper(s);
79110276Ssam 	c = lookup(s);
79210276Ssam 	if (c == (struct tab *)0) {
79310276Ssam 		reply(504, "Unknown command %s.", s);
79410276Ssam 		return;
79510276Ssam 	}
79610276Ssam 	if (c->implemented)
79710276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
79810276Ssam 	else
79910276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
80010276Ssam }
801