xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 10276)
1*10276Ssam /*
2*10276Ssam  * Grammar for FTP commands.
3*10276Ssam  * See RFC 765.
4*10276Ssam  */
5*10276Ssam 
6*10276Ssam %{
7*10276Ssam 
8*10276Ssam #ifndef lint
9*10276Ssam static char sccsid[] = "@(#)ftpcmd.y	4.1 83/01/13";
10*10276Ssam #endif
11*10276Ssam 
12*10276Ssam #include <sys/types.h>
13*10276Ssam #include <sys/socket.h>
14*10276Ssam 
15*10276Ssam #include <netinet/in.h>
16*10276Ssam 
17*10276Ssam #include <stdio.h>
18*10276Ssam #include <ctype.h>
19*10276Ssam #include <pwd.h>
20*10276Ssam #include <setjmp.h>
21*10276Ssam #include "ftp.h"
22*10276Ssam 
23*10276Ssam extern	struct sockaddr_in data_dest;
24*10276Ssam extern	int logged_in;
25*10276Ssam extern	struct passwd *pw;
26*10276Ssam extern	int guest;
27*10276Ssam extern	int logging;
28*10276Ssam extern	int type;
29*10276Ssam extern	int form;
30*10276Ssam extern	int debug;
31*10276Ssam extern	char hostname[];
32*10276Ssam extern	char *globerr;
33*10276Ssam char	**glob();
34*10276Ssam 
35*10276Ssam static	int cmd_type;
36*10276Ssam static	int cmd_form;
37*10276Ssam static	int cmd_bytesz;
38*10276Ssam 
39*10276Ssam static	struct passwd nobody = { "?", "?" };
40*10276Ssam 
41*10276Ssam char	*index();
42*10276Ssam %}
43*10276Ssam 
44*10276Ssam %token
45*10276Ssam 	A	B	C	E	F	I
46*10276Ssam 	L	N	P	R	S	T
47*10276Ssam 
48*10276Ssam 	SP	CRLF	COMMA	STRING	NUMBER
49*10276Ssam 
50*10276Ssam 	USER	PASS	ACCT	REIN	QUIT	PORT
51*10276Ssam 	PASV	TYPE	STRU	MODE	RETR	STOR
52*10276Ssam 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
53*10276Ssam 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
54*10276Ssam 	ABOR	DELE	CWD	LIST	NLST	SITE
55*10276Ssam 	STAT	HELP	NOOP	XMKD	XRMD	XPWD
56*10276Ssam 	XCUP
57*10276Ssam 
58*10276Ssam 	LEXERR
59*10276Ssam 
60*10276Ssam %start	cmd_list
61*10276Ssam 
62*10276Ssam %%
63*10276Ssam 
64*10276Ssam cmd_list:	/* empty */
65*10276Ssam 	|	cmd_list cmd
66*10276Ssam 	;
67*10276Ssam 
68*10276Ssam cmd:		USER SP username CRLF
69*10276Ssam 		= {
70*10276Ssam 			extern struct passwd *getpwnam();
71*10276Ssam 
72*10276Ssam 			if (strcmp($3, "ftp") == 0 ||
73*10276Ssam 			    strcmp($3, "anonymous") == 0) {
74*10276Ssam 				guest = 1;
75*10276Ssam 				pw = getpwnam("ftp");
76*10276Ssam 				reply(331,
77*10276Ssam 				  "Guest login ok, send ident as password.");
78*10276Ssam 			} else {
79*10276Ssam 				guest = 0;
80*10276Ssam 				pw = getpwnam($3);
81*10276Ssam 				reply(331, "Password required for %s.", $3);
82*10276Ssam 			}
83*10276Ssam 			free($3);
84*10276Ssam 			if (pw == NULL)
85*10276Ssam 				pw = &nobody;
86*10276Ssam 		}
87*10276Ssam 	|	PASS SP password CRLF
88*10276Ssam 		= {
89*10276Ssam 			pass($3);
90*10276Ssam 			free($3);
91*10276Ssam 		}
92*10276Ssam 	|	PORT SP host_port CRLF
93*10276Ssam 		= {
94*10276Ssam 			ack($1);
95*10276Ssam 		}
96*10276Ssam 	|	TYPE SP type_code CRLF
97*10276Ssam 		= {
98*10276Ssam 			switch (cmd_type) {
99*10276Ssam 
100*10276Ssam 			case TYPE_A:
101*10276Ssam 				if (cmd_form == FORM_N) {
102*10276Ssam 					reply(200, "Type set to A.");
103*10276Ssam 					type = cmd_type;
104*10276Ssam 					form = cmd_form;
105*10276Ssam 				} else
106*10276Ssam 					reply(504, "Form must be N.");
107*10276Ssam 				break;
108*10276Ssam 
109*10276Ssam 			case TYPE_E:
110*10276Ssam 				reply(504, "Type E not implemented.");
111*10276Ssam 				break;
112*10276Ssam 
113*10276Ssam 			case TYPE_I:
114*10276Ssam 				reply(200, "Type set to I.");
115*10276Ssam 				type = cmd_type;
116*10276Ssam 				break;
117*10276Ssam 
118*10276Ssam 			case TYPE_L:
119*10276Ssam 				if (cmd_bytesz == 8) {
120*10276Ssam 					reply(200,
121*10276Ssam 					    "Type set to L (byte size 8).");
122*10276Ssam 					type = cmd_type;
123*10276Ssam 				} else
124*10276Ssam 					reply(504, "Byte size must be 8.");
125*10276Ssam 			}
126*10276Ssam 		}
127*10276Ssam 	|	STRU SP struct_code CRLF
128*10276Ssam 		= {
129*10276Ssam 			switch ($3) {
130*10276Ssam 
131*10276Ssam 			case STRU_F:
132*10276Ssam 				reply(200, "STRU F ok.");
133*10276Ssam 				break;
134*10276Ssam 
135*10276Ssam 			default:
136*10276Ssam 				reply(502, "Unimplemented STRU type.");
137*10276Ssam 			}
138*10276Ssam 		}
139*10276Ssam 	|	MODE SP mode_code CRLF
140*10276Ssam 		= {
141*10276Ssam 			switch ($3) {
142*10276Ssam 
143*10276Ssam 			case MODE_S:
144*10276Ssam 				reply(200, "MODE S ok.");
145*10276Ssam 				break;
146*10276Ssam 
147*10276Ssam 			default:
148*10276Ssam 				reply(502, "Unimplemented MODE type.");
149*10276Ssam 			}
150*10276Ssam 		}
151*10276Ssam 	|	ALLO SP NUMBER CRLF
152*10276Ssam 		= {
153*10276Ssam 			ack($1);
154*10276Ssam 		}
155*10276Ssam 	|	RETR check_login SP pathname CRLF
156*10276Ssam 		= {
157*10276Ssam 			if ($2)
158*10276Ssam 				retrieve(0, $4);
159*10276Ssam 			free($4);
160*10276Ssam 		}
161*10276Ssam 	|	STOR check_login SP pathname CRLF
162*10276Ssam 		= {
163*10276Ssam 			if ($2)
164*10276Ssam 				store($4, "w");
165*10276Ssam 			free($4);
166*10276Ssam 		}
167*10276Ssam 	|	APPE check_login SP pathname CRLF
168*10276Ssam 		= {
169*10276Ssam 			if ($2)
170*10276Ssam 				store($4, "a");
171*10276Ssam 			free($4);
172*10276Ssam 		}
173*10276Ssam 	|	NLST check_login CRLF
174*10276Ssam 		= {
175*10276Ssam 			if ($2)
176*10276Ssam 				retrieve("ls", "");
177*10276Ssam 		}
178*10276Ssam 	|	NLST check_login SP pathname CRLF
179*10276Ssam 		= {
180*10276Ssam 			if ($2)
181*10276Ssam 				retrieve("ls %s", $4);
182*10276Ssam 			free($4);
183*10276Ssam 		}
184*10276Ssam 	|	LIST check_login CRLF
185*10276Ssam 		= {
186*10276Ssam 			if ($2)
187*10276Ssam 				retrieve("ls -l", "");
188*10276Ssam 		}
189*10276Ssam 	|	LIST check_login SP pathname CRLF
190*10276Ssam 		= {
191*10276Ssam 			if ($2)
192*10276Ssam 				retrieve("ls -l %s", $4);
193*10276Ssam 			free($4);
194*10276Ssam 		}
195*10276Ssam 	|	DELE check_login SP pathname CRLF
196*10276Ssam 		= {
197*10276Ssam 			if ($2)
198*10276Ssam 				delete($4);
199*10276Ssam 			free($4);
200*10276Ssam 		}
201*10276Ssam 	|	CWD check_login CRLF
202*10276Ssam 		= {
203*10276Ssam 			if ($2)
204*10276Ssam 				cwd(pw->pw_dir);
205*10276Ssam 		}
206*10276Ssam 	|	CWD check_login SP pathname CRLF
207*10276Ssam 		= {
208*10276Ssam 			if ($2)
209*10276Ssam 				cwd($4);
210*10276Ssam 			free($4);
211*10276Ssam 		}
212*10276Ssam 	|	rename_cmd
213*10276Ssam 	|	HELP CRLF
214*10276Ssam 		= {
215*10276Ssam 			help(0);
216*10276Ssam 		}
217*10276Ssam 	|	HELP SP STRING CRLF
218*10276Ssam 		= {
219*10276Ssam 			help($3);
220*10276Ssam 		}
221*10276Ssam 	|	NOOP CRLF
222*10276Ssam 		= {
223*10276Ssam 			ack($1);
224*10276Ssam 		}
225*10276Ssam 	|	XMKD check_login SP pathname CRLF
226*10276Ssam 		= {
227*10276Ssam 			if ($2)
228*10276Ssam 				do_mkdir($4);
229*10276Ssam 			free($4);
230*10276Ssam 		}
231*10276Ssam 	|	XRMD check_login SP pathname CRLF
232*10276Ssam 		= {
233*10276Ssam 			if ($2)
234*10276Ssam 				do_rmdir($4);
235*10276Ssam 			free($4);
236*10276Ssam 		}
237*10276Ssam 	|	XPWD check_login CRLF
238*10276Ssam 		= {
239*10276Ssam 			if ($2)
240*10276Ssam 				do_pwd();
241*10276Ssam 		}
242*10276Ssam 	|	XCUP check_login CRLF
243*10276Ssam 		= {
244*10276Ssam 			if ($2)
245*10276Ssam 				cwd("..");
246*10276Ssam 		}
247*10276Ssam 	|	QUIT CRLF
248*10276Ssam 		= {
249*10276Ssam 			reply(221, "Goodbye.");
250*10276Ssam 			exit(0);
251*10276Ssam 		}
252*10276Ssam 	|	error CRLF
253*10276Ssam 		= {
254*10276Ssam 			yyerrok;
255*10276Ssam 		}
256*10276Ssam 	;
257*10276Ssam 
258*10276Ssam username:	STRING
259*10276Ssam 	;
260*10276Ssam 
261*10276Ssam password:	STRING
262*10276Ssam 	;
263*10276Ssam 
264*10276Ssam byte_size:	NUMBER
265*10276Ssam 	;
266*10276Ssam 
267*10276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
268*10276Ssam 		NUMBER COMMA NUMBER
269*10276Ssam 		= {
270*10276Ssam 			register char *a, *p;
271*10276Ssam 
272*10276Ssam 			a = (char *)&data_dest.sin_addr;
273*10276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
274*10276Ssam 			p = (char *)&data_dest.sin_port;
275*10276Ssam 			p[0] = $9; p[1] = $11;
276*10276Ssam 		}
277*10276Ssam 	;
278*10276Ssam 
279*10276Ssam form_code:	N
280*10276Ssam 	= {
281*10276Ssam 		$$ = FORM_N;
282*10276Ssam 	}
283*10276Ssam 	|	T
284*10276Ssam 	= {
285*10276Ssam 		$$ = FORM_T;
286*10276Ssam 	}
287*10276Ssam 	|	C
288*10276Ssam 	= {
289*10276Ssam 		$$ = FORM_C;
290*10276Ssam 	}
291*10276Ssam 	;
292*10276Ssam 
293*10276Ssam type_code:	A
294*10276Ssam 	= {
295*10276Ssam 		cmd_type = TYPE_A;
296*10276Ssam 		cmd_form = FORM_N;
297*10276Ssam 	}
298*10276Ssam 	|	A SP form_code
299*10276Ssam 	= {
300*10276Ssam 		cmd_type = TYPE_A;
301*10276Ssam 		cmd_form = $3;
302*10276Ssam 	}
303*10276Ssam 	|	E
304*10276Ssam 	= {
305*10276Ssam 		cmd_type = TYPE_E;
306*10276Ssam 		cmd_form = FORM_N;
307*10276Ssam 	}
308*10276Ssam 	|	E SP form_code
309*10276Ssam 	= {
310*10276Ssam 		cmd_type = TYPE_E;
311*10276Ssam 		cmd_form = $3;
312*10276Ssam 	}
313*10276Ssam 	|	I
314*10276Ssam 	= {
315*10276Ssam 		cmd_type = TYPE_I;
316*10276Ssam 	}
317*10276Ssam 	|	L
318*10276Ssam 	= {
319*10276Ssam 		cmd_type = TYPE_L;
320*10276Ssam 		cmd_bytesz = 8;
321*10276Ssam 	}
322*10276Ssam 	|	L SP byte_size
323*10276Ssam 	= {
324*10276Ssam 		cmd_type = TYPE_L;
325*10276Ssam 		cmd_bytesz = $3;
326*10276Ssam 	}
327*10276Ssam 	/* this is for a bug in the BBN ftp */
328*10276Ssam 	|	L byte_size
329*10276Ssam 	= {
330*10276Ssam 		cmd_type = TYPE_L;
331*10276Ssam 		cmd_bytesz = $2;
332*10276Ssam 	}
333*10276Ssam 	;
334*10276Ssam 
335*10276Ssam struct_code:	F
336*10276Ssam 	= {
337*10276Ssam 		$$ = STRU_F;
338*10276Ssam 	}
339*10276Ssam 	|	R
340*10276Ssam 	= {
341*10276Ssam 		$$ = STRU_R;
342*10276Ssam 	}
343*10276Ssam 	|	P
344*10276Ssam 	= {
345*10276Ssam 		$$ = STRU_P;
346*10276Ssam 	}
347*10276Ssam 	;
348*10276Ssam 
349*10276Ssam mode_code:	S
350*10276Ssam 	= {
351*10276Ssam 		$$ = MODE_S;
352*10276Ssam 	}
353*10276Ssam 	|	B
354*10276Ssam 	= {
355*10276Ssam 		$$ = MODE_B;
356*10276Ssam 	}
357*10276Ssam 	|	C
358*10276Ssam 	= {
359*10276Ssam 		$$ = MODE_C;
360*10276Ssam 	}
361*10276Ssam 	;
362*10276Ssam 
363*10276Ssam pathname:	pathstring
364*10276Ssam 	= {
365*10276Ssam 		if ($1 && strncmp($1, "~", 1) == 0) {
366*10276Ssam 			$$ = (int)*glob($1);
367*10276Ssam 			if (globerr != NULL)
368*10276Ssam 				reply(550, globerr);
369*10276Ssam 			free($1);
370*10276Ssam 		} else
371*10276Ssam 			$$ = $1;
372*10276Ssam 	}
373*10276Ssam 	;
374*10276Ssam 
375*10276Ssam pathstring:	STRING
376*10276Ssam 	;
377*10276Ssam 
378*10276Ssam rename_cmd:	rename_from rename_to
379*10276Ssam 	= {
380*10276Ssam 		if ($1 && $2)
381*10276Ssam 			renamecmd($1, $2);
382*10276Ssam 		else
383*10276Ssam 			reply(503, "Bad sequence of commands.");
384*10276Ssam 		if ($1)
385*10276Ssam 			free($1);
386*10276Ssam 		if ($2)
387*10276Ssam 			free($2);
388*10276Ssam 	}
389*10276Ssam 	;
390*10276Ssam 
391*10276Ssam rename_from:	RNFR check_login SP pathname CRLF
392*10276Ssam 	= {
393*10276Ssam 		char *from = 0, *renamefrom();
394*10276Ssam 
395*10276Ssam 		if ($2)
396*10276Ssam 			from = renamefrom($4);
397*10276Ssam 		if (from == 0)
398*10276Ssam 			free($4);
399*10276Ssam 		$$ = (int)from;
400*10276Ssam 	}
401*10276Ssam 	;
402*10276Ssam 
403*10276Ssam rename_to:	RNTO SP pathname CRLF
404*10276Ssam 	= {
405*10276Ssam 		$$ = $3;
406*10276Ssam 	}
407*10276Ssam 	;
408*10276Ssam 
409*10276Ssam check_login:	/* empty */
410*10276Ssam 	= {
411*10276Ssam 		if (logged_in)
412*10276Ssam 			$$ = 1;
413*10276Ssam 		else {
414*10276Ssam 			reply(530, "Please login with USER and PASS.");
415*10276Ssam 			$$ = 0;
416*10276Ssam 		}
417*10276Ssam 	}
418*10276Ssam 	;
419*10276Ssam 
420*10276Ssam %%
421*10276Ssam 
422*10276Ssam extern jmp_buf errcatch;
423*10276Ssam 
424*10276Ssam #define	CMD	0	/* beginning of command */
425*10276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
426*10276Ssam #define	STR1	2	/* expect SP followed by STRING */
427*10276Ssam #define	STR2	3	/* expect STRING */
428*10276Ssam #define	OSTR	4	/* optional STRING */
429*10276Ssam 
430*10276Ssam struct tab {
431*10276Ssam 	char	*name;
432*10276Ssam 	short	token;
433*10276Ssam 	short	state;
434*10276Ssam 	short	implemented;	/* 1 if command is implemented */
435*10276Ssam 	char	*help;
436*10276Ssam };
437*10276Ssam 
438*10276Ssam struct tab cmdtab[] = {		/* In order defined in RFC 765 */
439*10276Ssam 	{ "USER", USER, STR1, 1,	"<sp> username" },
440*10276Ssam 	{ "PASS", PASS, STR1, 1,	"<sp> password" },
441*10276Ssam 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
442*10276Ssam 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
443*10276Ssam 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
444*10276Ssam 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
445*10276Ssam 	{ "PASV", PASV, ARGS, 0,	"(set server in passive mode)" },
446*10276Ssam 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
447*10276Ssam 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
448*10276Ssam 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
449*10276Ssam 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
450*10276Ssam 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
451*10276Ssam 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
452*10276Ssam 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
453*10276Ssam 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
454*10276Ssam 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
455*10276Ssam 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
456*10276Ssam 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
457*10276Ssam 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
458*10276Ssam 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
459*10276Ssam 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
460*10276Ssam 	{ "REST", REST, STR1, 0,	"(restart command)" },
461*10276Ssam 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
462*10276Ssam 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
463*10276Ssam 	{ "ABOR", ABOR, ARGS, 0,	"(abort operation)" },
464*10276Ssam 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
465*10276Ssam 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name]" },
466*10276Ssam 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
467*10276Ssam 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
468*10276Ssam 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
469*10276Ssam 	{ "SITE", SITE, STR1, 0,	"(get site parameters)" },
470*10276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
471*10276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
472*10276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
473*10276Ssam 	{ "XMKD", XMKD, STR1, 1,	"<sp> path-name" },
474*10276Ssam 	{ "XRMD", XRMD, STR1, 1,	"<sp> path-name" },
475*10276Ssam 	{ "XPWD", XPWD, ARGS, 1,	"(return current directory)" },
476*10276Ssam 	{ "XCUP", XCUP, ARGS, 1,	"(change to parent directory)" },
477*10276Ssam 	{ NULL,   0,    0,    0,	0 }
478*10276Ssam };
479*10276Ssam 
480*10276Ssam struct tab *
481*10276Ssam lookup(cmd)
482*10276Ssam 	char *cmd;
483*10276Ssam {
484*10276Ssam 	register struct tab *p;
485*10276Ssam 
486*10276Ssam 	for (p = cmdtab; p->name != NULL; p++)
487*10276Ssam 		if (strcmp(cmd, p->name) == 0)
488*10276Ssam 			return (p);
489*10276Ssam 	return (0);
490*10276Ssam }
491*10276Ssam 
492*10276Ssam #include "../telnet/telnet.h"
493*10276Ssam 
494*10276Ssam /*
495*10276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
496*10276Ssam  */
497*10276Ssam char *
498*10276Ssam getline(s, n, iop)
499*10276Ssam 	char *s;
500*10276Ssam 	register FILE *iop;
501*10276Ssam {
502*10276Ssam 	register c;
503*10276Ssam 	register char *cs;
504*10276Ssam 
505*10276Ssam 	cs = s;
506*10276Ssam 	while (--n > 0 && (c = getc(iop)) >= 0) {
507*10276Ssam 		while (c == IAC) {
508*10276Ssam 			c = getc(iop);	/* skip command */
509*10276Ssam 			c = getc(iop);	/* try next char */
510*10276Ssam 		}
511*10276Ssam 		*cs++ = c;
512*10276Ssam 		if (c=='\n')
513*10276Ssam 			break;
514*10276Ssam 	}
515*10276Ssam 	if (c < 0 && cs == s)
516*10276Ssam 		return (NULL);
517*10276Ssam 	*cs++ = '\0';
518*10276Ssam 	fprintf(stderr, "FTPD: command: '%s'\n", s);
519*10276Ssam 	fflush(stderr);
520*10276Ssam 	return (s);
521*10276Ssam }
522*10276Ssam 
523*10276Ssam yylex()
524*10276Ssam {
525*10276Ssam 	static char cbuf[512];
526*10276Ssam 	static int cpos, state;
527*10276Ssam 	register char *cp;
528*10276Ssam 	register struct tab *p;
529*10276Ssam 	int n;
530*10276Ssam 	char c;
531*10276Ssam 
532*10276Ssam 	for (;;) {
533*10276Ssam 		switch (state) {
534*10276Ssam 
535*10276Ssam 		case CMD:
536*10276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
537*10276Ssam 				reply(221, "You could at least say goodbye.");
538*10276Ssam 				exit(0);
539*10276Ssam 			}
540*10276Ssam 			if (index(cbuf, '\r')) {
541*10276Ssam 				cp = index(cbuf, '\r');
542*10276Ssam 				cp[0] = '\n'; cp[1] = 0;
543*10276Ssam 			}
544*10276Ssam 			if (index(cbuf, ' '))
545*10276Ssam 				cpos = index(cbuf, ' ') - cbuf;
546*10276Ssam 			else
547*10276Ssam 				cpos = 4;
548*10276Ssam 			c = cbuf[cpos];
549*10276Ssam 			cbuf[cpos] = '\0';
550*10276Ssam 			upper(cbuf);
551*10276Ssam 			p = lookup(cbuf);
552*10276Ssam 			cbuf[cpos] = c;
553*10276Ssam 			if (p != 0) {
554*10276Ssam 				if (p->implemented == 0) {
555*10276Ssam 					nack(p->name);
556*10276Ssam 					longjmp(errcatch);
557*10276Ssam 					/* NOTREACHED */
558*10276Ssam 				}
559*10276Ssam 				state = p->state;
560*10276Ssam 				yylval = (int) p->name;
561*10276Ssam 				return (p->token);
562*10276Ssam 			}
563*10276Ssam 			break;
564*10276Ssam 
565*10276Ssam 		case OSTR:
566*10276Ssam 			if (cbuf[cpos] == '\n') {
567*10276Ssam 				state = CMD;
568*10276Ssam 				return (CRLF);
569*10276Ssam 			}
570*10276Ssam 			/* FALL THRU */
571*10276Ssam 
572*10276Ssam 		case STR1:
573*10276Ssam 			if (cbuf[cpos] == ' ') {
574*10276Ssam 				cpos++;
575*10276Ssam 				state = STR2;
576*10276Ssam 				return (SP);
577*10276Ssam 			}
578*10276Ssam 			break;
579*10276Ssam 
580*10276Ssam 		case STR2:
581*10276Ssam 			cp = &cbuf[cpos];
582*10276Ssam 			n = strlen(cp);
583*10276Ssam 			cpos += n - 1;
584*10276Ssam 			/*
585*10276Ssam 			 * Make sure the string is nonempty and \n terminated.
586*10276Ssam 			 */
587*10276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
588*10276Ssam 				cbuf[cpos] = '\0';
589*10276Ssam 				yylval = copy(cp);
590*10276Ssam 				cbuf[cpos] = '\n';
591*10276Ssam 				state = ARGS;
592*10276Ssam 				return (STRING);
593*10276Ssam 			}
594*10276Ssam 			break;
595*10276Ssam 
596*10276Ssam 		case ARGS:
597*10276Ssam 			if (isdigit(cbuf[cpos])) {
598*10276Ssam 				cp = &cbuf[cpos];
599*10276Ssam 				while (isdigit(cbuf[++cpos]))
600*10276Ssam 					;
601*10276Ssam 				c = cbuf[cpos];
602*10276Ssam 				cbuf[cpos] = '\0';
603*10276Ssam 				yylval = atoi(cp);
604*10276Ssam 				cbuf[cpos] = c;
605*10276Ssam 				return (NUMBER);
606*10276Ssam 			}
607*10276Ssam 			switch (cbuf[cpos++]) {
608*10276Ssam 
609*10276Ssam 			case '\n':
610*10276Ssam 				state = CMD;
611*10276Ssam 				return (CRLF);
612*10276Ssam 
613*10276Ssam 			case ' ':
614*10276Ssam 				return (SP);
615*10276Ssam 
616*10276Ssam 			case ',':
617*10276Ssam 				return (COMMA);
618*10276Ssam 
619*10276Ssam 			case 'A':
620*10276Ssam 			case 'a':
621*10276Ssam 				return (A);
622*10276Ssam 
623*10276Ssam 			case 'B':
624*10276Ssam 			case 'b':
625*10276Ssam 				return (B);
626*10276Ssam 
627*10276Ssam 			case 'C':
628*10276Ssam 			case 'c':
629*10276Ssam 				return (C);
630*10276Ssam 
631*10276Ssam 			case 'E':
632*10276Ssam 			case 'e':
633*10276Ssam 				return (E);
634*10276Ssam 
635*10276Ssam 			case 'F':
636*10276Ssam 			case 'f':
637*10276Ssam 				return (F);
638*10276Ssam 
639*10276Ssam 			case 'I':
640*10276Ssam 			case 'i':
641*10276Ssam 				return (I);
642*10276Ssam 
643*10276Ssam 			case 'L':
644*10276Ssam 			case 'l':
645*10276Ssam 				return (L);
646*10276Ssam 
647*10276Ssam 			case 'N':
648*10276Ssam 			case 'n':
649*10276Ssam 				return (N);
650*10276Ssam 
651*10276Ssam 			case 'P':
652*10276Ssam 			case 'p':
653*10276Ssam 				return (P);
654*10276Ssam 
655*10276Ssam 			case 'R':
656*10276Ssam 			case 'r':
657*10276Ssam 				return (R);
658*10276Ssam 
659*10276Ssam 			case 'S':
660*10276Ssam 			case 's':
661*10276Ssam 				return (S);
662*10276Ssam 
663*10276Ssam 			case 'T':
664*10276Ssam 			case 't':
665*10276Ssam 				return (T);
666*10276Ssam 
667*10276Ssam 			}
668*10276Ssam 			break;
669*10276Ssam 
670*10276Ssam 		default:
671*10276Ssam 			fatal("Unknown state in scanner.");
672*10276Ssam 		}
673*10276Ssam 		yyerror();
674*10276Ssam 		state = CMD;
675*10276Ssam 		longjmp(errcatch);
676*10276Ssam 	}
677*10276Ssam }
678*10276Ssam 
679*10276Ssam upper(s)
680*10276Ssam 	char *s;
681*10276Ssam {
682*10276Ssam 	while (*s != '\0') {
683*10276Ssam 		if (islower(*s))
684*10276Ssam 			*s = toupper(*s);
685*10276Ssam 		s++;
686*10276Ssam 	}
687*10276Ssam }
688*10276Ssam 
689*10276Ssam copy(s)
690*10276Ssam 	char *s;
691*10276Ssam {
692*10276Ssam 	char *p;
693*10276Ssam 	extern char *malloc();
694*10276Ssam 
695*10276Ssam 	p = malloc(strlen(s) + 1);
696*10276Ssam 	if (p == NULL)
697*10276Ssam 		fatal("Ran out of memory.");
698*10276Ssam 	strcpy(p, s);
699*10276Ssam 	return ((int)p);
700*10276Ssam }
701*10276Ssam 
702*10276Ssam help(s)
703*10276Ssam 	char *s;
704*10276Ssam {
705*10276Ssam 	register struct tab *c;
706*10276Ssam 	register int width, NCMDS;
707*10276Ssam 
708*10276Ssam 	width = 0, NCMDS = 0;
709*10276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
710*10276Ssam 		int len = strlen(c->name);
711*10276Ssam 
712*10276Ssam 		if (c->implemented == 0)
713*10276Ssam 			len++;
714*10276Ssam 		if (len > width)
715*10276Ssam 			width = len;
716*10276Ssam 		NCMDS++;
717*10276Ssam 	}
718*10276Ssam 	width = (width + 8) &~ 7;
719*10276Ssam 	if (s == 0) {
720*10276Ssam 		register int i, j, w;
721*10276Ssam 		int columns, lines;
722*10276Ssam 
723*10276Ssam 		lreply(214,
724*10276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
725*10276Ssam 		columns = 76 / width;
726*10276Ssam 		if (columns == 0)
727*10276Ssam 			columns = 1;
728*10276Ssam 		lines = (NCMDS + columns - 1) / columns;
729*10276Ssam 		for (i = 0; i < lines; i++) {
730*10276Ssam 			printf("    ");
731*10276Ssam 			for (j = 0; j < columns; j++) {
732*10276Ssam 				c = cmdtab + j * lines + i;
733*10276Ssam 				printf("%s%c", c->name,
734*10276Ssam 					c->implemented ? ' ' : '*');
735*10276Ssam 				if (c + lines >= &cmdtab[NCMDS]) {
736*10276Ssam 					printf("\r\n");
737*10276Ssam 					break;
738*10276Ssam 				}
739*10276Ssam 				w = strlen(c->name);
740*10276Ssam 				while (w < width) {
741*10276Ssam 					putchar(' ');
742*10276Ssam 					w++;
743*10276Ssam 				}
744*10276Ssam 			}
745*10276Ssam 			printf("\r\n");
746*10276Ssam 		}
747*10276Ssam 		fflush(stdout);
748*10276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
749*10276Ssam 		return;
750*10276Ssam 	}
751*10276Ssam 	upper(s);
752*10276Ssam 	c = lookup(s);
753*10276Ssam 	if (c == (struct tab *)0) {
754*10276Ssam 		reply(504, "Unknown command %s.", s);
755*10276Ssam 		return;
756*10276Ssam 	}
757*10276Ssam 	if (c->implemented)
758*10276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
759*10276Ssam 	else
760*10276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
761*10276Ssam }
762