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