xref: /csrg-svn/usr.bin/yacc/test/ftp.y (revision 40083)
1*40083Sbostic /*
2*40083Sbostic  * Copyright (c) 1985, 1988 Regents of the University of California.
3*40083Sbostic  * All rights reserved.
4*40083Sbostic  *
5*40083Sbostic  * Redistribution and use in source and binary forms are permitted
6*40083Sbostic  * provided that the above copyright notice and this paragraph are
7*40083Sbostic  * duplicated in all such forms and that any documentation,
8*40083Sbostic  * advertising materials, and other materials related to such
9*40083Sbostic  * distribution and use acknowledge that the software was developed
10*40083Sbostic  * by the University of California, Berkeley.  The name of the
11*40083Sbostic  * University may not be used to endorse or promote products derived
12*40083Sbostic  * from this software without specific prior written permission.
13*40083Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*40083Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*40083Sbostic  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16*40083Sbostic  *
17*40083Sbostic  *	@(#)ftpcmd.y	5.20.1.1 (Berkeley) 3/2/89
18*40083Sbostic  */
19*40083Sbostic 
20*40083Sbostic /*
21*40083Sbostic  * Grammar for FTP commands.
22*40083Sbostic  * See RFC 959.
23*40083Sbostic  */
24*40083Sbostic 
25*40083Sbostic %{
26*40083Sbostic 
27*40083Sbostic #ifndef lint
28*40083Sbostic static char sccsid[] = "@(#)ftpcmd.y	5.20.1.1 (Berkeley) 3/2/89";
29*40083Sbostic #endif /* not lint */
30*40083Sbostic 
31*40083Sbostic #include <sys/param.h>
32*40083Sbostic #include <sys/socket.h>
33*40083Sbostic 
34*40083Sbostic #include <netinet/in.h>
35*40083Sbostic 
36*40083Sbostic #include <arpa/ftp.h>
37*40083Sbostic 
38*40083Sbostic #include <stdio.h>
39*40083Sbostic #include <signal.h>
40*40083Sbostic #include <ctype.h>
41*40083Sbostic #include <pwd.h>
42*40083Sbostic #include <setjmp.h>
43*40083Sbostic #include <syslog.h>
44*40083Sbostic #include <sys/stat.h>
45*40083Sbostic #include <time.h>
46*40083Sbostic 
47*40083Sbostic extern	struct sockaddr_in data_dest;
48*40083Sbostic extern	int logged_in;
49*40083Sbostic extern	struct passwd *pw;
50*40083Sbostic extern	int guest;
51*40083Sbostic extern	int logging;
52*40083Sbostic extern	int type;
53*40083Sbostic extern	int form;
54*40083Sbostic extern	int debug;
55*40083Sbostic extern	int timeout;
56*40083Sbostic extern	int maxtimeout;
57*40083Sbostic extern  int pdata;
58*40083Sbostic extern	char hostname[], remotehost[];
59*40083Sbostic extern	char proctitle[];
60*40083Sbostic extern	char *globerr;
61*40083Sbostic extern	int usedefault;
62*40083Sbostic extern  int transflag;
63*40083Sbostic extern  char tmpline[];
64*40083Sbostic char	**glob();
65*40083Sbostic 
66*40083Sbostic static	int cmd_type;
67*40083Sbostic static	int cmd_form;
68*40083Sbostic static	int cmd_bytesz;
69*40083Sbostic char	cbuf[512];
70*40083Sbostic char	*fromname;
71*40083Sbostic 
72*40083Sbostic char	*index();
73*40083Sbostic %}
74*40083Sbostic 
75*40083Sbostic %token
76*40083Sbostic 	A	B	C	E	F	I
77*40083Sbostic 	L	N	P	R	S	T
78*40083Sbostic 
79*40083Sbostic 	SP	CRLF	COMMA	STRING	NUMBER
80*40083Sbostic 
81*40083Sbostic 	USER	PASS	ACCT	REIN	QUIT	PORT
82*40083Sbostic 	PASV	TYPE	STRU	MODE	RETR	STOR
83*40083Sbostic 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
84*40083Sbostic 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
85*40083Sbostic 	ABOR	DELE	CWD	LIST	NLST	SITE
86*40083Sbostic 	STAT	HELP	NOOP	MKD	RMD	PWD
87*40083Sbostic 	CDUP	STOU	SMNT	SYST	SIZE	MDTM
88*40083Sbostic 
89*40083Sbostic 	UMASK	IDLE	CHMOD
90*40083Sbostic 
91*40083Sbostic 	LEXERR
92*40083Sbostic 
93*40083Sbostic %start	cmd_list
94*40083Sbostic 
95*40083Sbostic %%
96*40083Sbostic 
97*40083Sbostic cmd_list:	/* empty */
98*40083Sbostic 	|	cmd_list cmd
99*40083Sbostic 		= {
100*40083Sbostic 			fromname = (char *) 0;
101*40083Sbostic 		}
102*40083Sbostic 	|	cmd_list rcmd
103*40083Sbostic 	;
104*40083Sbostic 
105*40083Sbostic cmd:		USER SP username CRLF
106*40083Sbostic 		= {
107*40083Sbostic 			user((char *) $3);
108*40083Sbostic 			free((char *) $3);
109*40083Sbostic 		}
110*40083Sbostic 	|	PASS SP password CRLF
111*40083Sbostic 		= {
112*40083Sbostic 			pass((char *) $3);
113*40083Sbostic 			free((char *) $3);
114*40083Sbostic 		}
115*40083Sbostic 	|	PORT SP host_port CRLF
116*40083Sbostic 		= {
117*40083Sbostic 			usedefault = 0;
118*40083Sbostic 			if (pdata >= 0) {
119*40083Sbostic 				(void) close(pdata);
120*40083Sbostic 				pdata = -1;
121*40083Sbostic 			}
122*40083Sbostic 			reply(200, "PORT command successful.");
123*40083Sbostic 		}
124*40083Sbostic 	|	PASV CRLF
125*40083Sbostic 		= {
126*40083Sbostic 			passive();
127*40083Sbostic 		}
128*40083Sbostic 	|	TYPE SP type_code CRLF
129*40083Sbostic 		= {
130*40083Sbostic 			switch (cmd_type) {
131*40083Sbostic 
132*40083Sbostic 			case TYPE_A:
133*40083Sbostic 				if (cmd_form == FORM_N) {
134*40083Sbostic 					reply(200, "Type set to A.");
135*40083Sbostic 					type = cmd_type;
136*40083Sbostic 					form = cmd_form;
137*40083Sbostic 				} else
138*40083Sbostic 					reply(504, "Form must be N.");
139*40083Sbostic 				break;
140*40083Sbostic 
141*40083Sbostic 			case TYPE_E:
142*40083Sbostic 				reply(504, "Type E not implemented.");
143*40083Sbostic 				break;
144*40083Sbostic 
145*40083Sbostic 			case TYPE_I:
146*40083Sbostic 				reply(200, "Type set to I.");
147*40083Sbostic 				type = cmd_type;
148*40083Sbostic 				break;
149*40083Sbostic 
150*40083Sbostic 			case TYPE_L:
151*40083Sbostic #if NBBY == 8
152*40083Sbostic 				if (cmd_bytesz == 8) {
153*40083Sbostic 					reply(200,
154*40083Sbostic 					    "Type set to L (byte size 8).");
155*40083Sbostic 					type = cmd_type;
156*40083Sbostic 				} else
157*40083Sbostic 					reply(504, "Byte size must be 8.");
158*40083Sbostic #else /* NBBY == 8 */
159*40083Sbostic 				UNIMPLEMENTED for NBBY != 8
160*40083Sbostic #endif /* NBBY == 8 */
161*40083Sbostic 			}
162*40083Sbostic 		}
163*40083Sbostic 	|	STRU SP struct_code CRLF
164*40083Sbostic 		= {
165*40083Sbostic 			switch ($3) {
166*40083Sbostic 
167*40083Sbostic 			case STRU_F:
168*40083Sbostic 				reply(200, "STRU F ok.");
169*40083Sbostic 				break;
170*40083Sbostic 
171*40083Sbostic 			default:
172*40083Sbostic 				reply(504, "Unimplemented STRU type.");
173*40083Sbostic 			}
174*40083Sbostic 		}
175*40083Sbostic 	|	MODE SP mode_code CRLF
176*40083Sbostic 		= {
177*40083Sbostic 			switch ($3) {
178*40083Sbostic 
179*40083Sbostic 			case MODE_S:
180*40083Sbostic 				reply(200, "MODE S ok.");
181*40083Sbostic 				break;
182*40083Sbostic 
183*40083Sbostic 			default:
184*40083Sbostic 				reply(502, "Unimplemented MODE type.");
185*40083Sbostic 			}
186*40083Sbostic 		}
187*40083Sbostic 	|	ALLO SP NUMBER CRLF
188*40083Sbostic 		= {
189*40083Sbostic 			reply(202, "ALLO command ignored.");
190*40083Sbostic 		}
191*40083Sbostic 	|	ALLO SP NUMBER SP R SP NUMBER CRLF
192*40083Sbostic 		= {
193*40083Sbostic 			reply(202, "ALLO command ignored.");
194*40083Sbostic 		}
195*40083Sbostic 	|	RETR check_login SP pathname CRLF
196*40083Sbostic 		= {
197*40083Sbostic 			if ($2 && $4 != NULL)
198*40083Sbostic 				retrieve((char *) 0, (char *) $4);
199*40083Sbostic 			if ($4 != NULL)
200*40083Sbostic 				free((char *) $4);
201*40083Sbostic 		}
202*40083Sbostic 	|	STOR check_login SP pathname CRLF
203*40083Sbostic 		= {
204*40083Sbostic 			if ($2 && $4 != NULL)
205*40083Sbostic 				store((char *) $4, "w", 0);
206*40083Sbostic 			if ($4 != NULL)
207*40083Sbostic 				free((char *) $4);
208*40083Sbostic 		}
209*40083Sbostic 	|	APPE check_login SP pathname CRLF
210*40083Sbostic 		= {
211*40083Sbostic 			if ($2 && $4 != NULL)
212*40083Sbostic 				store((char *) $4, "a", 0);
213*40083Sbostic 			if ($4 != NULL)
214*40083Sbostic 				free((char *) $4);
215*40083Sbostic 		}
216*40083Sbostic 	|	NLST check_login CRLF
217*40083Sbostic 		= {
218*40083Sbostic 			if ($2)
219*40083Sbostic 				send_file_list(".");
220*40083Sbostic 		}
221*40083Sbostic 	|	NLST check_login SP STRING CRLF
222*40083Sbostic 		= {
223*40083Sbostic 			if ($2 && $4 != NULL)
224*40083Sbostic 				send_file_list((char *) $4);
225*40083Sbostic 			if ($4 != NULL)
226*40083Sbostic 				free((char *) $4);
227*40083Sbostic 		}
228*40083Sbostic 	|	LIST check_login CRLF
229*40083Sbostic 		= {
230*40083Sbostic 			if ($2)
231*40083Sbostic 				retrieve("/bin/ls -lgA", "");
232*40083Sbostic 		}
233*40083Sbostic 	|	LIST check_login SP pathname CRLF
234*40083Sbostic 		= {
235*40083Sbostic 			if ($2 && $4 != NULL)
236*40083Sbostic 				retrieve("/bin/ls -lgA %s", (char *) $4);
237*40083Sbostic 			if ($4 != NULL)
238*40083Sbostic 				free((char *) $4);
239*40083Sbostic 		}
240*40083Sbostic 	|	STAT check_login SP pathname CRLF
241*40083Sbostic 		= {
242*40083Sbostic 			if ($2 && $4 != NULL)
243*40083Sbostic 				statfilecmd((char *) $4);
244*40083Sbostic 			if ($4 != NULL)
245*40083Sbostic 				free((char *) $4);
246*40083Sbostic 		}
247*40083Sbostic 	|	STAT CRLF
248*40083Sbostic 		= {
249*40083Sbostic 			statcmd();
250*40083Sbostic 		}
251*40083Sbostic 	|	DELE check_login SP pathname CRLF
252*40083Sbostic 		= {
253*40083Sbostic 			if ($2 && $4 != NULL)
254*40083Sbostic 				delete((char *) $4);
255*40083Sbostic 			if ($4 != NULL)
256*40083Sbostic 				free((char *) $4);
257*40083Sbostic 		}
258*40083Sbostic 	|	RNTO SP pathname CRLF
259*40083Sbostic 		= {
260*40083Sbostic 			if (fromname) {
261*40083Sbostic 				renamecmd(fromname, (char *) $3);
262*40083Sbostic 				free(fromname);
263*40083Sbostic 				fromname = (char *) 0;
264*40083Sbostic 			} else {
265*40083Sbostic 				reply(503, "Bad sequence of commands.");
266*40083Sbostic 			}
267*40083Sbostic 			free((char *) $3);
268*40083Sbostic 		}
269*40083Sbostic 	|	ABOR CRLF
270*40083Sbostic 		= {
271*40083Sbostic 			reply(225, "ABOR command successful.");
272*40083Sbostic 		}
273*40083Sbostic 	|	CWD check_login CRLF
274*40083Sbostic 		= {
275*40083Sbostic 			if ($2)
276*40083Sbostic 				cwd(pw->pw_dir);
277*40083Sbostic 		}
278*40083Sbostic 	|	CWD check_login SP pathname CRLF
279*40083Sbostic 		= {
280*40083Sbostic 			if ($2 && $4 != NULL)
281*40083Sbostic 				cwd((char *) $4);
282*40083Sbostic 			if ($4 != NULL)
283*40083Sbostic 				free((char *) $4);
284*40083Sbostic 		}
285*40083Sbostic 	|	HELP CRLF
286*40083Sbostic 		= {
287*40083Sbostic 			help(cmdtab, (char *) 0);
288*40083Sbostic 		}
289*40083Sbostic 	|	HELP SP STRING CRLF
290*40083Sbostic 		= {
291*40083Sbostic 			register char *cp = (char *)$3;
292*40083Sbostic 
293*40083Sbostic 			if (strncasecmp(cp, "SITE", 4) == 0) {
294*40083Sbostic 				cp = (char *)$3 + 4;
295*40083Sbostic 				if (*cp == ' ')
296*40083Sbostic 					cp++;
297*40083Sbostic 				if (*cp)
298*40083Sbostic 					help(sitetab, cp);
299*40083Sbostic 				else
300*40083Sbostic 					help(sitetab, (char *) 0);
301*40083Sbostic 			} else
302*40083Sbostic 				help(cmdtab, (char *) $3);
303*40083Sbostic 		}
304*40083Sbostic 	|	NOOP CRLF
305*40083Sbostic 		= {
306*40083Sbostic 			reply(200, "NOOP command successful.");
307*40083Sbostic 		}
308*40083Sbostic 	|	MKD check_login SP pathname CRLF
309*40083Sbostic 		= {
310*40083Sbostic 			if ($2 && $4 != NULL)
311*40083Sbostic 				makedir((char *) $4);
312*40083Sbostic 			if ($4 != NULL)
313*40083Sbostic 				free((char *) $4);
314*40083Sbostic 		}
315*40083Sbostic 	|	RMD check_login SP pathname CRLF
316*40083Sbostic 		= {
317*40083Sbostic 			if ($2 && $4 != NULL)
318*40083Sbostic 				removedir((char *) $4);
319*40083Sbostic 			if ($4 != NULL)
320*40083Sbostic 				free((char *) $4);
321*40083Sbostic 		}
322*40083Sbostic 	|	PWD check_login CRLF
323*40083Sbostic 		= {
324*40083Sbostic 			if ($2)
325*40083Sbostic 				pwd();
326*40083Sbostic 		}
327*40083Sbostic 	|	CDUP check_login CRLF
328*40083Sbostic 		= {
329*40083Sbostic 			if ($2)
330*40083Sbostic 				cwd("..");
331*40083Sbostic 		}
332*40083Sbostic 	|	SITE SP HELP CRLF
333*40083Sbostic 		= {
334*40083Sbostic 			help(sitetab, (char *) 0);
335*40083Sbostic 		}
336*40083Sbostic 	|	SITE SP HELP SP STRING CRLF
337*40083Sbostic 		= {
338*40083Sbostic 			help(sitetab, (char *) $5);
339*40083Sbostic 		}
340*40083Sbostic 	|	SITE SP UMASK check_login CRLF
341*40083Sbostic 		= {
342*40083Sbostic 			int oldmask;
343*40083Sbostic 
344*40083Sbostic 			if ($4) {
345*40083Sbostic 				oldmask = umask(0);
346*40083Sbostic 				(void) umask(oldmask);
347*40083Sbostic 				reply(200, "Current UMASK is %03o", oldmask);
348*40083Sbostic 			}
349*40083Sbostic 		}
350*40083Sbostic 	|	SITE SP UMASK check_login SP octal_number CRLF
351*40083Sbostic 		= {
352*40083Sbostic 			int oldmask;
353*40083Sbostic 
354*40083Sbostic 			if ($4) {
355*40083Sbostic 				if (($6 == -1) || ($6 > 0777)) {
356*40083Sbostic 					reply(501, "Bad UMASK value");
357*40083Sbostic 				} else {
358*40083Sbostic 					oldmask = umask($6);
359*40083Sbostic 					reply(200,
360*40083Sbostic 					    "UMASK set to %03o (was %03o)",
361*40083Sbostic 					    $6, oldmask);
362*40083Sbostic 				}
363*40083Sbostic 			}
364*40083Sbostic 		}
365*40083Sbostic 	|	SITE SP CHMOD check_login SP octal_number SP pathname CRLF
366*40083Sbostic 		= {
367*40083Sbostic 			if ($4 && ($8 != NULL)) {
368*40083Sbostic 				if ($6 > 0777)
369*40083Sbostic 					reply(501,
370*40083Sbostic 				"CHMOD: Mode value must be between 0 and 0777");
371*40083Sbostic 				else if (chmod((char *) $8, $6) < 0)
372*40083Sbostic 					perror_reply(550, (char *) $8);
373*40083Sbostic 				else
374*40083Sbostic 					reply(200, "CHMOD command successful.");
375*40083Sbostic 			}
376*40083Sbostic 			if ($8 != NULL)
377*40083Sbostic 				free((char *) $8);
378*40083Sbostic 		}
379*40083Sbostic 	|	SITE SP IDLE CRLF
380*40083Sbostic 		= {
381*40083Sbostic 			reply(200,
382*40083Sbostic 			    "Current IDLE time limit is %d seconds; max %d",
383*40083Sbostic 				timeout, maxtimeout);
384*40083Sbostic 		}
385*40083Sbostic 	|	SITE SP IDLE SP NUMBER CRLF
386*40083Sbostic 		= {
387*40083Sbostic 			if ($5 < 30 || $5 > maxtimeout) {
388*40083Sbostic 				reply(501,
389*40083Sbostic 			"Maximum IDLE time must be between 30 and %d seconds",
390*40083Sbostic 				    maxtimeout);
391*40083Sbostic 			} else {
392*40083Sbostic 				timeout = $5;
393*40083Sbostic 				(void) alarm((unsigned) timeout);
394*40083Sbostic 				reply(200,
395*40083Sbostic 				    "Maximum IDLE time set to %d seconds",
396*40083Sbostic 				    timeout);
397*40083Sbostic 			}
398*40083Sbostic 		}
399*40083Sbostic 	|	STOU check_login SP pathname CRLF
400*40083Sbostic 		= {
401*40083Sbostic 			if ($2 && $4 != NULL)
402*40083Sbostic 				store((char *) $4, "w", 1);
403*40083Sbostic 			if ($4 != NULL)
404*40083Sbostic 				free((char *) $4);
405*40083Sbostic 		}
406*40083Sbostic 	|	SYST CRLF
407*40083Sbostic 		= {
408*40083Sbostic #ifdef unix
409*40083Sbostic #ifdef BSD
410*40083Sbostic 			reply(215, "UNIX Type: L%d Version: BSD-%d",
411*40083Sbostic 				NBBY, BSD);
412*40083Sbostic #else /* BSD */
413*40083Sbostic 			reply(215, "UNIX Type: L%d", NBBY);
414*40083Sbostic #endif /* BSD */
415*40083Sbostic #else /* unix */
416*40083Sbostic 			reply(215, "UNKNOWN Type: L%d", NBBY);
417*40083Sbostic #endif /* unix */
418*40083Sbostic 		}
419*40083Sbostic 
420*40083Sbostic 		/*
421*40083Sbostic 		 * SIZE is not in RFC959, but Postel has blessed it and
422*40083Sbostic 		 * it will be in the updated RFC.
423*40083Sbostic 		 *
424*40083Sbostic 		 * Return size of file in a format suitable for
425*40083Sbostic 		 * using with RESTART (we just count bytes).
426*40083Sbostic 		 */
427*40083Sbostic 	|	SIZE check_login SP pathname CRLF
428*40083Sbostic 		= {
429*40083Sbostic 			if ($2 && $4 != NULL)
430*40083Sbostic 				sizecmd((char *) $4);
431*40083Sbostic 			if ($4 != NULL)
432*40083Sbostic 				free((char *) $4);
433*40083Sbostic 		}
434*40083Sbostic 
435*40083Sbostic 		/*
436*40083Sbostic 		 * MDTM is not in RFC959, but Postel has blessed it and
437*40083Sbostic 		 * it will be in the updated RFC.
438*40083Sbostic 		 *
439*40083Sbostic 		 * Return modification time of file as an ISO 3307
440*40083Sbostic 		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
441*40083Sbostic 		 * where xxx is the fractional second (of any precision,
442*40083Sbostic 		 * not necessarily 3 digits)
443*40083Sbostic 		 */
444*40083Sbostic 	|	MDTM check_login SP pathname CRLF
445*40083Sbostic 		= {
446*40083Sbostic 			if ($2 && $4 != NULL) {
447*40083Sbostic 				struct stat stbuf;
448*40083Sbostic 				if (stat((char *) $4, &stbuf) < 0)
449*40083Sbostic 					perror_reply(550, "%s", (char *) $4);
450*40083Sbostic 				else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
451*40083Sbostic 					reply(550, "%s: not a plain file.",
452*40083Sbostic 						(char *) $4);
453*40083Sbostic 				} else {
454*40083Sbostic 					register struct tm *t;
455*40083Sbostic 					struct tm *gmtime();
456*40083Sbostic 					t = gmtime(&stbuf.st_mtime);
457*40083Sbostic 					reply(213,
458*40083Sbostic 					    "19%02d%02d%02d%02d%02d%02d",
459*40083Sbostic 					    t->tm_year, t->tm_mon+1, t->tm_mday,
460*40083Sbostic 					    t->tm_hour, t->tm_min, t->tm_sec);
461*40083Sbostic 				}
462*40083Sbostic 			}
463*40083Sbostic 			if ($4 != NULL)
464*40083Sbostic 				free((char *) $4);
465*40083Sbostic 		}
466*40083Sbostic 	|	QUIT CRLF
467*40083Sbostic 		= {
468*40083Sbostic 			reply(221, "Goodbye.");
469*40083Sbostic 			dologout(0);
470*40083Sbostic 		}
471*40083Sbostic 	|	error CRLF
472*40083Sbostic 		= {
473*40083Sbostic 			yyerrok;
474*40083Sbostic 		}
475*40083Sbostic 	;
476*40083Sbostic rcmd:		RNFR check_login SP pathname CRLF
477*40083Sbostic 		= {
478*40083Sbostic 			char *renamefrom();
479*40083Sbostic 
480*40083Sbostic 			if ($2 && $4) {
481*40083Sbostic 				fromname = renamefrom((char *) $4);
482*40083Sbostic 				if (fromname == (char *) 0 && $4) {
483*40083Sbostic 					free((char *) $4);
484*40083Sbostic 				}
485*40083Sbostic 			}
486*40083Sbostic 		}
487*40083Sbostic 	;
488*40083Sbostic 
489*40083Sbostic username:	STRING
490*40083Sbostic 	;
491*40083Sbostic 
492*40083Sbostic password:	/* empty */
493*40083Sbostic 		= {
494*40083Sbostic 			*(char **)&($$) = "";
495*40083Sbostic 		}
496*40083Sbostic 	|	STRING
497*40083Sbostic 	;
498*40083Sbostic 
499*40083Sbostic byte_size:	NUMBER
500*40083Sbostic 	;
501*40083Sbostic 
502*40083Sbostic host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
503*40083Sbostic 		NUMBER COMMA NUMBER
504*40083Sbostic 		= {
505*40083Sbostic 			register char *a, *p;
506*40083Sbostic 
507*40083Sbostic 			a = (char *)&data_dest.sin_addr;
508*40083Sbostic 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
509*40083Sbostic 			p = (char *)&data_dest.sin_port;
510*40083Sbostic 			p[0] = $9; p[1] = $11;
511*40083Sbostic 			data_dest.sin_family = AF_INET;
512*40083Sbostic 		}
513*40083Sbostic 	;
514*40083Sbostic 
515*40083Sbostic form_code:	N
516*40083Sbostic 	= {
517*40083Sbostic 		$$ = FORM_N;
518*40083Sbostic 	}
519*40083Sbostic 	|	T
520*40083Sbostic 	= {
521*40083Sbostic 		$$ = FORM_T;
522*40083Sbostic 	}
523*40083Sbostic 	|	C
524*40083Sbostic 	= {
525*40083Sbostic 		$$ = FORM_C;
526*40083Sbostic 	}
527*40083Sbostic 	;
528*40083Sbostic 
529*40083Sbostic type_code:	A
530*40083Sbostic 	= {
531*40083Sbostic 		cmd_type = TYPE_A;
532*40083Sbostic 		cmd_form = FORM_N;
533*40083Sbostic 	}
534*40083Sbostic 	|	A SP form_code
535*40083Sbostic 	= {
536*40083Sbostic 		cmd_type = TYPE_A;
537*40083Sbostic 		cmd_form = $3;
538*40083Sbostic 	}
539*40083Sbostic 	|	E
540*40083Sbostic 	= {
541*40083Sbostic 		cmd_type = TYPE_E;
542*40083Sbostic 		cmd_form = FORM_N;
543*40083Sbostic 	}
544*40083Sbostic 	|	E SP form_code
545*40083Sbostic 	= {
546*40083Sbostic 		cmd_type = TYPE_E;
547*40083Sbostic 		cmd_form = $3;
548*40083Sbostic 	}
549*40083Sbostic 	|	I
550*40083Sbostic 	= {
551*40083Sbostic 		cmd_type = TYPE_I;
552*40083Sbostic 	}
553*40083Sbostic 	|	L
554*40083Sbostic 	= {
555*40083Sbostic 		cmd_type = TYPE_L;
556*40083Sbostic 		cmd_bytesz = NBBY;
557*40083Sbostic 	}
558*40083Sbostic 	|	L SP byte_size
559*40083Sbostic 	= {
560*40083Sbostic 		cmd_type = TYPE_L;
561*40083Sbostic 		cmd_bytesz = $3;
562*40083Sbostic 	}
563*40083Sbostic 	/* this is for a bug in the BBN ftp */
564*40083Sbostic 	|	L byte_size
565*40083Sbostic 	= {
566*40083Sbostic 		cmd_type = TYPE_L;
567*40083Sbostic 		cmd_bytesz = $2;
568*40083Sbostic 	}
569*40083Sbostic 	;
570*40083Sbostic 
571*40083Sbostic struct_code:	F
572*40083Sbostic 	= {
573*40083Sbostic 		$$ = STRU_F;
574*40083Sbostic 	}
575*40083Sbostic 	|	R
576*40083Sbostic 	= {
577*40083Sbostic 		$$ = STRU_R;
578*40083Sbostic 	}
579*40083Sbostic 	|	P
580*40083Sbostic 	= {
581*40083Sbostic 		$$ = STRU_P;
582*40083Sbostic 	}
583*40083Sbostic 	;
584*40083Sbostic 
585*40083Sbostic mode_code:	S
586*40083Sbostic 	= {
587*40083Sbostic 		$$ = MODE_S;
588*40083Sbostic 	}
589*40083Sbostic 	|	B
590*40083Sbostic 	= {
591*40083Sbostic 		$$ = MODE_B;
592*40083Sbostic 	}
593*40083Sbostic 	|	C
594*40083Sbostic 	= {
595*40083Sbostic 		$$ = MODE_C;
596*40083Sbostic 	}
597*40083Sbostic 	;
598*40083Sbostic 
599*40083Sbostic pathname:	pathstring
600*40083Sbostic 	= {
601*40083Sbostic 		/*
602*40083Sbostic 		 * Problem: this production is used for all pathname
603*40083Sbostic 		 * processing, but only gives a 550 error reply.
604*40083Sbostic 		 * This is a valid reply in some cases but not in others.
605*40083Sbostic 		 */
606*40083Sbostic 		if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
607*40083Sbostic 			*(char **)&($$) = *glob((char *) $1);
608*40083Sbostic 			if (globerr != NULL) {
609*40083Sbostic 				reply(550, globerr);
610*40083Sbostic 				$$ = NULL;
611*40083Sbostic 			}
612*40083Sbostic 			free((char *) $1);
613*40083Sbostic 		} else
614*40083Sbostic 			$$ = $1;
615*40083Sbostic 	}
616*40083Sbostic 	;
617*40083Sbostic 
618*40083Sbostic pathstring:	STRING
619*40083Sbostic 	;
620*40083Sbostic 
621*40083Sbostic octal_number:	NUMBER
622*40083Sbostic 	= {
623*40083Sbostic 		register int ret, dec, multby, digit;
624*40083Sbostic 
625*40083Sbostic 		/*
626*40083Sbostic 		 * Convert a number that was read as decimal number
627*40083Sbostic 		 * to what it would be if it had been read as octal.
628*40083Sbostic 		 */
629*40083Sbostic 		dec = $1;
630*40083Sbostic 		multby = 1;
631*40083Sbostic 		ret = 0;
632*40083Sbostic 		while (dec) {
633*40083Sbostic 			digit = dec%10;
634*40083Sbostic 			if (digit > 7) {
635*40083Sbostic 				ret = -1;
636*40083Sbostic 				break;
637*40083Sbostic 			}
638*40083Sbostic 			ret += digit * multby;
639*40083Sbostic 			multby *= 8;
640*40083Sbostic 			dec /= 10;
641*40083Sbostic 		}
642*40083Sbostic 		$$ = ret;
643*40083Sbostic 	}
644*40083Sbostic 	;
645*40083Sbostic 
646*40083Sbostic check_login:	/* empty */
647*40083Sbostic 	= {
648*40083Sbostic 		if (logged_in)
649*40083Sbostic 			$$ = 1;
650*40083Sbostic 		else {
651*40083Sbostic 			reply(530, "Please login with USER and PASS.");
652*40083Sbostic 			$$ = 0;
653*40083Sbostic 		}
654*40083Sbostic 	}
655*40083Sbostic 	;
656*40083Sbostic 
657*40083Sbostic %%
658*40083Sbostic 
659*40083Sbostic extern jmp_buf errcatch;
660*40083Sbostic 
661*40083Sbostic #define	CMD	0	/* beginning of command */
662*40083Sbostic #define	ARGS	1	/* expect miscellaneous arguments */
663*40083Sbostic #define	STR1	2	/* expect SP followed by STRING */
664*40083Sbostic #define	STR2	3	/* expect STRING */
665*40083Sbostic #define	OSTR	4	/* optional SP then STRING */
666*40083Sbostic #define	ZSTR1	5	/* SP then optional STRING */
667*40083Sbostic #define	ZSTR2	6	/* optional STRING after SP */
668*40083Sbostic #define	SITECMD	7	/* SITE command */
669*40083Sbostic #define	NSTR	8	/* Number followed by a string */
670*40083Sbostic 
671*40083Sbostic struct tab {
672*40083Sbostic 	char	*name;
673*40083Sbostic 	short	token;
674*40083Sbostic 	short	state;
675*40083Sbostic 	short	implemented;	/* 1 if command is implemented */
676*40083Sbostic 	char	*help;
677*40083Sbostic };
678*40083Sbostic 
679*40083Sbostic struct tab cmdtab[] = {		/* In order defined in RFC 765 */
680*40083Sbostic 	{ "USER", USER, STR1, 1,	"<sp> username" },
681*40083Sbostic 	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
682*40083Sbostic 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
683*40083Sbostic 	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
684*40083Sbostic 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
685*40083Sbostic 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
686*40083Sbostic 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
687*40083Sbostic 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
688*40083Sbostic 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
689*40083Sbostic 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
690*40083Sbostic 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
691*40083Sbostic 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
692*40083Sbostic 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
693*40083Sbostic 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
694*40083Sbostic 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
695*40083Sbostic 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
696*40083Sbostic 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
697*40083Sbostic 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
698*40083Sbostic 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
699*40083Sbostic 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
700*40083Sbostic 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
701*40083Sbostic 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
702*40083Sbostic 	{ "REST", REST, ARGS, 0,	"(restart command)" },
703*40083Sbostic 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
704*40083Sbostic 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
705*40083Sbostic 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
706*40083Sbostic 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
707*40083Sbostic 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
708*40083Sbostic 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
709*40083Sbostic 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
710*40083Sbostic 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
711*40083Sbostic 	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
712*40083Sbostic 	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
713*40083Sbostic 	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
714*40083Sbostic 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
715*40083Sbostic 	{ "NOOP", NOOP, ARGS, 1,	"" },
716*40083Sbostic 	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
717*40083Sbostic 	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
718*40083Sbostic 	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
719*40083Sbostic 	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
720*40083Sbostic 	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
721*40083Sbostic 	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
722*40083Sbostic 	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
723*40083Sbostic 	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
724*40083Sbostic 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
725*40083Sbostic 	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
726*40083Sbostic 	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
727*40083Sbostic 	{ NULL,   0,    0,    0,	0 }
728*40083Sbostic };
729*40083Sbostic 
730*40083Sbostic struct tab sitetab[] = {
731*40083Sbostic 	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
732*40083Sbostic 	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
733*40083Sbostic 	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
734*40083Sbostic 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
735*40083Sbostic 	{ NULL,   0,    0,    0,	0 }
736*40083Sbostic };
737*40083Sbostic 
738*40083Sbostic struct tab *
lookup(p,cmd)739*40083Sbostic lookup(p, cmd)
740*40083Sbostic 	register struct tab *p;
741*40083Sbostic 	char *cmd;
742*40083Sbostic {
743*40083Sbostic 
744*40083Sbostic 	for (; p->name != NULL; p++)
745*40083Sbostic 		if (strcmp(cmd, p->name) == 0)
746*40083Sbostic 			return (p);
747*40083Sbostic 	return (0);
748*40083Sbostic }
749*40083Sbostic 
750*40083Sbostic #include <arpa/telnet.h>
751*40083Sbostic 
752*40083Sbostic /*
753*40083Sbostic  * getline - a hacked up version of fgets to ignore TELNET escape codes.
754*40083Sbostic  */
755*40083Sbostic char *
getline(s,n,iop)756*40083Sbostic getline(s, n, iop)
757*40083Sbostic 	char *s;
758*40083Sbostic 	register FILE *iop;
759*40083Sbostic {
760*40083Sbostic 	register c;
761*40083Sbostic 	register char *cs;
762*40083Sbostic 
763*40083Sbostic 	cs = s;
764*40083Sbostic /* tmpline may contain saved command from urgent mode interruption */
765*40083Sbostic 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
766*40083Sbostic 		*cs++ = tmpline[c];
767*40083Sbostic 		if (tmpline[c] == '\n') {
768*40083Sbostic 			*cs++ = '\0';
769*40083Sbostic 			if (debug)
770*40083Sbostic 				syslog(LOG_DEBUG, "command: %s", s);
771*40083Sbostic 			tmpline[0] = '\0';
772*40083Sbostic 			return(s);
773*40083Sbostic 		}
774*40083Sbostic 		if (c == 0)
775*40083Sbostic 			tmpline[0] = '\0';
776*40083Sbostic 	}
777*40083Sbostic 	while ((c = getc(iop)) != EOF) {
778*40083Sbostic 		c &= 0377;
779*40083Sbostic 		if (c == IAC) {
780*40083Sbostic 		    if ((c = getc(iop)) != EOF) {
781*40083Sbostic 			c &= 0377;
782*40083Sbostic 			switch (c) {
783*40083Sbostic 			case WILL:
784*40083Sbostic 			case WONT:
785*40083Sbostic 				c = getc(iop);
786*40083Sbostic 				printf("%c%c%c", IAC, DONT, 0377&c);
787*40083Sbostic 				(void) fflush(stdout);
788*40083Sbostic 				continue;
789*40083Sbostic 			case DO:
790*40083Sbostic 			case DONT:
791*40083Sbostic 				c = getc(iop);
792*40083Sbostic 				printf("%c%c%c", IAC, WONT, 0377&c);
793*40083Sbostic 				(void) fflush(stdout);
794*40083Sbostic 				continue;
795*40083Sbostic 			case IAC:
796*40083Sbostic 				break;
797*40083Sbostic 			default:
798*40083Sbostic 				continue;	/* ignore command */
799*40083Sbostic 			}
800*40083Sbostic 		    }
801*40083Sbostic 		}
802*40083Sbostic 		*cs++ = c;
803*40083Sbostic 		if (--n <= 0 || c == '\n')
804*40083Sbostic 			break;
805*40083Sbostic 	}
806*40083Sbostic 	if (c == EOF && cs == s)
807*40083Sbostic 		return (NULL);
808*40083Sbostic 	*cs++ = '\0';
809*40083Sbostic 	if (debug)
810*40083Sbostic 		syslog(LOG_DEBUG, "command: %s", s);
811*40083Sbostic 	return (s);
812*40083Sbostic }
813*40083Sbostic 
814*40083Sbostic static int
toolong()815*40083Sbostic toolong()
816*40083Sbostic {
817*40083Sbostic 	time_t now;
818*40083Sbostic 	extern char *ctime();
819*40083Sbostic 	extern time_t time();
820*40083Sbostic 
821*40083Sbostic 	reply(421,
822*40083Sbostic 	  "Timeout (%d seconds): closing control connection.", timeout);
823*40083Sbostic 	(void) time(&now);
824*40083Sbostic 	if (logging) {
825*40083Sbostic 		syslog(LOG_INFO,
826*40083Sbostic 			"User %s timed out after %d seconds at %s",
827*40083Sbostic 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
828*40083Sbostic 	}
829*40083Sbostic 	dologout(1);
830*40083Sbostic }
831*40083Sbostic 
yylex()832*40083Sbostic yylex()
833*40083Sbostic {
834*40083Sbostic 	static int cpos, state;
835*40083Sbostic 	register char *cp, *cp2;
836*40083Sbostic 	register struct tab *p;
837*40083Sbostic 	int n;
838*40083Sbostic 	char c, *strpbrk();
839*40083Sbostic 	char *copy();
840*40083Sbostic 
841*40083Sbostic 	for (;;) {
842*40083Sbostic 		switch (state) {
843*40083Sbostic 
844*40083Sbostic 		case CMD:
845*40083Sbostic 			(void) signal(SIGALRM, toolong);
846*40083Sbostic 			(void) alarm((unsigned) timeout);
847*40083Sbostic 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
848*40083Sbostic 				reply(221, "You could at least say goodbye.");
849*40083Sbostic 				dologout(0);
850*40083Sbostic 			}
851*40083Sbostic 			(void) alarm(0);
852*40083Sbostic #ifdef SETPROCTITLE
853*40083Sbostic 			if (strncasecmp(cbuf, "PASS", 4) != NULL)
854*40083Sbostic 				setproctitle("%s: %s", proctitle, cbuf);
855*40083Sbostic #endif /* SETPROCTITLE */
856*40083Sbostic 			if ((cp = index(cbuf, '\r'))) {
857*40083Sbostic 				*cp++ = '\n';
858*40083Sbostic 				*cp = '\0';
859*40083Sbostic 			}
860*40083Sbostic 			if ((cp = strpbrk(cbuf, " \n")))
861*40083Sbostic 				cpos = cp - cbuf;
862*40083Sbostic 			if (cpos == 0)
863*40083Sbostic 				cpos = 4;
864*40083Sbostic 			c = cbuf[cpos];
865*40083Sbostic 			cbuf[cpos] = '\0';
866*40083Sbostic 			upper(cbuf);
867*40083Sbostic 			p = lookup(cmdtab, cbuf);
868*40083Sbostic 			cbuf[cpos] = c;
869*40083Sbostic 			if (p != 0) {
870*40083Sbostic 				if (p->implemented == 0) {
871*40083Sbostic 					nack(p->name);
872*40083Sbostic 					longjmp(errcatch,0);
873*40083Sbostic 					/* NOTREACHED */
874*40083Sbostic 				}
875*40083Sbostic 				state = p->state;
876*40083Sbostic 				*(char **)&yylval = p->name;
877*40083Sbostic 				return (p->token);
878*40083Sbostic 			}
879*40083Sbostic 			break;
880*40083Sbostic 
881*40083Sbostic 		case SITECMD:
882*40083Sbostic 			if (cbuf[cpos] == ' ') {
883*40083Sbostic 				cpos++;
884*40083Sbostic 				return (SP);
885*40083Sbostic 			}
886*40083Sbostic 			cp = &cbuf[cpos];
887*40083Sbostic 			if ((cp2 = strpbrk(cp, " \n")))
888*40083Sbostic 				cpos = cp2 - cbuf;
889*40083Sbostic 			c = cbuf[cpos];
890*40083Sbostic 			cbuf[cpos] = '\0';
891*40083Sbostic 			upper(cp);
892*40083Sbostic 			p = lookup(sitetab, cp);
893*40083Sbostic 			cbuf[cpos] = c;
894*40083Sbostic 			if (p != 0) {
895*40083Sbostic 				if (p->implemented == 0) {
896*40083Sbostic 					state = CMD;
897*40083Sbostic 					nack(p->name);
898*40083Sbostic 					longjmp(errcatch,0);
899*40083Sbostic 					/* NOTREACHED */
900*40083Sbostic 				}
901*40083Sbostic 				state = p->state;
902*40083Sbostic 				*(char **)&yylval = p->name;
903*40083Sbostic 				return (p->token);
904*40083Sbostic 			}
905*40083Sbostic 			state = CMD;
906*40083Sbostic 			break;
907*40083Sbostic 
908*40083Sbostic 		case OSTR:
909*40083Sbostic 			if (cbuf[cpos] == '\n') {
910*40083Sbostic 				state = CMD;
911*40083Sbostic 				return (CRLF);
912*40083Sbostic 			}
913*40083Sbostic 			/* FALLTHROUGH */
914*40083Sbostic 
915*40083Sbostic 		case STR1:
916*40083Sbostic 		case ZSTR1:
917*40083Sbostic 		dostr1:
918*40083Sbostic 			if (cbuf[cpos] == ' ') {
919*40083Sbostic 				cpos++;
920*40083Sbostic 				state = state == OSTR ? STR2 : ++state;
921*40083Sbostic 				return (SP);
922*40083Sbostic 			}
923*40083Sbostic 			break;
924*40083Sbostic 
925*40083Sbostic 		case ZSTR2:
926*40083Sbostic 			if (cbuf[cpos] == '\n') {
927*40083Sbostic 				state = CMD;
928*40083Sbostic 				return (CRLF);
929*40083Sbostic 			}
930*40083Sbostic 			/* FALLTHROUGH */
931*40083Sbostic 
932*40083Sbostic 		case STR2:
933*40083Sbostic 			cp = &cbuf[cpos];
934*40083Sbostic 			n = strlen(cp);
935*40083Sbostic 			cpos += n - 1;
936*40083Sbostic 			/*
937*40083Sbostic 			 * Make sure the string is nonempty and \n terminated.
938*40083Sbostic 			 */
939*40083Sbostic 			if (n > 1 && cbuf[cpos] == '\n') {
940*40083Sbostic 				cbuf[cpos] = '\0';
941*40083Sbostic 				*(char **)&yylval = copy(cp);
942*40083Sbostic 				cbuf[cpos] = '\n';
943*40083Sbostic 				state = ARGS;
944*40083Sbostic 				return (STRING);
945*40083Sbostic 			}
946*40083Sbostic 			break;
947*40083Sbostic 
948*40083Sbostic 		case NSTR:
949*40083Sbostic 			if (cbuf[cpos] == ' ') {
950*40083Sbostic 				cpos++;
951*40083Sbostic 				return (SP);
952*40083Sbostic 			}
953*40083Sbostic 			if (isdigit(cbuf[cpos])) {
954*40083Sbostic 				cp = &cbuf[cpos];
955*40083Sbostic 				while (isdigit(cbuf[++cpos]))
956*40083Sbostic 					;
957*40083Sbostic 				c = cbuf[cpos];
958*40083Sbostic 				cbuf[cpos] = '\0';
959*40083Sbostic 				yylval = atoi(cp);
960*40083Sbostic 				cbuf[cpos] = c;
961*40083Sbostic 				state = STR1;
962*40083Sbostic 				return (NUMBER);
963*40083Sbostic 			}
964*40083Sbostic 			state = STR1;
965*40083Sbostic 			goto dostr1;
966*40083Sbostic 
967*40083Sbostic 		case ARGS:
968*40083Sbostic 			if (isdigit(cbuf[cpos])) {
969*40083Sbostic 				cp = &cbuf[cpos];
970*40083Sbostic 				while (isdigit(cbuf[++cpos]))
971*40083Sbostic 					;
972*40083Sbostic 				c = cbuf[cpos];
973*40083Sbostic 				cbuf[cpos] = '\0';
974*40083Sbostic 				yylval = atoi(cp);
975*40083Sbostic 				cbuf[cpos] = c;
976*40083Sbostic 				return (NUMBER);
977*40083Sbostic 			}
978*40083Sbostic 			switch (cbuf[cpos++]) {
979*40083Sbostic 
980*40083Sbostic 			case '\n':
981*40083Sbostic 				state = CMD;
982*40083Sbostic 				return (CRLF);
983*40083Sbostic 
984*40083Sbostic 			case ' ':
985*40083Sbostic 				return (SP);
986*40083Sbostic 
987*40083Sbostic 			case ',':
988*40083Sbostic 				return (COMMA);
989*40083Sbostic 
990*40083Sbostic 			case 'A':
991*40083Sbostic 			case 'a':
992*40083Sbostic 				return (A);
993*40083Sbostic 
994*40083Sbostic 			case 'B':
995*40083Sbostic 			case 'b':
996*40083Sbostic 				return (B);
997*40083Sbostic 
998*40083Sbostic 			case 'C':
999*40083Sbostic 			case 'c':
1000*40083Sbostic 				return (C);
1001*40083Sbostic 
1002*40083Sbostic 			case 'E':
1003*40083Sbostic 			case 'e':
1004*40083Sbostic 				return (E);
1005*40083Sbostic 
1006*40083Sbostic 			case 'F':
1007*40083Sbostic 			case 'f':
1008*40083Sbostic 				return (F);
1009*40083Sbostic 
1010*40083Sbostic 			case 'I':
1011*40083Sbostic 			case 'i':
1012*40083Sbostic 				return (I);
1013*40083Sbostic 
1014*40083Sbostic 			case 'L':
1015*40083Sbostic 			case 'l':
1016*40083Sbostic 				return (L);
1017*40083Sbostic 
1018*40083Sbostic 			case 'N':
1019*40083Sbostic 			case 'n':
1020*40083Sbostic 				return (N);
1021*40083Sbostic 
1022*40083Sbostic 			case 'P':
1023*40083Sbostic 			case 'p':
1024*40083Sbostic 				return (P);
1025*40083Sbostic 
1026*40083Sbostic 			case 'R':
1027*40083Sbostic 			case 'r':
1028*40083Sbostic 				return (R);
1029*40083Sbostic 
1030*40083Sbostic 			case 'S':
1031*40083Sbostic 			case 's':
1032*40083Sbostic 				return (S);
1033*40083Sbostic 
1034*40083Sbostic 			case 'T':
1035*40083Sbostic 			case 't':
1036*40083Sbostic 				return (T);
1037*40083Sbostic 
1038*40083Sbostic 			}
1039*40083Sbostic 			break;
1040*40083Sbostic 
1041*40083Sbostic 		default:
1042*40083Sbostic 			fatal("Unknown state in scanner.");
1043*40083Sbostic 		}
1044*40083Sbostic 		yyerror((char *) 0);
1045*40083Sbostic 		state = CMD;
1046*40083Sbostic 		longjmp(errcatch,0);
1047*40083Sbostic 	}
1048*40083Sbostic }
1049*40083Sbostic 
upper(s)1050*40083Sbostic upper(s)
1051*40083Sbostic 	register char *s;
1052*40083Sbostic {
1053*40083Sbostic 	while (*s != '\0') {
1054*40083Sbostic 		if (islower(*s))
1055*40083Sbostic 			*s = toupper(*s);
1056*40083Sbostic 		s++;
1057*40083Sbostic 	}
1058*40083Sbostic }
1059*40083Sbostic 
1060*40083Sbostic char *
copy(s)1061*40083Sbostic copy(s)
1062*40083Sbostic 	char *s;
1063*40083Sbostic {
1064*40083Sbostic 	char *p;
1065*40083Sbostic 	extern char *malloc(), *strcpy();
1066*40083Sbostic 
1067*40083Sbostic 	p = malloc((unsigned) strlen(s) + 1);
1068*40083Sbostic 	if (p == NULL)
1069*40083Sbostic 		fatal("Ran out of memory.");
1070*40083Sbostic 	(void) strcpy(p, s);
1071*40083Sbostic 	return (p);
1072*40083Sbostic }
1073*40083Sbostic 
1074*40083Sbostic help(ctab, s)
1075*40083Sbostic 	struct tab *ctab;
1076*40083Sbostic 	char *s;
1077*40083Sbostic {
1078*40083Sbostic 	register struct tab *c;
1079*40083Sbostic 	register int width, NCMDS;
1080*40083Sbostic 	char *type;
1081*40083Sbostic 
1082*40083Sbostic 	if (ctab == sitetab)
1083*40083Sbostic 		type = "SITE ";
1084*40083Sbostic 	else
1085*40083Sbostic 		type = "";
1086*40083Sbostic 	width = 0, NCMDS = 0;
1087*40083Sbostic 	for (c = ctab; c->name != NULL; c++) {
1088*40083Sbostic 		int len = strlen(c->name);
1089*40083Sbostic 
1090*40083Sbostic 		if (len > width)
1091*40083Sbostic 			width = len;
1092*40083Sbostic 		NCMDS++;
1093*40083Sbostic 	}
1094*40083Sbostic 	width = (width + 8) &~ 7;
1095*40083Sbostic 	if (s == 0) {
1096*40083Sbostic 		register int i, j, w;
1097*40083Sbostic 		int columns, lines;
1098*40083Sbostic 
1099*40083Sbostic 		lreply(214, "The following %scommands are recognized %s.",
1100*40083Sbostic 		    type, "(* =>'s unimplemented)");
1101*40083Sbostic 		columns = 76 / width;
1102*40083Sbostic 		if (columns == 0)
1103*40083Sbostic 			columns = 1;
1104*40083Sbostic 		lines = (NCMDS + columns - 1) / columns;
1105*40083Sbostic 		for (i = 0; i < lines; i++) {
1106*40083Sbostic 			printf("   ");
1107*40083Sbostic 			for (j = 0; j < columns; j++) {
1108*40083Sbostic 				c = ctab + j * lines + i;
1109*40083Sbostic 				printf("%s%c", c->name,
1110*40083Sbostic 					c->implemented ? ' ' : '*');
1111*40083Sbostic 				if (c + lines >= &ctab[NCMDS])
1112*40083Sbostic 					break;
1113*40083Sbostic 				w = strlen(c->name) + 1;
1114*40083Sbostic 				while (w < width) {
1115*40083Sbostic 					putchar(' ');
1116*40083Sbostic 					w++;
1117*40083Sbostic 				}
1118*40083Sbostic 			}
1119*40083Sbostic 			printf("\r\n");
1120*40083Sbostic 		}
1121*40083Sbostic 		(void) fflush(stdout);
1122*40083Sbostic 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1123*40083Sbostic 		return;
1124*40083Sbostic 	}
1125*40083Sbostic 	upper(s);
1126*40083Sbostic 	c = lookup(ctab, s);
1127*40083Sbostic 	if (c == (struct tab *)0) {
1128*40083Sbostic 		reply(502, "Unknown command %s.", s);
1129*40083Sbostic 		return;
1130*40083Sbostic 	}
1131*40083Sbostic 	if (c->implemented)
1132*40083Sbostic 		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1133*40083Sbostic 	else
1134*40083Sbostic 		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1135*40083Sbostic 		    c->name, c->help);
1136*40083Sbostic }
1137*40083Sbostic 
sizecmd(filename)1138*40083Sbostic sizecmd(filename)
1139*40083Sbostic char *filename;
1140*40083Sbostic {
1141*40083Sbostic 	switch (type) {
1142*40083Sbostic 	case TYPE_L:
1143*40083Sbostic 	case TYPE_I: {
1144*40083Sbostic 		struct stat stbuf;
1145*40083Sbostic 		if (stat(filename, &stbuf) < 0 ||
1146*40083Sbostic 		    (stbuf.st_mode&S_IFMT) != S_IFREG)
1147*40083Sbostic 			reply(550, "%s: not a plain file.", filename);
1148*40083Sbostic 		else
1149*40083Sbostic 			reply(213, "%lu", stbuf.st_size);
1150*40083Sbostic 		break;}
1151*40083Sbostic 	case TYPE_A: {
1152*40083Sbostic 		FILE *fin;
1153*40083Sbostic 		register int c, count;
1154*40083Sbostic 		struct stat stbuf;
1155*40083Sbostic 		fin = fopen(filename, "r");
1156*40083Sbostic 		if (fin == NULL) {
1157*40083Sbostic 			perror_reply(550, filename);
1158*40083Sbostic 			return;
1159*40083Sbostic 		}
1160*40083Sbostic 		if (fstat(fileno(fin), &stbuf) < 0 ||
1161*40083Sbostic 		    (stbuf.st_mode&S_IFMT) != S_IFREG) {
1162*40083Sbostic 			reply(550, "%s: not a plain file.", filename);
1163*40083Sbostic 			(void) fclose(fin);
1164*40083Sbostic 			return;
1165*40083Sbostic 		}
1166*40083Sbostic 
1167*40083Sbostic 		count = 0;
1168*40083Sbostic 		while((c=getc(fin)) != EOF) {
1169*40083Sbostic 			if (c == '\n')	/* will get expanded to \r\n */
1170*40083Sbostic 				count++;
1171*40083Sbostic 			count++;
1172*40083Sbostic 		}
1173*40083Sbostic 		(void) fclose(fin);
1174*40083Sbostic 
1175*40083Sbostic 		reply(213, "%ld", count);
1176*40083Sbostic 		break;}
1177*40083Sbostic 	default:
1178*40083Sbostic 		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1179*40083Sbostic 	}
1180*40083Sbostic }
1181