1234949Sbapt/*
2234949Sbapt * Copyright (c) 1985, 1988 Regents of the University of California.
3234949Sbapt * All rights reserved.
4234949Sbapt *
5234949Sbapt * Redistribution and use in source and binary forms are permitted
6234949Sbapt * provided that the above copyright notice and this paragraph are
7234949Sbapt * duplicated in all such forms and that any documentation,
8234949Sbapt * advertising materials, and other materials related to such
9234949Sbapt * distribution and use acknowledge that the software was developed
10234949Sbapt * by the University of California, Berkeley.  The name of the
11234949Sbapt * University may not be used to endorse or promote products derived
12234949Sbapt * from this software without specific prior written permission.
13234949Sbapt * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14234949Sbapt * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15234949Sbapt * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16234949Sbapt *
17234949Sbapt *	@(#)ftpcmd.y	5.20.1.1 (Berkeley) 3/2/89
18234949Sbapt */
19234949Sbapt
20234949Sbapt/*
21234949Sbapt * Grammar for FTP commands.
22234949Sbapt * See RFC 959.
23234949Sbapt */
24234949Sbapt
25234949Sbapt%{
26234949Sbapt
27234949Sbapt/* sccsid[] = "@(#)ftpcmd.y	5.20.1.1 (Berkeley) 3/2/89"; */
28234949Sbapt
29234949Sbapt#include <sys/param.h>
30234949Sbapt#include <sys/socket.h>
31234949Sbapt
32234949Sbapt#include <netinet/in.h>
33234949Sbapt
34234949Sbapt#include <arpa/ftp.h>
35234949Sbapt
36234949Sbapt#include <stdlib.h>
37234949Sbapt#include <unistd.h>
38234949Sbapt#include <stdio.h>
39234949Sbapt#include <signal.h>
40234949Sbapt#include <ctype.h>
41234949Sbapt#include <pwd.h>
42234949Sbapt#include <setjmp.h>
43234949Sbapt#include <syslog.h>
44234949Sbapt#include <sys/stat.h>
45234949Sbapt#include <string.h>
46234949Sbapt#include <time.h>
47234949Sbapt#include <assert.h>
48234949Sbapt
49234949Sbapt#ifdef YYBISON
50234949Sbaptint yylex(void);
51234949Sbaptstatic void yyerror(const char *);
52234949Sbapt#endif
53234949Sbapt
54234949Sbaptextern	struct sockaddr_in data_dest;
55234949Sbaptextern	int logged_in;
56234949Sbaptextern	struct passwd *pw;
57234949Sbaptextern	int guest;
58234949Sbaptextern	int logging;
59234949Sbaptextern	int type;
60234949Sbaptextern	int form;
61234949Sbaptextern	int debug;
62234949Sbaptextern	int timeout;
63234949Sbaptextern	int maxtimeout;
64234949Sbaptextern  int pdata;
65234949Sbaptextern	char hostname[], remotehost[];
66234949Sbaptextern	char proctitle[];
67234949Sbaptextern	char *globerr;
68234949Sbaptextern	int usedefault;
69234949Sbaptextern  int transflag;
70234949Sbaptextern  char tmpline[];
71234949Sbapt
72234949Sbaptextern char **glob(char *);
73234949Sbaptextern char *renamefrom(char *);
74234949Sbaptextern void cwd(const char *);
75234949Sbapt
76234949Sbaptextern void dologout(int);
77234949Sbaptextern void fatal(const char *);
78234949Sbaptextern void makedir(const char *);
79234949Sbaptextern void nack(const char *);
80234949Sbaptextern void pass(const char *);
81234949Sbaptextern void passive(void);
82234949Sbaptextern void pwd(void);
83234949Sbaptextern void removedir(char *);
84234949Sbaptextern void renamecmd(char *, char *);
85234949Sbaptextern void retrieve(const char *, const char *);
86234949Sbaptextern void send_file_list(const char *);
87234949Sbaptextern void statcmd(void);
88234949Sbaptextern void statfilecmd(const char *);
89234949Sbaptextern void store(char *, const char *, int);
90234949Sbaptextern void user(const char *);
91234949Sbapt
92234949Sbaptextern void perror_reply(int, const char *, ...);
93234949Sbaptextern void reply(int, const char *, ...);
94234949Sbaptextern void lreply(int, const char *, ...);
95234949Sbapt
96234949Sbaptstatic	int cmd_type;
97234949Sbaptstatic	int cmd_form;
98234949Sbaptstatic	int cmd_bytesz;
99234949Sbaptchar	cbuf[512];
100234949Sbaptchar	*fromname;
101234949Sbapt
102234949Sbaptstruct tab {
103234949Sbapt	const char *name;
104234949Sbapt	short	token;
105234949Sbapt	short	state;
106234949Sbapt	short	implemented;	/* 1 if command is implemented */
107234949Sbapt	const char *help;
108234949Sbapt};
109234949Sbapt
110234949Sbaptstatic char * copy(const char *);
111234949Sbapt
112234949Sbapt#ifdef YYBISON
113234949Sbaptstatic void sizecmd(char *filename);
114234949Sbaptstatic void help(struct tab *ctab, char *s);
115234949Sbaptstruct tab cmdtab[];
116234949Sbaptstruct tab sitetab[];
117234949Sbapt#endif
118234949Sbapt
119234949Sbaptstatic void
120234949Sbaptyyerror(const char *msg)
121234949Sbapt{
122234949Sbapt	perror(msg);
123234949Sbapt}
124234949Sbapt%}
125234949Sbapt
126251143Sbapt%union
127251143Sbapt{
128251143Sbapt	int ival;
129251143Sbapt	char *sval;
130251143Sbapt}
131251143Sbapt%token <ival> NUMBER
132251143Sbapt%token <sval> STRING
133251143Sbapt
134251143Sbapt%type <ival>
135251143Sbapt	byte_size
136251143Sbapt	check_login
137251143Sbapt	form_code
138251143Sbapt	mode_code
139251143Sbapt	octal_number
140251143Sbapt	struct_code
141251143Sbapt
142251143Sbapt%type <sval>
143251143Sbapt	password
144251143Sbapt	pathname
145251143Sbapt	pathstring
146251143Sbapt	username
147251143Sbapt
148234949Sbapt%token
149234949Sbapt	A	B	C	E	F	I
150234949Sbapt	L	N	P	R	S	T
151234949Sbapt
152234949Sbapt	SP	CRLF	COMMA	STRING	NUMBER
153234949Sbapt
154234949Sbapt	USER	PASS	ACCT	REIN	QUIT	PORT
155234949Sbapt	PASV	TYPE	STRU	MODE	RETR	STOR
156234949Sbapt	APPE	MLFL	MAIL	MSND	MSOM	MSAM
157234949Sbapt	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
158234949Sbapt	ABOR	DELE	CWD	LIST	NLST	SITE
159234949Sbapt	STAT	HELP	NOOP	MKD	RMD	PWD
160234949Sbapt	CDUP	STOU	SMNT	SYST	SIZE	MDTM
161234949Sbapt
162234949Sbapt	UMASK	IDLE	CHMOD
163234949Sbapt
164234949Sbapt	LEXERR
165234949Sbapt
166234949Sbapt%start	cmd_list
167234949Sbapt
168234949Sbapt%%
169234949Sbapt
170234949Sbaptcmd_list:	/* empty */
171234949Sbapt	|	cmd_list cmd
172234949Sbapt		{
173234949Sbapt			fromname = (char *) 0;
174234949Sbapt		}
175234949Sbapt	|	cmd_list rcmd
176234949Sbapt	;
177234949Sbapt
178234949Sbaptcmd:		USER SP username CRLF
179234949Sbapt		{
180251143Sbapt			user($3);
181251143Sbapt			free($3);
182234949Sbapt		}
183234949Sbapt	|	PASS SP password CRLF
184234949Sbapt		{
185251143Sbapt			pass($3);
186251143Sbapt			free($3);
187234949Sbapt		}
188234949Sbapt	|	PORT SP host_port CRLF
189234949Sbapt		{
190234949Sbapt			usedefault = 0;
191234949Sbapt			if (pdata >= 0) {
192234949Sbapt				(void) close(pdata);
193234949Sbapt				pdata = -1;
194234949Sbapt			}
195234949Sbapt			reply(200, "PORT command successful.");
196234949Sbapt		}
197234949Sbapt	|	PASV CRLF
198234949Sbapt		{
199234949Sbapt			passive();
200234949Sbapt		}
201234949Sbapt	|	TYPE SP type_code CRLF
202234949Sbapt		{
203234949Sbapt			switch (cmd_type) {
204234949Sbapt
205234949Sbapt			case TYPE_A:
206234949Sbapt				if (cmd_form == FORM_N) {
207234949Sbapt					reply(200, "Type set to A.");
208234949Sbapt					type = cmd_type;
209234949Sbapt					form = cmd_form;
210234949Sbapt				} else
211234949Sbapt					reply(504, "Form must be N.");
212234949Sbapt				break;
213234949Sbapt
214234949Sbapt			case TYPE_E:
215234949Sbapt				reply(504, "Type E not implemented.");
216234949Sbapt				break;
217234949Sbapt
218234949Sbapt			case TYPE_I:
219234949Sbapt				reply(200, "Type set to I.");
220234949Sbapt				type = cmd_type;
221234949Sbapt				break;
222234949Sbapt
223234949Sbapt			case TYPE_L:
224234949Sbapt#if NBBY == 8
225234949Sbapt				if (cmd_bytesz == 8) {
226234949Sbapt					reply(200,
227234949Sbapt					    "Type set to L (byte size 8).");
228234949Sbapt					type = cmd_type;
229234949Sbapt				} else
230234949Sbapt					reply(504, "Byte size must be 8.");
231234949Sbapt#else /* NBBY == 8 */
232234949Sbapt				UNIMPLEMENTED for NBBY != 8
233234949Sbapt#endif /* NBBY == 8 */
234234949Sbapt			}
235234949Sbapt		}
236234949Sbapt	|	STRU SP struct_code CRLF
237234949Sbapt		{
238234949Sbapt			switch ($3) {
239234949Sbapt
240234949Sbapt			case STRU_F:
241234949Sbapt				reply(200, "STRU F ok.");
242234949Sbapt				break;
243234949Sbapt
244234949Sbapt			default:
245234949Sbapt				reply(504, "Unimplemented STRU type.");
246234949Sbapt			}
247234949Sbapt		}
248234949Sbapt	|	MODE SP mode_code CRLF
249234949Sbapt		{
250234949Sbapt			switch ($3) {
251234949Sbapt
252234949Sbapt			case MODE_S:
253234949Sbapt				reply(200, "MODE S ok.");
254234949Sbapt				break;
255234949Sbapt
256234949Sbapt			default:
257234949Sbapt				reply(502, "Unimplemented MODE type.");
258234949Sbapt			}
259234949Sbapt		}
260234949Sbapt	|	ALLO SP NUMBER CRLF
261234949Sbapt		{
262234949Sbapt			reply(202, "ALLO command ignored.");
263234949Sbapt		}
264234949Sbapt	|	ALLO SP NUMBER SP R SP NUMBER CRLF
265234949Sbapt		{
266234949Sbapt			reply(202, "ALLO command ignored.");
267234949Sbapt		}
268234949Sbapt	|	RETR check_login SP pathname CRLF
269234949Sbapt		{
270234949Sbapt			if ($2 && $4 != 0)
271251143Sbapt				retrieve((char *) 0, $4);
272234949Sbapt			if ($4 != 0)
273251143Sbapt				free($4);
274234949Sbapt		}
275234949Sbapt	|	STOR check_login SP pathname CRLF
276234949Sbapt		{
277234949Sbapt			if ($2 && $4 != 0)
278251143Sbapt				store($4, "w", 0);
279234949Sbapt			if ($4 != 0)
280251143Sbapt				free($4);
281234949Sbapt		}
282234949Sbapt	|	APPE check_login SP pathname CRLF
283234949Sbapt		{
284234949Sbapt			if ($2 && $4 != 0)
285251143Sbapt				store($4, "a", 0);
286234949Sbapt			if ($4 != 0)
287251143Sbapt				free($4);
288234949Sbapt		}
289234949Sbapt	|	NLST check_login CRLF
290234949Sbapt		{
291234949Sbapt			if ($2)
292234949Sbapt				send_file_list(".");
293234949Sbapt		}
294234949Sbapt	|	NLST check_login SP STRING CRLF
295234949Sbapt		{
296234949Sbapt			if ($2 && $4 != 0)
297234949Sbapt				send_file_list((char *) $4);
298234949Sbapt			if ($4 != 0)
299234949Sbapt				free((char *) $4);
300234949Sbapt		}
301234949Sbapt	|	LIST check_login CRLF
302234949Sbapt		{
303234949Sbapt			if ($2)
304234949Sbapt				retrieve("/bin/ls -lgA", "");
305234949Sbapt		}
306234949Sbapt	|	LIST check_login SP pathname CRLF
307234949Sbapt		{
308234949Sbapt			if ($2 && $4 != 0)
309251143Sbapt				retrieve("/bin/ls -lgA %s", $4);
310234949Sbapt			if ($4 != 0)
311251143Sbapt				free($4);
312234949Sbapt		}
313234949Sbapt	|	STAT check_login SP pathname CRLF
314234949Sbapt		{
315234949Sbapt			if ($2 && $4 != 0)
316251143Sbapt				statfilecmd($4);
317234949Sbapt			if ($4 != 0)
318251143Sbapt				free($4);
319234949Sbapt		}
320234949Sbapt	|	STAT CRLF
321234949Sbapt		{
322234949Sbapt			statcmd();
323234949Sbapt		}
324234949Sbapt	|	DELE check_login SP pathname CRLF
325234949Sbapt		{
326234949Sbapt			if ($2 && $4 != 0)
327234949Sbapt				remove((char *) $4);
328234949Sbapt			if ($4 != 0)
329234949Sbapt				free((char *) $4);
330234949Sbapt		}
331234949Sbapt	|	RNTO SP pathname CRLF
332234949Sbapt		{
333234949Sbapt			if (fromname) {
334234949Sbapt				renamecmd(fromname, (char *) $3);
335234949Sbapt				free(fromname);
336234949Sbapt				fromname = (char *) 0;
337234949Sbapt			} else {
338234949Sbapt				reply(503, "Bad sequence of commands.");
339234949Sbapt			}
340234949Sbapt			free((char *) $3);
341234949Sbapt		}
342234949Sbapt	|	ABOR CRLF
343234949Sbapt		{
344234949Sbapt			reply(225, "ABOR command successful.");
345234949Sbapt		}
346234949Sbapt	|	CWD check_login CRLF
347234949Sbapt		{
348234949Sbapt			if ($2)
349234949Sbapt				cwd(pw->pw_dir);
350234949Sbapt		}
351234949Sbapt	|	CWD check_login SP pathname CRLF
352234949Sbapt		{
353234949Sbapt			if ($2 && $4 != 0)
354234949Sbapt				cwd((char *) $4);
355234949Sbapt			if ($4 != 0)
356234949Sbapt				free((char *) $4);
357234949Sbapt		}
358234949Sbapt	|	HELP CRLF
359234949Sbapt		{
360234949Sbapt			help(cmdtab, (char *) 0);
361234949Sbapt		}
362234949Sbapt	|	HELP SP STRING CRLF
363234949Sbapt		{
364234949Sbapt			register char *cp = (char *)$3;
365234949Sbapt
366234949Sbapt			if (strncasecmp(cp, "SITE", 4) == 0) {
367234949Sbapt				cp = (char *)$3 + 4;
368234949Sbapt				if (*cp == ' ')
369234949Sbapt					cp++;
370234949Sbapt				if (*cp)
371234949Sbapt					help(sitetab, cp);
372234949Sbapt				else
373234949Sbapt					help(sitetab, (char *) 0);
374234949Sbapt			} else
375234949Sbapt				help(cmdtab, (char *) $3);
376234949Sbapt		}
377234949Sbapt	|	NOOP CRLF
378234949Sbapt		{
379234949Sbapt			reply(200, "NOOP command successful.");
380234949Sbapt		}
381234949Sbapt	|	MKD check_login SP pathname CRLF
382234949Sbapt		{
383234949Sbapt			if ($2 && $4 != 0)
384234949Sbapt				makedir((char *) $4);
385234949Sbapt			if ($4 != 0)
386234949Sbapt				free((char *) $4);
387234949Sbapt		}
388234949Sbapt	|	RMD check_login SP pathname CRLF
389234949Sbapt		{
390234949Sbapt			if ($2 && $4 != 0)
391234949Sbapt				removedir((char *) $4);
392234949Sbapt			if ($4 != 0)
393234949Sbapt				free((char *) $4);
394234949Sbapt		}
395234949Sbapt	|	PWD check_login CRLF
396234949Sbapt		{
397234949Sbapt			if ($2)
398234949Sbapt				pwd();
399234949Sbapt		}
400234949Sbapt	|	CDUP check_login CRLF
401234949Sbapt		{
402234949Sbapt			if ($2)
403234949Sbapt				cwd("..");
404234949Sbapt		}
405234949Sbapt	|	SITE SP HELP CRLF
406234949Sbapt		{
407234949Sbapt			help(sitetab, (char *) 0);
408234949Sbapt		}
409234949Sbapt	|	SITE SP HELP SP STRING CRLF
410234949Sbapt		{
411234949Sbapt			help(sitetab, (char *) $5);
412234949Sbapt		}
413234949Sbapt	|	SITE SP UMASK check_login CRLF
414234949Sbapt		{
415234949Sbapt			int oldmask;
416234949Sbapt
417234949Sbapt			if ($4) {
418234949Sbapt				oldmask = umask(0);
419234949Sbapt				(void) umask(oldmask);
420234949Sbapt				reply(200, "Current UMASK is %03o", oldmask);
421234949Sbapt			}
422234949Sbapt		}
423234949Sbapt	|	SITE SP UMASK check_login SP octal_number CRLF
424234949Sbapt		{
425234949Sbapt			int oldmask;
426234949Sbapt
427234949Sbapt			if ($4) {
428234949Sbapt				if (($6 == -1) || ($6 > 0777)) {
429234949Sbapt					reply(501, "Bad UMASK value");
430234949Sbapt				} else {
431234949Sbapt					oldmask = umask($6);
432234949Sbapt					reply(200,
433234949Sbapt					    "UMASK set to %03o (was %03o)",
434234949Sbapt					    $6, oldmask);
435234949Sbapt				}
436234949Sbapt			}
437234949Sbapt		}
438234949Sbapt	|	SITE SP CHMOD check_login SP octal_number SP pathname CRLF
439234949Sbapt		{
440234949Sbapt			if ($4 && ($8 != 0)) {
441234949Sbapt				if ($6 > 0777)
442234949Sbapt					reply(501,
443234949Sbapt				"CHMOD: Mode value must be between 0 and 0777");
444234949Sbapt				else if (chmod((char *) $8, $6) < 0)
445234949Sbapt					perror_reply(550, (char *) $8);
446234949Sbapt				else
447234949Sbapt					reply(200, "CHMOD command successful.");
448234949Sbapt			}
449234949Sbapt			if ($8 != 0)
450234949Sbapt				free((char *) $8);
451234949Sbapt		}
452234949Sbapt	|	SITE SP IDLE CRLF
453234949Sbapt		{
454234949Sbapt			reply(200,
455234949Sbapt			    "Current IDLE time limit is %d seconds; max %d",
456234949Sbapt				timeout, maxtimeout);
457234949Sbapt		}
458234949Sbapt	|	SITE SP IDLE SP NUMBER CRLF
459234949Sbapt		{
460234949Sbapt			if ($5 < 30 || $5 > maxtimeout) {
461234949Sbapt				reply(501,
462234949Sbapt			"Maximum IDLE time must be between 30 and %d seconds",
463234949Sbapt				    maxtimeout);
464234949Sbapt			} else {
465234949Sbapt				timeout = $5;
466234949Sbapt				(void) alarm((unsigned) timeout);
467234949Sbapt				reply(200,
468234949Sbapt				    "Maximum IDLE time set to %d seconds",
469234949Sbapt				    timeout);
470234949Sbapt			}
471234949Sbapt		}
472234949Sbapt	|	STOU check_login SP pathname CRLF
473234949Sbapt		{
474234949Sbapt			if ($2 && $4 != 0)
475234949Sbapt				store((char *) $4, "w", 1);
476234949Sbapt			if ($4 != 0)
477234949Sbapt				free((char *) $4);
478234949Sbapt		}
479234949Sbapt	|	SYST CRLF
480234949Sbapt		{
481234949Sbapt#ifdef unix
482234949Sbapt#ifdef BSD
483234949Sbapt			reply(215, "UNIX Type: L%d Version: BSD-%d",
484234949Sbapt				NBBY, BSD);
485234949Sbapt#else /* BSD */
486234949Sbapt			reply(215, "UNIX Type: L%d", NBBY);
487234949Sbapt#endif /* BSD */
488234949Sbapt#else /* unix */
489234949Sbapt			reply(215, "UNKNOWN Type: L%d", NBBY);
490234949Sbapt#endif /* unix */
491234949Sbapt		}
492234949Sbapt
493234949Sbapt		/*
494234949Sbapt		 * SIZE is not in RFC959, but Postel has blessed it and
495234949Sbapt		 * it will be in the updated RFC.
496234949Sbapt		 *
497234949Sbapt		 * Return size of file in a format suitable for
498234949Sbapt		 * using with RESTART (we just count bytes).
499234949Sbapt		 */
500234949Sbapt	|	SIZE check_login SP pathname CRLF
501234949Sbapt		{
502234949Sbapt			if ($2 && $4 != 0)
503234949Sbapt				sizecmd((char *) $4);
504234949Sbapt			if ($4 != 0)
505234949Sbapt				free((char *) $4);
506234949Sbapt		}
507234949Sbapt
508234949Sbapt		/*
509234949Sbapt		 * MDTM is not in RFC959, but Postel has blessed it and
510234949Sbapt		 * it will be in the updated RFC.
511234949Sbapt		 *
512234949Sbapt		 * Return modification time of file as an ISO 3307
513234949Sbapt		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
514234949Sbapt		 * where xxx is the fractional second (of any precision,
515234949Sbapt		 * not necessarily 3 digits)
516234949Sbapt		 */
517234949Sbapt	|	MDTM check_login SP pathname CRLF
518234949Sbapt		{
519234949Sbapt			if ($2 && $4 != 0) {
520234949Sbapt				struct stat stbuf;
521234949Sbapt				if (stat((char *) $4, &stbuf) < 0)
522234949Sbapt					perror_reply(550, "%s", (char *) $4);
523234949Sbapt				else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
524234949Sbapt					reply(550, "%s: not a plain file.",
525234949Sbapt						(char *) $4);
526234949Sbapt				} else {
527234949Sbapt					register struct tm *t;
528234949Sbapt					t = gmtime(&stbuf.st_mtime);
529234949Sbapt					reply(213,
530234949Sbapt					    "%04d%02d%02d%02d%02d%02d",
531234949Sbapt					    1900 + t->tm_year,
532234949Sbapt					    t->tm_mon+1, t->tm_mday,
533234949Sbapt					    t->tm_hour, t->tm_min, t->tm_sec);
534234949Sbapt				}
535234949Sbapt			}
536234949Sbapt			if ($4 != 0)
537234949Sbapt				free((char *) $4);
538234949Sbapt		}
539234949Sbapt	|	QUIT CRLF
540234949Sbapt		{
541234949Sbapt			reply(221, "Goodbye.");
542234949Sbapt			dologout(0);
543234949Sbapt		}
544234949Sbapt	|	error CRLF
545234949Sbapt		{
546234949Sbapt			yyerrok;
547234949Sbapt		}
548234949Sbapt	;
549234949Sbaptrcmd:		RNFR check_login SP pathname CRLF
550234949Sbapt		{
551234949Sbapt			if ($2 && $4) {
552234949Sbapt				fromname = renamefrom((char *) $4);
553234949Sbapt				if (fromname == (char *) 0 && $4) {
554234949Sbapt					free((char *) $4);
555234949Sbapt				}
556234949Sbapt			}
557234949Sbapt		}
558234949Sbapt	;
559234949Sbapt
560234949Sbaptusername:	STRING
561234949Sbapt	;
562234949Sbapt
563234949Sbaptpassword:	/* empty */
564234949Sbapt		{
565234949Sbapt			*(const char **)(&($$)) = "";
566234949Sbapt		}
567234949Sbapt	|	STRING
568234949Sbapt	;
569234949Sbapt
570234949Sbaptbyte_size:	NUMBER
571234949Sbapt	;
572234949Sbapt
573234949Sbapthost_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
574234949Sbapt		NUMBER COMMA NUMBER
575234949Sbapt		{
576234949Sbapt			register char *a, *p;
577234949Sbapt
578234949Sbapt			a = (char *)&data_dest.sin_addr;
579251143Sbapt			a[0] = (char) $1;
580251143Sbapt			a[1] = (char) $3;
581251143Sbapt			a[2] = (char) $5;
582251143Sbapt			a[3] = (char) $7;
583234949Sbapt			p = (char *)&data_dest.sin_port;
584251143Sbapt			p[0] = (char) $9;
585251143Sbapt			p[1] = (char) $11;
586234949Sbapt			data_dest.sin_family = AF_INET;
587234949Sbapt		}
588234949Sbapt	;
589234949Sbapt
590234949Sbaptform_code:	N
591234949Sbapt	{
592234949Sbapt		$$ = FORM_N;
593234949Sbapt	}
594234949Sbapt	|	T
595234949Sbapt	{
596234949Sbapt		$$ = FORM_T;
597234949Sbapt	}
598234949Sbapt	|	C
599234949Sbapt	{
600234949Sbapt		$$ = FORM_C;
601234949Sbapt	}
602234949Sbapt	;
603234949Sbapt
604234949Sbapttype_code:	A
605234949Sbapt	{
606234949Sbapt		cmd_type = TYPE_A;
607234949Sbapt		cmd_form = FORM_N;
608234949Sbapt	}
609234949Sbapt	|	A SP form_code
610234949Sbapt	{
611234949Sbapt		cmd_type = TYPE_A;
612234949Sbapt		cmd_form = $3;
613234949Sbapt	}
614234949Sbapt	|	E
615234949Sbapt	{
616234949Sbapt		cmd_type = TYPE_E;
617234949Sbapt		cmd_form = FORM_N;
618234949Sbapt	}
619234949Sbapt	|	E SP form_code
620234949Sbapt	{
621234949Sbapt		cmd_type = TYPE_E;
622234949Sbapt		cmd_form = $3;
623234949Sbapt	}
624234949Sbapt	|	I
625234949Sbapt	{
626234949Sbapt		cmd_type = TYPE_I;
627234949Sbapt	}
628234949Sbapt	|	L
629234949Sbapt	{
630234949Sbapt		cmd_type = TYPE_L;
631234949Sbapt		cmd_bytesz = NBBY;
632234949Sbapt	}
633234949Sbapt	|	L SP byte_size
634234949Sbapt	{
635234949Sbapt		cmd_type = TYPE_L;
636234949Sbapt		cmd_bytesz = $3;
637234949Sbapt	}
638234949Sbapt	/* this is for a bug in the BBN ftp */
639234949Sbapt	|	L byte_size
640234949Sbapt	{
641234949Sbapt		cmd_type = TYPE_L;
642234949Sbapt		cmd_bytesz = $2;
643234949Sbapt	}
644234949Sbapt	;
645234949Sbapt
646234949Sbaptstruct_code:	F
647234949Sbapt	{
648234949Sbapt		$$ = STRU_F;
649234949Sbapt	}
650234949Sbapt	|	R
651234949Sbapt	{
652234949Sbapt		$$ = STRU_R;
653234949Sbapt	}
654234949Sbapt	|	P
655234949Sbapt	{
656234949Sbapt		$$ = STRU_P;
657234949Sbapt	}
658234949Sbapt	;
659234949Sbapt
660234949Sbaptmode_code:	S
661234949Sbapt	{
662234949Sbapt		$$ = MODE_S;
663234949Sbapt	}
664234949Sbapt	|	B
665234949Sbapt	{
666234949Sbapt		$$ = MODE_B;
667234949Sbapt	}
668234949Sbapt	|	C
669234949Sbapt	{
670234949Sbapt		$$ = MODE_C;
671234949Sbapt	}
672234949Sbapt	;
673234949Sbapt
674234949Sbaptpathname:	pathstring
675234949Sbapt	{
676234949Sbapt		/*
677234949Sbapt		 * Problem: this production is used for all pathname
678234949Sbapt		 * processing, but only gives a 550 error reply.
679234949Sbapt		 * This is a valid reply in some cases but not in others.
680234949Sbapt		 */
681234949Sbapt		if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
682234949Sbapt			*(char **)&($$) = *glob((char *) $1);
683234949Sbapt			if (globerr != 0) {
684234949Sbapt				reply(550, globerr);
685234949Sbapt				$$ = 0;
686234949Sbapt			}
687234949Sbapt			free((char *) $1);
688234949Sbapt		} else
689234949Sbapt			$$ = $1;
690234949Sbapt	}
691234949Sbapt	;
692234949Sbapt
693234949Sbaptpathstring:	STRING
694234949Sbapt	;
695234949Sbapt
696234949Sbaptoctal_number:	NUMBER
697234949Sbapt	{
698234949Sbapt		register int ret, dec, multby, digit;
699234949Sbapt
700234949Sbapt		/*
701234949Sbapt		 * Convert a number that was read as decimal number
702234949Sbapt		 * to what it would be if it had been read as octal.
703234949Sbapt		 */
704234949Sbapt		dec = $1;
705234949Sbapt		multby = 1;
706234949Sbapt		ret = 0;
707234949Sbapt		while (dec) {
708234949Sbapt			digit = dec%10;
709234949Sbapt			if (digit > 7) {
710234949Sbapt				ret = -1;
711234949Sbapt				break;
712234949Sbapt			}
713234949Sbapt			ret += digit * multby;
714234949Sbapt			multby *= 8;
715234949Sbapt			dec /= 10;
716234949Sbapt		}
717234949Sbapt		$$ = ret;
718234949Sbapt	}
719234949Sbapt	;
720234949Sbapt
721234949Sbaptcheck_login:	/* empty */
722234949Sbapt	{
723234949Sbapt		if (logged_in)
724234949Sbapt			$$ = 1;
725234949Sbapt		else {
726234949Sbapt			reply(530, "Please login with USER and PASS.");
727234949Sbapt			$$ = 0;
728234949Sbapt		}
729234949Sbapt	}
730234949Sbapt	;
731234949Sbapt
732234949Sbapt%%
733234949Sbapt
734234949Sbapt#ifdef YYBYACC
735234949Sbaptextern int YYLEX_DECL();
736234949Sbapt#endif
737234949Sbapt
738234949Sbaptextern jmp_buf errcatch;
739234949Sbapt
740234949Sbaptstatic void upper(char *);
741234949Sbapt
742234949Sbapt#define	CMD	0	/* beginning of command */
743234949Sbapt#define	ARGS	1	/* expect miscellaneous arguments */
744234949Sbapt#define	STR1	2	/* expect SP followed by STRING */
745234949Sbapt#define	STR2	3	/* expect STRING */
746234949Sbapt#define	OSTR	4	/* optional SP then STRING */
747234949Sbapt#define	ZSTR1	5	/* SP then optional STRING */
748234949Sbapt#define	ZSTR2	6	/* optional STRING after SP */
749234949Sbapt#define	SITECMD	7	/* SITE command */
750234949Sbapt#define	NSTR	8	/* Number followed by a string */
751234949Sbapt
752234949Sbaptstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
753234949Sbapt	{ "USER", USER, STR1, 1,	"<sp> username" },
754234949Sbapt	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
755234949Sbapt	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
756234949Sbapt	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
757234949Sbapt	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
758234949Sbapt	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
759234949Sbapt	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
760234949Sbapt	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
761234949Sbapt	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
762234949Sbapt	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
763234949Sbapt	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
764234949Sbapt	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
765234949Sbapt	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
766234949Sbapt	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
767234949Sbapt	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
768234949Sbapt	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
769234949Sbapt	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
770234949Sbapt	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
771234949Sbapt	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
772234949Sbapt	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
773234949Sbapt	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
774234949Sbapt	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
775234949Sbapt	{ "REST", REST, ARGS, 0,	"(restart command)" },
776234949Sbapt	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
777234949Sbapt	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
778234949Sbapt	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
779234949Sbapt	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
780234949Sbapt	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
781234949Sbapt	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
782234949Sbapt	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
783234949Sbapt	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
784234949Sbapt	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
785234949Sbapt	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
786234949Sbapt	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
787234949Sbapt	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
788234949Sbapt	{ "NOOP", NOOP, ARGS, 1,	"" },
789234949Sbapt	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
790234949Sbapt	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
791234949Sbapt	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
792234949Sbapt	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
793234949Sbapt	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
794234949Sbapt	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
795234949Sbapt	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
796234949Sbapt	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
797234949Sbapt	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
798234949Sbapt	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
799234949Sbapt	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
800234949Sbapt	{ 0,   0,    0,    0,	0 }
801234949Sbapt};
802234949Sbapt
803234949Sbaptstruct tab sitetab[] = {
804234949Sbapt	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
805234949Sbapt	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
806234949Sbapt	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
807234949Sbapt	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
808234949Sbapt	{ 0,   0,    0,    0,	0 }
809234949Sbapt};
810234949Sbapt
811234949Sbaptstatic struct tab *
812234949Sbaptlookup(struct tab *p, char *cmd)
813234949Sbapt{
814234949Sbapt
815234949Sbapt	for (; p->name != 0; p++)
816234949Sbapt		if (strcmp(cmd, p->name) == 0)
817234949Sbapt			return (p);
818234949Sbapt	return (0);
819234949Sbapt}
820234949Sbapt
821234949Sbapt#include <arpa/telnet.h>
822234949Sbapt
823234949Sbapt/*
824234949Sbapt * get_line - a hacked up version of fgets to ignore TELNET escape codes.
825234949Sbapt */
826234949Sbaptstatic char *
827234949Sbaptget_line(char *s, int n, FILE *iop)
828234949Sbapt{
829234949Sbapt	register int c;
830234949Sbapt	register char *cs;
831234949Sbapt
832234949Sbapt	cs = s;
833234949Sbapt/* tmpline may contain saved command from urgent mode interruption */
834234949Sbapt	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
835234949Sbapt		*cs++ = tmpline[c];
836234949Sbapt		if (tmpline[c] == '\n') {
837234949Sbapt			*cs = '\0';
838234949Sbapt			if (debug)
839234949Sbapt				syslog(LOG_DEBUG, "command: %s", s);
840234949Sbapt			tmpline[0] = '\0';
841234949Sbapt			return(s);
842234949Sbapt		}
843234949Sbapt		if (c == 0)
844234949Sbapt			tmpline[0] = '\0';
845234949Sbapt	}
846234949Sbapt	while ((c = getc(iop)) != EOF) {
847234949Sbapt		c &= 0377;
848234949Sbapt		if (c == IAC) {
849234949Sbapt		    if ((c = getc(iop)) != EOF) {
850234949Sbapt			c &= 0377;
851234949Sbapt			switch (c) {
852234949Sbapt			case WILL:
853234949Sbapt			case WONT:
854234949Sbapt				c = getc(iop);
855234949Sbapt				printf("%c%c%c", IAC, DONT, 0377&c);
856234949Sbapt				(void) fflush(stdout);
857234949Sbapt				continue;
858234949Sbapt			case DO:
859234949Sbapt			case DONT:
860234949Sbapt				c = getc(iop);
861234949Sbapt				printf("%c%c%c", IAC, WONT, 0377&c);
862234949Sbapt				(void) fflush(stdout);
863234949Sbapt				continue;
864234949Sbapt			case IAC:
865234949Sbapt				break;
866234949Sbapt			default:
867234949Sbapt				continue;	/* ignore command */
868234949Sbapt			}
869234949Sbapt		    }
870234949Sbapt		}
871251143Sbapt		*cs++ = (char) c;
872234949Sbapt		if (--n <= 0 || c == '\n')
873234949Sbapt			break;
874234949Sbapt	}
875234949Sbapt	if (c == EOF && cs == s)
876234949Sbapt		return (0);
877234949Sbapt	*cs = '\0';
878234949Sbapt	if (debug)
879234949Sbapt		syslog(LOG_DEBUG, "command: %s", s);
880234949Sbapt	return (s);
881234949Sbapt}
882234949Sbapt
883234949Sbaptstatic void
884234949Sbapttoolong(int sig)
885234949Sbapt{
886234949Sbapt	time_t now;
887234949Sbapt
888234949Sbapt	(void) sig;
889234949Sbapt	reply(421,
890234949Sbapt	  "Timeout (%d seconds): closing control connection.", timeout);
891234949Sbapt	(void) time(&now);
892234949Sbapt	if (logging) {
893234949Sbapt		syslog(LOG_INFO,
894234949Sbapt			"User %s timed out after %d seconds at %s",
895234949Sbapt			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
896234949Sbapt	}
897234949Sbapt	dologout(1);
898234949Sbapt}
899234949Sbapt
900234949Sbaptint
901234949Sbaptyylex(void)
902234949Sbapt{
903234949Sbapt	static int cpos, state;
904234949Sbapt	register char *cp, *cp2;
905234949Sbapt	register struct tab *p;
906234949Sbapt	int n;
907234949Sbapt	char c;
908234949Sbapt
909234949Sbapt	for (;;) {
910234949Sbapt		switch (state) {
911234949Sbapt
912234949Sbapt		case CMD:
913234949Sbapt			(void) signal(SIGALRM, toolong);
914234949Sbapt			(void) alarm((unsigned) timeout);
915234949Sbapt			if (get_line(cbuf, sizeof(cbuf)-1, stdin) == 0) {
916234949Sbapt				reply(221, "You could at least say goodbye.");
917234949Sbapt				dologout(0);
918234949Sbapt			}
919234949Sbapt			(void) alarm(0);
920234949Sbapt#ifdef SETPROCTITLE
921234949Sbapt			if (strncasecmp(cbuf, "PASS", 4) != 0)
922234949Sbapt				setproctitle("%s: %s", proctitle, cbuf);
923234949Sbapt#endif /* SETPROCTITLE */
924234949Sbapt			if ((cp = strchr(cbuf, '\r'))) {
925234949Sbapt				*cp++ = '\n';
926234949Sbapt				*cp = '\0';
927234949Sbapt			}
928234949Sbapt			if ((cp = strpbrk(cbuf, " \n")))
929251143Sbapt				cpos = (int) (cp - cbuf);
930234949Sbapt			if (cpos == 0)
931234949Sbapt				cpos = 4;
932234949Sbapt			c = cbuf[cpos];
933234949Sbapt			cbuf[cpos] = '\0';
934234949Sbapt			upper(cbuf);
935234949Sbapt			p = lookup(cmdtab, cbuf);
936234949Sbapt			cbuf[cpos] = c;
937234949Sbapt			if (p != 0) {
938234949Sbapt				if (p->implemented == 0) {
939234949Sbapt					nack(p->name);
940234949Sbapt					longjmp(errcatch,0);
941234949Sbapt					/* NOTREACHED */
942234949Sbapt				}
943234949Sbapt				state = p->state;
944234949Sbapt				*(const char **)(&yylval) = p->name;
945234949Sbapt				return (p->token);
946234949Sbapt			}
947234949Sbapt			break;
948234949Sbapt
949234949Sbapt		case SITECMD:
950234949Sbapt			if (cbuf[cpos] == ' ') {
951234949Sbapt				cpos++;
952234949Sbapt				return (SP);
953234949Sbapt			}
954234949Sbapt			cp = &cbuf[cpos];
955234949Sbapt			if ((cp2 = strpbrk(cp, " \n")))
956251143Sbapt				cpos = (int) (cp2 - cbuf);
957234949Sbapt			c = cbuf[cpos];
958234949Sbapt			cbuf[cpos] = '\0';
959234949Sbapt			upper(cp);
960234949Sbapt			p = lookup(sitetab, cp);
961234949Sbapt			cbuf[cpos] = c;
962234949Sbapt			if (p != 0) {
963234949Sbapt				if (p->implemented == 0) {
964234949Sbapt					state = CMD;
965234949Sbapt					nack(p->name);
966234949Sbapt					longjmp(errcatch,0);
967234949Sbapt					/* NOTREACHED */
968234949Sbapt				}
969234949Sbapt				state = p->state;
970234949Sbapt				*(const char **)(&yylval) = p->name;
971234949Sbapt				return (p->token);
972234949Sbapt			}
973234949Sbapt			state = CMD;
974234949Sbapt			break;
975234949Sbapt
976234949Sbapt		case OSTR:
977234949Sbapt			if (cbuf[cpos] == '\n') {
978234949Sbapt				state = CMD;
979234949Sbapt				return (CRLF);
980234949Sbapt			}
981234949Sbapt			/* FALLTHROUGH */
982234949Sbapt
983234949Sbapt		case STR1:
984234949Sbapt		case ZSTR1:
985234949Sbapt		dostr1:
986234949Sbapt			if (cbuf[cpos] == ' ') {
987234949Sbapt				cpos++;
988234949Sbapt				if (state == OSTR)
989234949Sbapt					state = STR2;
990234949Sbapt				else
991234949Sbapt					++state;
992234949Sbapt				return (SP);
993234949Sbapt			}
994234949Sbapt			break;
995234949Sbapt
996234949Sbapt		case ZSTR2:
997234949Sbapt			if (cbuf[cpos] == '\n') {
998234949Sbapt				state = CMD;
999234949Sbapt				return (CRLF);
1000234949Sbapt			}
1001234949Sbapt			/* FALLTHROUGH */
1002234949Sbapt
1003234949Sbapt		case STR2:
1004234949Sbapt			cp = &cbuf[cpos];
1005251143Sbapt			n = (int) strlen(cp);
1006234949Sbapt			cpos += n - 1;
1007234949Sbapt			/*
1008234949Sbapt			 * Make sure the string is nonempty and \n terminated.
1009234949Sbapt			 */
1010234949Sbapt			if (n > 1 && cbuf[cpos] == '\n') {
1011234949Sbapt				cbuf[cpos] = '\0';
1012234949Sbapt				*(char **)&yylval = copy(cp);
1013234949Sbapt				cbuf[cpos] = '\n';
1014234949Sbapt				state = ARGS;
1015234949Sbapt				return (STRING);
1016234949Sbapt			}
1017234949Sbapt			break;
1018234949Sbapt
1019234949Sbapt		case NSTR:
1020234949Sbapt			if (cbuf[cpos] == ' ') {
1021234949Sbapt				cpos++;
1022234949Sbapt				return (SP);
1023234949Sbapt			}
1024234949Sbapt			if (isdigit(cbuf[cpos])) {
1025234949Sbapt				cp = &cbuf[cpos];
1026234949Sbapt				while (isdigit(cbuf[++cpos]))
1027234949Sbapt					;
1028234949Sbapt				c = cbuf[cpos];
1029234949Sbapt				cbuf[cpos] = '\0';
1030251143Sbapt				yylval.ival = atoi(cp);
1031234949Sbapt				cbuf[cpos] = c;
1032234949Sbapt				state = STR1;
1033234949Sbapt				return (NUMBER);
1034234949Sbapt			}
1035234949Sbapt			state = STR1;
1036234949Sbapt			goto dostr1;
1037234949Sbapt
1038234949Sbapt		case ARGS:
1039234949Sbapt			if (isdigit(cbuf[cpos])) {
1040234949Sbapt				cp = &cbuf[cpos];
1041234949Sbapt				while (isdigit(cbuf[++cpos]))
1042234949Sbapt					;
1043234949Sbapt				c = cbuf[cpos];
1044234949Sbapt				cbuf[cpos] = '\0';
1045251143Sbapt				yylval.ival = atoi(cp);
1046234949Sbapt				cbuf[cpos] = c;
1047234949Sbapt				return (NUMBER);
1048234949Sbapt			}
1049234949Sbapt			switch (cbuf[cpos++]) {
1050234949Sbapt
1051234949Sbapt			case '\n':
1052234949Sbapt				state = CMD;
1053234949Sbapt				return (CRLF);
1054234949Sbapt
1055234949Sbapt			case ' ':
1056234949Sbapt				return (SP);
1057234949Sbapt
1058234949Sbapt			case ',':
1059234949Sbapt				return (COMMA);
1060234949Sbapt
1061234949Sbapt			case 'A':
1062234949Sbapt			case 'a':
1063234949Sbapt				return (A);
1064234949Sbapt
1065234949Sbapt			case 'B':
1066234949Sbapt			case 'b':
1067234949Sbapt				return (B);
1068234949Sbapt
1069234949Sbapt			case 'C':
1070234949Sbapt			case 'c':
1071234949Sbapt				return (C);
1072234949Sbapt
1073234949Sbapt			case 'E':
1074234949Sbapt			case 'e':
1075234949Sbapt				return (E);
1076234949Sbapt
1077234949Sbapt			case 'F':
1078234949Sbapt			case 'f':
1079234949Sbapt				return (F);
1080234949Sbapt
1081234949Sbapt			case 'I':
1082234949Sbapt			case 'i':
1083234949Sbapt				return (I);
1084234949Sbapt
1085234949Sbapt			case 'L':
1086234949Sbapt			case 'l':
1087234949Sbapt				return (L);
1088234949Sbapt
1089234949Sbapt			case 'N':
1090234949Sbapt			case 'n':
1091234949Sbapt				return (N);
1092234949Sbapt
1093234949Sbapt			case 'P':
1094234949Sbapt			case 'p':
1095234949Sbapt				return (P);
1096234949Sbapt
1097234949Sbapt			case 'R':
1098234949Sbapt			case 'r':
1099234949Sbapt				return (R);
1100234949Sbapt
1101234949Sbapt			case 'S':
1102234949Sbapt			case 's':
1103234949Sbapt				return (S);
1104234949Sbapt
1105234949Sbapt			case 'T':
1106234949Sbapt			case 't':
1107234949Sbapt				return (T);
1108234949Sbapt
1109234949Sbapt			}
1110234949Sbapt			break;
1111234949Sbapt
1112234949Sbapt		default:
1113234949Sbapt			fatal("Unknown state in scanner.");
1114234949Sbapt		}
1115234949Sbapt		yyerror((char *) 0);
1116234949Sbapt		state = CMD;
1117234949Sbapt		longjmp(errcatch,0);
1118234949Sbapt	}
1119234949Sbapt}
1120234949Sbapt
1121234949Sbaptstatic void
1122234949Sbaptupper(char *s)
1123234949Sbapt{
1124234949Sbapt	while (*s != '\0') {
1125234949Sbapt		if (islower(*s))
1126234949Sbapt			*s = toupper(*s);
1127234949Sbapt		s++;
1128234949Sbapt	}
1129234949Sbapt}
1130234949Sbapt
1131234949Sbaptstatic char *
1132234949Sbaptcopy(const char *s)
1133234949Sbapt{
1134234949Sbapt	char *p;
1135234949Sbapt
1136234949Sbapt	p = (char * )malloc(strlen(s) + 1);
1137234949Sbapt	if (p == 0)
1138234949Sbapt		fatal("Ran out of memory.");
1139234949Sbapt	else
1140234949Sbapt		(void) strcpy(p, s);
1141234949Sbapt	return (p);
1142234949Sbapt}
1143234949Sbapt
1144234949Sbaptstatic void
1145234949Sbapthelp(struct tab *ctab, char *s)
1146234949Sbapt{
1147234949Sbapt	register struct tab *c;
1148234949Sbapt	register int width, NCMDS;
1149234949Sbapt	const char *help_type;
1150234949Sbapt
1151234949Sbapt	if (ctab == sitetab)
1152234949Sbapt		help_type = "SITE ";
1153234949Sbapt	else
1154234949Sbapt		help_type = "";
1155234949Sbapt	width = 0, NCMDS = 0;
1156234949Sbapt	for (c = ctab; c->name != 0; c++) {
1157251143Sbapt		int len = (int) strlen(c->name);
1158234949Sbapt
1159234949Sbapt		if (len > width)
1160234949Sbapt			width = len;
1161234949Sbapt		NCMDS++;
1162234949Sbapt	}
1163234949Sbapt	width = (width + 8) &~ 7;
1164234949Sbapt	if (s == 0) {
1165234949Sbapt		register int i, j, w;
1166234949Sbapt		int columns, lines;
1167234949Sbapt
1168234949Sbapt		lreply(214, "The following %scommands are recognized %s.",
1169234949Sbapt		    help_type, "(* =>'s unimplemented)");
1170234949Sbapt		columns = 76 / width;
1171234949Sbapt		if (columns == 0)
1172234949Sbapt			columns = 1;
1173234949Sbapt		lines = (NCMDS + columns - 1) / columns;
1174234949Sbapt		for (i = 0; i < lines; i++) {
1175234949Sbapt			printf("   ");
1176234949Sbapt			for (j = 0; j < columns; j++) {
1177234949Sbapt				c = ctab + j * lines + i;
1178234949Sbapt				assert(c->name != 0);
1179234949Sbapt				printf("%s%c", c->name,
1180234949Sbapt					c->implemented ? ' ' : '*');
1181234949Sbapt				if (c + lines >= &ctab[NCMDS])
1182234949Sbapt					break;
1183251143Sbapt				w = (int) strlen(c->name) + 1;
1184234949Sbapt				while (w < width) {
1185234949Sbapt					putchar(' ');
1186234949Sbapt					w++;
1187234949Sbapt				}
1188234949Sbapt			}
1189234949Sbapt			printf("\r\n");
1190234949Sbapt		}
1191234949Sbapt		(void) fflush(stdout);
1192234949Sbapt		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1193234949Sbapt		return;
1194234949Sbapt	}
1195234949Sbapt	upper(s);
1196234949Sbapt	c = lookup(ctab, s);
1197234949Sbapt	if (c == (struct tab *)0) {
1198234949Sbapt		reply(502, "Unknown command %s.", s);
1199234949Sbapt		return;
1200234949Sbapt	}
1201234949Sbapt	if (c->implemented)
1202234949Sbapt		reply(214, "Syntax: %s%s %s", help_type, c->name, c->help);
1203234949Sbapt	else
1204234949Sbapt		reply(214, "%s%-*s\t%s; unimplemented.", help_type, width,
1205234949Sbapt		    c->name, c->help);
1206234949Sbapt}
1207234949Sbapt
1208234949Sbaptstatic void
1209234949Sbaptsizecmd(char *filename)
1210234949Sbapt{
1211234949Sbapt	switch (type) {
1212234949Sbapt	case TYPE_L:
1213234949Sbapt	case TYPE_I: {
1214234949Sbapt		struct stat stbuf;
1215234949Sbapt		if (stat(filename, &stbuf) < 0 ||
1216234949Sbapt		    (stbuf.st_mode&S_IFMT) != S_IFREG)
1217234949Sbapt			reply(550, "%s: not a plain file.", filename);
1218234949Sbapt		else
1219234949Sbapt#ifdef HAVE_LONG_LONG
1220234949Sbapt			reply(213, "%llu", (long long) stbuf.st_size);
1221234949Sbapt#else
1222234949Sbapt			reply(213, "%lu", stbuf.st_size);
1223234949Sbapt#endif
1224234949Sbapt		break;}
1225234949Sbapt	case TYPE_A: {
1226234949Sbapt		FILE *fin;
1227234949Sbapt		register int c, count;
1228234949Sbapt		struct stat stbuf;
1229234949Sbapt		fin = fopen(filename, "r");
1230234949Sbapt		if (fin == 0) {
1231234949Sbapt			perror_reply(550, filename);
1232234949Sbapt			return;
1233234949Sbapt		}
1234234949Sbapt		if (fstat(fileno(fin), &stbuf) < 0 ||
1235234949Sbapt		    (stbuf.st_mode&S_IFMT) != S_IFREG) {
1236234949Sbapt			reply(550, "%s: not a plain file.", filename);
1237234949Sbapt			(void) fclose(fin);
1238234949Sbapt			return;
1239234949Sbapt		}
1240234949Sbapt
1241234949Sbapt		count = 0;
1242234949Sbapt		while((c=getc(fin)) != EOF) {
1243234949Sbapt			if (c == '\n')	/* will get expanded to \r\n */
1244234949Sbapt				count++;
1245234949Sbapt			count++;
1246234949Sbapt		}
1247234949Sbapt		(void) fclose(fin);
1248234949Sbapt
1249234949Sbapt		reply(213, "%ld", count);
1250234949Sbapt		break;}
1251234949Sbapt	default:
1252234949Sbapt		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1253234949Sbapt	}
1254234949Sbapt}
1255