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