11590Srgrimes/*
21590Srgrimes * Copyright (c) 1985, 1988 Regents of the University of California.
31590Srgrimes * All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms are permitted
61590Srgrimes * provided that the above copyright notice and this paragraph are
71590Srgrimes * duplicated in all such forms and that any documentation,
81590Srgrimes * advertising materials, and other materials related to such
91590Srgrimes * distribution and use acknowledge that the software was developed
101590Srgrimes * by the University of California, Berkeley.  The name of the
111590Srgrimes * University may not be used to endorse or promote products derived
121590Srgrimes * from this software without specific prior written permission.
131590Srgrimes * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
141590Srgrimes * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
151590Srgrimes * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
161590Srgrimes *
171590Srgrimes *	@(#)ftpcmd.y	5.20.1.1 (Berkeley) 3/2/89
181590Srgrimes */
191590Srgrimes
201590Srgrimes/*
211590Srgrimes * Grammar for FTP commands.
221590Srgrimes * See RFC 959.
231590Srgrimes */
241590Srgrimes
251590Srgrimes%{
261590Srgrimes
271590Srgrimes#ifndef lint
281590Srgrimesstatic char sccsid[] = "@(#)ftpcmd.y	5.20.1.1 (Berkeley) 3/2/89";
291590Srgrimes#endif /* not lint */
301590Srgrimes
311590Srgrimes#include <sys/param.h>
321590Srgrimes#include <sys/socket.h>
331590Srgrimes
341590Srgrimes#include <netinet/in.h>
351590Srgrimes
361590Srgrimes#include <arpa/ftp.h>
371590Srgrimes
381590Srgrimes#include <stdio.h>
391590Srgrimes#include <signal.h>
401590Srgrimes#include <ctype.h>
411590Srgrimes#include <pwd.h>
421590Srgrimes#include <setjmp.h>
431590Srgrimes#include <syslog.h>
441590Srgrimes#include <sys/stat.h>
451590Srgrimes#include <time.h>
461590Srgrimes
471590Srgrimesextern	struct sockaddr_in data_dest;
481590Srgrimesextern	int logged_in;
491590Srgrimesextern	struct passwd *pw;
501590Srgrimesextern	int guest;
511590Srgrimesextern	int logging;
521590Srgrimesextern	int type;
531590Srgrimesextern	int form;
541590Srgrimesextern	int debug;
551590Srgrimesextern	int timeout;
561590Srgrimesextern	int maxtimeout;
571590Srgrimesextern  int pdata;
581590Srgrimesextern	char hostname[], remotehost[];
591590Srgrimesextern	char proctitle[];
601590Srgrimesextern	char *globerr;
611590Srgrimesextern	int usedefault;
621590Srgrimesextern  int transflag;
631590Srgrimesextern  char tmpline[];
641590Srgrimeschar	**glob();
651590Srgrimes
661590Srgrimesstatic	int cmd_type;
671590Srgrimesstatic	int cmd_form;
681590Srgrimesstatic	int cmd_bytesz;
691590Srgrimeschar	cbuf[512];
701590Srgrimeschar	*fromname;
711590Srgrimes
721590Srgrimeschar	*index();
731590Srgrimes%}
741590Srgrimes
751590Srgrimes%token
761590Srgrimes	A	B	C	E	F	I
771590Srgrimes	L	N	P	R	S	T
781590Srgrimes
791590Srgrimes	SP	CRLF	COMMA	STRING	NUMBER
801590Srgrimes
811590Srgrimes	USER	PASS	ACCT	REIN	QUIT	PORT
821590Srgrimes	PASV	TYPE	STRU	MODE	RETR	STOR
831590Srgrimes	APPE	MLFL	MAIL	MSND	MSOM	MSAM
841590Srgrimes	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
851590Srgrimes	ABOR	DELE	CWD	LIST	NLST	SITE
861590Srgrimes	STAT	HELP	NOOP	MKD	RMD	PWD
871590Srgrimes	CDUP	STOU	SMNT	SYST	SIZE	MDTM
881590Srgrimes
891590Srgrimes	UMASK	IDLE	CHMOD
901590Srgrimes
911590Srgrimes	LEXERR
921590Srgrimes
931590Srgrimes%start	cmd_list
941590Srgrimes
951590Srgrimes%%
961590Srgrimes
971590Srgrimescmd_list:	/* empty */
981590Srgrimes	|	cmd_list cmd
991590Srgrimes		= {
1001590Srgrimes			fromname = (char *) 0;
1011590Srgrimes		}
1021590Srgrimes	|	cmd_list rcmd
1031590Srgrimes	;
1041590Srgrimes
1051590Srgrimescmd:		USER SP username CRLF
1061590Srgrimes		= {
1071590Srgrimes			user((char *) $3);
1081590Srgrimes			free((char *) $3);
1091590Srgrimes		}
1101590Srgrimes	|	PASS SP password CRLF
1111590Srgrimes		= {
1121590Srgrimes			pass((char *) $3);
1131590Srgrimes			free((char *) $3);
1141590Srgrimes		}
1151590Srgrimes	|	PORT SP host_port CRLF
1161590Srgrimes		= {
1171590Srgrimes			usedefault = 0;
1181590Srgrimes			if (pdata >= 0) {
1191590Srgrimes				(void) close(pdata);
1201590Srgrimes				pdata = -1;
1211590Srgrimes			}
1221590Srgrimes			reply(200, "PORT command successful.");
1231590Srgrimes		}
1241590Srgrimes	|	PASV CRLF
1251590Srgrimes		= {
1261590Srgrimes			passive();
1271590Srgrimes		}
1281590Srgrimes	|	TYPE SP type_code CRLF
1291590Srgrimes		= {
1301590Srgrimes			switch (cmd_type) {
1311590Srgrimes
1321590Srgrimes			case TYPE_A:
1331590Srgrimes				if (cmd_form == FORM_N) {
1341590Srgrimes					reply(200, "Type set to A.");
1351590Srgrimes					type = cmd_type;
1361590Srgrimes					form = cmd_form;
1371590Srgrimes				} else
1381590Srgrimes					reply(504, "Form must be N.");
1391590Srgrimes				break;
1401590Srgrimes
1411590Srgrimes			case TYPE_E:
1421590Srgrimes				reply(504, "Type E not implemented.");
1431590Srgrimes				break;
1441590Srgrimes
1451590Srgrimes			case TYPE_I:
1461590Srgrimes				reply(200, "Type set to I.");
1471590Srgrimes				type = cmd_type;
1481590Srgrimes				break;
1491590Srgrimes
1501590Srgrimes			case TYPE_L:
1511590Srgrimes#if NBBY == 8
1521590Srgrimes				if (cmd_bytesz == 8) {
1531590Srgrimes					reply(200,
1541590Srgrimes					    "Type set to L (byte size 8).");
1551590Srgrimes					type = cmd_type;
1561590Srgrimes				} else
1571590Srgrimes					reply(504, "Byte size must be 8.");
1581590Srgrimes#else /* NBBY == 8 */
1591590Srgrimes				UNIMPLEMENTED for NBBY != 8
1601590Srgrimes#endif /* NBBY == 8 */
1611590Srgrimes			}
1621590Srgrimes		}
1631590Srgrimes	|	STRU SP struct_code CRLF
1641590Srgrimes		= {
1651590Srgrimes			switch ($3) {
1661590Srgrimes
1671590Srgrimes			case STRU_F:
1681590Srgrimes				reply(200, "STRU F ok.");
1691590Srgrimes				break;
1701590Srgrimes
1711590Srgrimes			default:
1721590Srgrimes				reply(504, "Unimplemented STRU type.");
1731590Srgrimes			}
1741590Srgrimes		}
1751590Srgrimes	|	MODE SP mode_code CRLF
1761590Srgrimes		= {
1771590Srgrimes			switch ($3) {
1781590Srgrimes
1791590Srgrimes			case MODE_S:
1801590Srgrimes				reply(200, "MODE S ok.");
1811590Srgrimes				break;
1821590Srgrimes
1831590Srgrimes			default:
1841590Srgrimes				reply(502, "Unimplemented MODE type.");
1851590Srgrimes			}
1861590Srgrimes		}
1871590Srgrimes	|	ALLO SP NUMBER CRLF
1881590Srgrimes		= {
1891590Srgrimes			reply(202, "ALLO command ignored.");
1901590Srgrimes		}
1911590Srgrimes	|	ALLO SP NUMBER SP R SP NUMBER CRLF
1921590Srgrimes		= {
1931590Srgrimes			reply(202, "ALLO command ignored.");
1941590Srgrimes		}
1951590Srgrimes	|	RETR check_login SP pathname CRLF
1961590Srgrimes		= {
1971590Srgrimes			if ($2 && $4 != NULL)
1981590Srgrimes				retrieve((char *) 0, (char *) $4);
1991590Srgrimes			if ($4 != NULL)
2001590Srgrimes				free((char *) $4);
2011590Srgrimes		}
2021590Srgrimes	|	STOR check_login SP pathname CRLF
2031590Srgrimes		= {
2041590Srgrimes			if ($2 && $4 != NULL)
2051590Srgrimes				store((char *) $4, "w", 0);
2061590Srgrimes			if ($4 != NULL)
2071590Srgrimes				free((char *) $4);
2081590Srgrimes		}
2091590Srgrimes	|	APPE check_login SP pathname CRLF
2101590Srgrimes		= {
2111590Srgrimes			if ($2 && $4 != NULL)
2121590Srgrimes				store((char *) $4, "a", 0);
2131590Srgrimes			if ($4 != NULL)
2141590Srgrimes				free((char *) $4);
2151590Srgrimes		}
2161590Srgrimes	|	NLST check_login CRLF
2171590Srgrimes		= {
2181590Srgrimes			if ($2)
2191590Srgrimes				send_file_list(".");
2201590Srgrimes		}
2211590Srgrimes	|	NLST check_login SP STRING CRLF
2221590Srgrimes		= {
2231590Srgrimes			if ($2 && $4 != NULL)
2241590Srgrimes				send_file_list((char *) $4);
2251590Srgrimes			if ($4 != NULL)
2261590Srgrimes				free((char *) $4);
2271590Srgrimes		}
2281590Srgrimes	|	LIST check_login CRLF
2291590Srgrimes		= {
2301590Srgrimes			if ($2)
2311590Srgrimes				retrieve("/bin/ls -lgA", "");
2321590Srgrimes		}
2331590Srgrimes	|	LIST check_login SP pathname CRLF
2341590Srgrimes		= {
2351590Srgrimes			if ($2 && $4 != NULL)
2361590Srgrimes				retrieve("/bin/ls -lgA %s", (char *) $4);
2371590Srgrimes			if ($4 != NULL)
2381590Srgrimes				free((char *) $4);
2391590Srgrimes		}
2401590Srgrimes	|	STAT check_login SP pathname CRLF
2411590Srgrimes		= {
2421590Srgrimes			if ($2 && $4 != NULL)
2431590Srgrimes				statfilecmd((char *) $4);
2441590Srgrimes			if ($4 != NULL)
2451590Srgrimes				free((char *) $4);
2461590Srgrimes		}
2471590Srgrimes	|	STAT CRLF
2481590Srgrimes		= {
2491590Srgrimes			statcmd();
2501590Srgrimes		}
2511590Srgrimes	|	DELE check_login SP pathname CRLF
2521590Srgrimes		= {
2531590Srgrimes			if ($2 && $4 != NULL)
2541590Srgrimes				delete((char *) $4);
2551590Srgrimes			if ($4 != NULL)
2561590Srgrimes				free((char *) $4);
2571590Srgrimes		}
2581590Srgrimes	|	RNTO SP pathname CRLF
2591590Srgrimes		= {
2601590Srgrimes			if (fromname) {
2611590Srgrimes				renamecmd(fromname, (char *) $3);
2621590Srgrimes				free(fromname);
2631590Srgrimes				fromname = (char *) 0;
2641590Srgrimes			} else {
2651590Srgrimes				reply(503, "Bad sequence of commands.");
2661590Srgrimes			}
2671590Srgrimes			free((char *) $3);
2681590Srgrimes		}
2691590Srgrimes	|	ABOR CRLF
2701590Srgrimes		= {
2711590Srgrimes			reply(225, "ABOR command successful.");
2721590Srgrimes		}
2731590Srgrimes	|	CWD check_login CRLF
2741590Srgrimes		= {
2751590Srgrimes			if ($2)
2761590Srgrimes				cwd(pw->pw_dir);
2771590Srgrimes		}
2781590Srgrimes	|	CWD check_login SP pathname CRLF
2791590Srgrimes		= {
2801590Srgrimes			if ($2 && $4 != NULL)
2811590Srgrimes				cwd((char *) $4);
2821590Srgrimes			if ($4 != NULL)
2831590Srgrimes				free((char *) $4);
2841590Srgrimes		}
2851590Srgrimes	|	HELP CRLF
2861590Srgrimes		= {
2871590Srgrimes			help(cmdtab, (char *) 0);
2881590Srgrimes		}
2891590Srgrimes	|	HELP SP STRING CRLF
2901590Srgrimes		= {
2911590Srgrimes			register char *cp = (char *)$3;
2921590Srgrimes
2931590Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
2941590Srgrimes				cp = (char *)$3 + 4;
2951590Srgrimes				if (*cp == ' ')
2961590Srgrimes					cp++;
2971590Srgrimes				if (*cp)
2981590Srgrimes					help(sitetab, cp);
2991590Srgrimes				else
3001590Srgrimes					help(sitetab, (char *) 0);
3011590Srgrimes			} else
3021590Srgrimes				help(cmdtab, (char *) $3);
3031590Srgrimes		}
3041590Srgrimes	|	NOOP CRLF
3051590Srgrimes		= {
3061590Srgrimes			reply(200, "NOOP command successful.");
3071590Srgrimes		}
3081590Srgrimes	|	MKD check_login SP pathname CRLF
3091590Srgrimes		= {
3101590Srgrimes			if ($2 && $4 != NULL)
3111590Srgrimes				makedir((char *) $4);
3121590Srgrimes			if ($4 != NULL)
3131590Srgrimes				free((char *) $4);
3141590Srgrimes		}
3151590Srgrimes	|	RMD check_login SP pathname CRLF
3161590Srgrimes		= {
3171590Srgrimes			if ($2 && $4 != NULL)
3181590Srgrimes				removedir((char *) $4);
3191590Srgrimes			if ($4 != NULL)
3201590Srgrimes				free((char *) $4);
3211590Srgrimes		}
3221590Srgrimes	|	PWD check_login CRLF
3231590Srgrimes		= {
3241590Srgrimes			if ($2)
3251590Srgrimes				pwd();
3261590Srgrimes		}
3271590Srgrimes	|	CDUP check_login CRLF
3281590Srgrimes		= {
3291590Srgrimes			if ($2)
3301590Srgrimes				cwd("..");
3311590Srgrimes		}
3321590Srgrimes	|	SITE SP HELP CRLF
3331590Srgrimes		= {
3341590Srgrimes			help(sitetab, (char *) 0);
3351590Srgrimes		}
3361590Srgrimes	|	SITE SP HELP SP STRING CRLF
3371590Srgrimes		= {
3381590Srgrimes			help(sitetab, (char *) $5);
3391590Srgrimes		}
3401590Srgrimes	|	SITE SP UMASK check_login CRLF
3411590Srgrimes		= {
3421590Srgrimes			int oldmask;
3431590Srgrimes
3441590Srgrimes			if ($4) {
3451590Srgrimes				oldmask = umask(0);
3461590Srgrimes				(void) umask(oldmask);
3471590Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
3481590Srgrimes			}
3491590Srgrimes		}
3501590Srgrimes	|	SITE SP UMASK check_login SP octal_number CRLF
3511590Srgrimes		= {
3521590Srgrimes			int oldmask;
3531590Srgrimes
3541590Srgrimes			if ($4) {
3551590Srgrimes				if (($6 == -1) || ($6 > 0777)) {
3561590Srgrimes					reply(501, "Bad UMASK value");
3571590Srgrimes				} else {
3581590Srgrimes					oldmask = umask($6);
3591590Srgrimes					reply(200,
3601590Srgrimes					    "UMASK set to %03o (was %03o)",
3611590Srgrimes					    $6, oldmask);
3621590Srgrimes				}
3631590Srgrimes			}
3641590Srgrimes		}
3651590Srgrimes	|	SITE SP CHMOD check_login SP octal_number SP pathname CRLF
3661590Srgrimes		= {
3671590Srgrimes			if ($4 && ($8 != NULL)) {
3681590Srgrimes				if ($6 > 0777)
3691590Srgrimes					reply(501,
3701590Srgrimes				"CHMOD: Mode value must be between 0 and 0777");
3711590Srgrimes				else if (chmod((char *) $8, $6) < 0)
3721590Srgrimes					perror_reply(550, (char *) $8);
3731590Srgrimes				else
3741590Srgrimes					reply(200, "CHMOD command successful.");
3751590Srgrimes			}
3761590Srgrimes			if ($8 != NULL)
3771590Srgrimes				free((char *) $8);
3781590Srgrimes		}
3791590Srgrimes	|	SITE SP IDLE CRLF
3801590Srgrimes		= {
3811590Srgrimes			reply(200,
3821590Srgrimes			    "Current IDLE time limit is %d seconds; max %d",
3831590Srgrimes				timeout, maxtimeout);
3841590Srgrimes		}
3851590Srgrimes	|	SITE SP IDLE SP NUMBER CRLF
3861590Srgrimes		= {
3871590Srgrimes			if ($5 < 30 || $5 > maxtimeout) {
3881590Srgrimes				reply(501,
3891590Srgrimes			"Maximum IDLE time must be between 30 and %d seconds",
3901590Srgrimes				    maxtimeout);
3911590Srgrimes			} else {
3921590Srgrimes				timeout = $5;
3931590Srgrimes				(void) alarm((unsigned) timeout);
3941590Srgrimes				reply(200,
3951590Srgrimes				    "Maximum IDLE time set to %d seconds",
3961590Srgrimes				    timeout);
3971590Srgrimes			}
3981590Srgrimes		}
3991590Srgrimes	|	STOU check_login SP pathname CRLF
4001590Srgrimes		= {
4011590Srgrimes			if ($2 && $4 != NULL)
4021590Srgrimes				store((char *) $4, "w", 1);
4031590Srgrimes			if ($4 != NULL)
4041590Srgrimes				free((char *) $4);
4051590Srgrimes		}
4061590Srgrimes	|	SYST CRLF
4071590Srgrimes		= {
4081590Srgrimes#ifdef unix
4091590Srgrimes#ifdef BSD
4101590Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
4111590Srgrimes				NBBY, BSD);
4121590Srgrimes#else /* BSD */
4131590Srgrimes			reply(215, "UNIX Type: L%d", NBBY);
4141590Srgrimes#endif /* BSD */
4151590Srgrimes#else /* unix */
4161590Srgrimes			reply(215, "UNKNOWN Type: L%d", NBBY);
4171590Srgrimes#endif /* unix */
4181590Srgrimes		}
4191590Srgrimes
4201590Srgrimes		/*
4211590Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
4221590Srgrimes		 * it will be in the updated RFC.
4231590Srgrimes		 *
4241590Srgrimes		 * Return size of file in a format suitable for
4251590Srgrimes		 * using with RESTART (we just count bytes).
4261590Srgrimes		 */
4271590Srgrimes	|	SIZE check_login SP pathname CRLF
4281590Srgrimes		= {
4291590Srgrimes			if ($2 && $4 != NULL)
4301590Srgrimes				sizecmd((char *) $4);
4311590Srgrimes			if ($4 != NULL)
4321590Srgrimes				free((char *) $4);
4331590Srgrimes		}
4341590Srgrimes
4351590Srgrimes		/*
4361590Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
4371590Srgrimes		 * it will be in the updated RFC.
4381590Srgrimes		 *
4391590Srgrimes		 * Return modification time of file as an ISO 3307
4401590Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
4411590Srgrimes		 * where xxx is the fractional second (of any precision,
4421590Srgrimes		 * not necessarily 3 digits)
4431590Srgrimes		 */
4441590Srgrimes	|	MDTM check_login SP pathname CRLF
4451590Srgrimes		= {
4461590Srgrimes			if ($2 && $4 != NULL) {
4471590Srgrimes				struct stat stbuf;
4481590Srgrimes				if (stat((char *) $4, &stbuf) < 0)
4491590Srgrimes					perror_reply(550, "%s", (char *) $4);
4501590Srgrimes				else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
4511590Srgrimes					reply(550, "%s: not a plain file.",
4521590Srgrimes						(char *) $4);
4531590Srgrimes				} else {
4541590Srgrimes					register struct tm *t;
4551590Srgrimes					struct tm *gmtime();
4561590Srgrimes					t = gmtime(&stbuf.st_mtime);
4571590Srgrimes					reply(213,
45842816Sdanny					    "%d%02d%02d%02d%02d%02d",
45942816Sdanny					    t->tm_year+1900, t->tm_mon+1, t->tm_mday,
4601590Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
4611590Srgrimes				}
4621590Srgrimes			}
4631590Srgrimes			if ($4 != NULL)
4641590Srgrimes				free((char *) $4);
4651590Srgrimes		}
4661590Srgrimes	|	QUIT CRLF
4671590Srgrimes		= {
4681590Srgrimes			reply(221, "Goodbye.");
4691590Srgrimes			dologout(0);
4701590Srgrimes		}
4711590Srgrimes	|	error CRLF
4721590Srgrimes		= {
4731590Srgrimes			yyerrok;
4741590Srgrimes		}
4751590Srgrimes	;
4761590Srgrimesrcmd:		RNFR check_login SP pathname CRLF
4771590Srgrimes		= {
4781590Srgrimes			char *renamefrom();
4791590Srgrimes
4801590Srgrimes			if ($2 && $4) {
4811590Srgrimes				fromname = renamefrom((char *) $4);
4821590Srgrimes				if (fromname == (char *) 0 && $4) {
4831590Srgrimes					free((char *) $4);
4841590Srgrimes				}
4851590Srgrimes			}
4861590Srgrimes		}
4871590Srgrimes	;
4881590Srgrimes
4891590Srgrimesusername:	STRING
4901590Srgrimes	;
4911590Srgrimes
4921590Srgrimespassword:	/* empty */
4931590Srgrimes		= {
4941590Srgrimes			*(char **)&($$) = "";
4951590Srgrimes		}
4961590Srgrimes	|	STRING
4971590Srgrimes	;
4981590Srgrimes
4991590Srgrimesbyte_size:	NUMBER
5001590Srgrimes	;
5011590Srgrimes
5021590Srgrimeshost_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
5031590Srgrimes		NUMBER COMMA NUMBER
5041590Srgrimes		= {
5051590Srgrimes			register char *a, *p;
5061590Srgrimes
5071590Srgrimes			a = (char *)&data_dest.sin_addr;
5081590Srgrimes			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
5091590Srgrimes			p = (char *)&data_dest.sin_port;
5101590Srgrimes			p[0] = $9; p[1] = $11;
5111590Srgrimes			data_dest.sin_family = AF_INET;
5121590Srgrimes		}
5131590Srgrimes	;
5141590Srgrimes
5151590Srgrimesform_code:	N
5161590Srgrimes	= {
5171590Srgrimes		$$ = FORM_N;
5181590Srgrimes	}
5191590Srgrimes	|	T
5201590Srgrimes	= {
5211590Srgrimes		$$ = FORM_T;
5221590Srgrimes	}
5231590Srgrimes	|	C
5241590Srgrimes	= {
5251590Srgrimes		$$ = FORM_C;
5261590Srgrimes	}
5271590Srgrimes	;
5281590Srgrimes
5291590Srgrimestype_code:	A
5301590Srgrimes	= {
5311590Srgrimes		cmd_type = TYPE_A;
5321590Srgrimes		cmd_form = FORM_N;
5331590Srgrimes	}
5341590Srgrimes	|	A SP form_code
5351590Srgrimes	= {
5361590Srgrimes		cmd_type = TYPE_A;
5371590Srgrimes		cmd_form = $3;
5381590Srgrimes	}
5391590Srgrimes	|	E
5401590Srgrimes	= {
5411590Srgrimes		cmd_type = TYPE_E;
5421590Srgrimes		cmd_form = FORM_N;
5431590Srgrimes	}
5441590Srgrimes	|	E SP form_code
5451590Srgrimes	= {
5461590Srgrimes		cmd_type = TYPE_E;
5471590Srgrimes		cmd_form = $3;
5481590Srgrimes	}
5491590Srgrimes	|	I
5501590Srgrimes	= {
5511590Srgrimes		cmd_type = TYPE_I;
5521590Srgrimes	}
5531590Srgrimes	|	L
5541590Srgrimes	= {
5551590Srgrimes		cmd_type = TYPE_L;
5561590Srgrimes		cmd_bytesz = NBBY;
5571590Srgrimes	}
5581590Srgrimes	|	L SP byte_size
5591590Srgrimes	= {
5601590Srgrimes		cmd_type = TYPE_L;
5611590Srgrimes		cmd_bytesz = $3;
5621590Srgrimes	}
5631590Srgrimes	/* this is for a bug in the BBN ftp */
5641590Srgrimes	|	L byte_size
5651590Srgrimes	= {
5661590Srgrimes		cmd_type = TYPE_L;
5671590Srgrimes		cmd_bytesz = $2;
5681590Srgrimes	}
5691590Srgrimes	;
5701590Srgrimes
5711590Srgrimesstruct_code:	F
5721590Srgrimes	= {
5731590Srgrimes		$$ = STRU_F;
5741590Srgrimes	}
5751590Srgrimes	|	R
5761590Srgrimes	= {
5771590Srgrimes		$$ = STRU_R;
5781590Srgrimes	}
5791590Srgrimes	|	P
5801590Srgrimes	= {
5811590Srgrimes		$$ = STRU_P;
5821590Srgrimes	}
5831590Srgrimes	;
5841590Srgrimes
5851590Srgrimesmode_code:	S
5861590Srgrimes	= {
5871590Srgrimes		$$ = MODE_S;
5881590Srgrimes	}
5891590Srgrimes	|	B
5901590Srgrimes	= {
5911590Srgrimes		$$ = MODE_B;
5921590Srgrimes	}
5931590Srgrimes	|	C
5941590Srgrimes	= {
5951590Srgrimes		$$ = MODE_C;
5961590Srgrimes	}
5971590Srgrimes	;
5981590Srgrimes
5991590Srgrimespathname:	pathstring
6001590Srgrimes	= {
6011590Srgrimes		/*
6021590Srgrimes		 * Problem: this production is used for all pathname
6031590Srgrimes		 * processing, but only gives a 550 error reply.
6041590Srgrimes		 * This is a valid reply in some cases but not in others.
6051590Srgrimes		 */
6061590Srgrimes		if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
6071590Srgrimes			*(char **)&($$) = *glob((char *) $1);
6081590Srgrimes			if (globerr != NULL) {
6091590Srgrimes				reply(550, globerr);
6101590Srgrimes				$$ = NULL;
6111590Srgrimes			}
6121590Srgrimes			free((char *) $1);
6131590Srgrimes		} else
6141590Srgrimes			$$ = $1;
6151590Srgrimes	}
6161590Srgrimes	;
6171590Srgrimes
6181590Srgrimespathstring:	STRING
6191590Srgrimes	;
6201590Srgrimes
6211590Srgrimesoctal_number:	NUMBER
6221590Srgrimes	= {
6231590Srgrimes		register int ret, dec, multby, digit;
6241590Srgrimes
6251590Srgrimes		/*
6261590Srgrimes		 * Convert a number that was read as decimal number
6271590Srgrimes		 * to what it would be if it had been read as octal.
6281590Srgrimes		 */
6291590Srgrimes		dec = $1;
6301590Srgrimes		multby = 1;
6311590Srgrimes		ret = 0;
6321590Srgrimes		while (dec) {
6331590Srgrimes			digit = dec%10;
6341590Srgrimes			if (digit > 7) {
6351590Srgrimes				ret = -1;
6361590Srgrimes				break;
6371590Srgrimes			}
6381590Srgrimes			ret += digit * multby;
6391590Srgrimes			multby *= 8;
6401590Srgrimes			dec /= 10;
6411590Srgrimes		}
6421590Srgrimes		$$ = ret;
6431590Srgrimes	}
6441590Srgrimes	;
6451590Srgrimes
6461590Srgrimescheck_login:	/* empty */
6471590Srgrimes	= {
6481590Srgrimes		if (logged_in)
6491590Srgrimes			$$ = 1;
6501590Srgrimes		else {
6511590Srgrimes			reply(530, "Please login with USER and PASS.");
6521590Srgrimes			$$ = 0;
6531590Srgrimes		}
6541590Srgrimes	}
6551590Srgrimes	;
6561590Srgrimes
6571590Srgrimes%%
6581590Srgrimes
6591590Srgrimesextern jmp_buf errcatch;
6601590Srgrimes
6611590Srgrimes#define	CMD	0	/* beginning of command */
6621590Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
6631590Srgrimes#define	STR1	2	/* expect SP followed by STRING */
6641590Srgrimes#define	STR2	3	/* expect STRING */
6651590Srgrimes#define	OSTR	4	/* optional SP then STRING */
6661590Srgrimes#define	ZSTR1	5	/* SP then optional STRING */
6671590Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
6681590Srgrimes#define	SITECMD	7	/* SITE command */
6691590Srgrimes#define	NSTR	8	/* Number followed by a string */
6701590Srgrimes
6711590Srgrimesstruct tab {
6721590Srgrimes	char	*name;
6731590Srgrimes	short	token;
6741590Srgrimes	short	state;
6751590Srgrimes	short	implemented;	/* 1 if command is implemented */
6761590Srgrimes	char	*help;
6771590Srgrimes};
6781590Srgrimes
6791590Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
6801590Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
6811590Srgrimes	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
6821590Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
6831590Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
6841590Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
6851590Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
6861590Srgrimes	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
6871590Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
6881590Srgrimes	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
6891590Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
6901590Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
6911590Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
6921590Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
6931590Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
6941590Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
6951590Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
6961590Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
6971590Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
6981590Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
6991590Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
7001590Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
7011590Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
7021590Srgrimes	{ "REST", REST, ARGS, 0,	"(restart command)" },
7031590Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
7041590Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
7051590Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
7061590Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
7071590Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
7081590Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
7091590Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
7101590Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
7111590Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
7121590Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
7131590Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
7141590Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
7151590Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
7161590Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
7171590Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
7181590Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
7191590Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
7201590Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
7211590Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
7221590Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
7231590Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
7241590Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
7251590Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
7261590Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
7271590Srgrimes	{ NULL,   0,    0,    0,	0 }
7281590Srgrimes};
7291590Srgrimes
7301590Srgrimesstruct tab sitetab[] = {
7311590Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
7321590Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
7331590Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
7341590Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
7351590Srgrimes	{ NULL,   0,    0,    0,	0 }
7361590Srgrimes};
7371590Srgrimes
7381590Srgrimesstruct tab *
7391590Srgrimeslookup(p, cmd)
7401590Srgrimes	register struct tab *p;
7411590Srgrimes	char *cmd;
7421590Srgrimes{
7431590Srgrimes
7441590Srgrimes	for (; p->name != NULL; p++)
7451590Srgrimes		if (strcmp(cmd, p->name) == 0)
7461590Srgrimes			return (p);
7471590Srgrimes	return (0);
7481590Srgrimes}
7491590Srgrimes
7501590Srgrimes#include <arpa/telnet.h>
7511590Srgrimes
7521590Srgrimes/*
7531590Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
7541590Srgrimes */
7551590Srgrimeschar *
7561590Srgrimesgetline(s, n, iop)
7571590Srgrimes	char *s;
7581590Srgrimes	register FILE *iop;
7591590Srgrimes{
7601590Srgrimes	register c;
7611590Srgrimes	register char *cs;
7621590Srgrimes
7631590Srgrimes	cs = s;
7641590Srgrimes/* tmpline may contain saved command from urgent mode interruption */
7651590Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
7661590Srgrimes		*cs++ = tmpline[c];
7671590Srgrimes		if (tmpline[c] == '\n') {
7681590Srgrimes			*cs++ = '\0';
7691590Srgrimes			if (debug)
7701590Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
7711590Srgrimes			tmpline[0] = '\0';
7721590Srgrimes			return(s);
7731590Srgrimes		}
7741590Srgrimes		if (c == 0)
7751590Srgrimes			tmpline[0] = '\0';
7761590Srgrimes	}
7771590Srgrimes	while ((c = getc(iop)) != EOF) {
7781590Srgrimes		c &= 0377;
7791590Srgrimes		if (c == IAC) {
7801590Srgrimes		    if ((c = getc(iop)) != EOF) {
7811590Srgrimes			c &= 0377;
7821590Srgrimes			switch (c) {
7831590Srgrimes			case WILL:
7841590Srgrimes			case WONT:
7851590Srgrimes				c = getc(iop);
7861590Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
7871590Srgrimes				(void) fflush(stdout);
7881590Srgrimes				continue;
7891590Srgrimes			case DO:
7901590Srgrimes			case DONT:
7911590Srgrimes				c = getc(iop);
7921590Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
7931590Srgrimes				(void) fflush(stdout);
7941590Srgrimes				continue;
7951590Srgrimes			case IAC:
7961590Srgrimes				break;
7971590Srgrimes			default:
7981590Srgrimes				continue;	/* ignore command */
7991590Srgrimes			}
8001590Srgrimes		    }
8011590Srgrimes		}
8021590Srgrimes		*cs++ = c;
8031590Srgrimes		if (--n <= 0 || c == '\n')
8041590Srgrimes			break;
8051590Srgrimes	}
8061590Srgrimes	if (c == EOF && cs == s)
8071590Srgrimes		return (NULL);
8081590Srgrimes	*cs++ = '\0';
8091590Srgrimes	if (debug)
8101590Srgrimes		syslog(LOG_DEBUG, "command: %s", s);
8111590Srgrimes	return (s);
8121590Srgrimes}
8131590Srgrimes
8141590Srgrimesstatic int
8151590Srgrimestoolong()
8161590Srgrimes{
8171590Srgrimes	time_t now;
8181590Srgrimes	extern char *ctime();
8191590Srgrimes	extern time_t time();
8201590Srgrimes
8211590Srgrimes	reply(421,
8221590Srgrimes	  "Timeout (%d seconds): closing control connection.", timeout);
8231590Srgrimes	(void) time(&now);
8241590Srgrimes	if (logging) {
8251590Srgrimes		syslog(LOG_INFO,
8261590Srgrimes			"User %s timed out after %d seconds at %s",
8271590Srgrimes			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
8281590Srgrimes	}
8291590Srgrimes	dologout(1);
8301590Srgrimes}
8311590Srgrimes
8321590Srgrimesyylex()
8331590Srgrimes{
8341590Srgrimes	static int cpos, state;
8351590Srgrimes	register char *cp, *cp2;
8361590Srgrimes	register struct tab *p;
8371590Srgrimes	int n;
8381590Srgrimes	char c, *strpbrk();
8391590Srgrimes	char *copy();
8401590Srgrimes
8411590Srgrimes	for (;;) {
8421590Srgrimes		switch (state) {
8431590Srgrimes
8441590Srgrimes		case CMD:
8451590Srgrimes			(void) signal(SIGALRM, toolong);
8461590Srgrimes			(void) alarm((unsigned) timeout);
8471590Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
8481590Srgrimes				reply(221, "You could at least say goodbye.");
8491590Srgrimes				dologout(0);
8501590Srgrimes			}
8511590Srgrimes			(void) alarm(0);
8521590Srgrimes#ifdef SETPROCTITLE
8531590Srgrimes			if (strncasecmp(cbuf, "PASS", 4) != NULL)
8541590Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
8551590Srgrimes#endif /* SETPROCTITLE */
8561590Srgrimes			if ((cp = index(cbuf, '\r'))) {
8571590Srgrimes				*cp++ = '\n';
8581590Srgrimes				*cp = '\0';
8591590Srgrimes			}
8601590Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
8611590Srgrimes				cpos = cp - cbuf;
8621590Srgrimes			if (cpos == 0)
8631590Srgrimes				cpos = 4;
8641590Srgrimes			c = cbuf[cpos];
8651590Srgrimes			cbuf[cpos] = '\0';
8661590Srgrimes			upper(cbuf);
8671590Srgrimes			p = lookup(cmdtab, cbuf);
8681590Srgrimes			cbuf[cpos] = c;
8691590Srgrimes			if (p != 0) {
8701590Srgrimes				if (p->implemented == 0) {
8711590Srgrimes					nack(p->name);
8721590Srgrimes					longjmp(errcatch,0);
8731590Srgrimes					/* NOTREACHED */
8741590Srgrimes				}
8751590Srgrimes				state = p->state;
8761590Srgrimes				*(char **)&yylval = p->name;
8771590Srgrimes				return (p->token);
8781590Srgrimes			}
8791590Srgrimes			break;
8801590Srgrimes
8811590Srgrimes		case SITECMD:
8821590Srgrimes			if (cbuf[cpos] == ' ') {
8831590Srgrimes				cpos++;
8841590Srgrimes				return (SP);
8851590Srgrimes			}
8861590Srgrimes			cp = &cbuf[cpos];
8871590Srgrimes			if ((cp2 = strpbrk(cp, " \n")))
8881590Srgrimes				cpos = cp2 - cbuf;
8891590Srgrimes			c = cbuf[cpos];
8901590Srgrimes			cbuf[cpos] = '\0';
8911590Srgrimes			upper(cp);
8921590Srgrimes			p = lookup(sitetab, cp);
8931590Srgrimes			cbuf[cpos] = c;
8941590Srgrimes			if (p != 0) {
8951590Srgrimes				if (p->implemented == 0) {
8961590Srgrimes					state = CMD;
8971590Srgrimes					nack(p->name);
8981590Srgrimes					longjmp(errcatch,0);
8991590Srgrimes					/* NOTREACHED */
9001590Srgrimes				}
9011590Srgrimes				state = p->state;
9021590Srgrimes				*(char **)&yylval = p->name;
9031590Srgrimes				return (p->token);
9041590Srgrimes			}
9051590Srgrimes			state = CMD;
9061590Srgrimes			break;
9071590Srgrimes
9081590Srgrimes		case OSTR:
9091590Srgrimes			if (cbuf[cpos] == '\n') {
9101590Srgrimes				state = CMD;
9111590Srgrimes				return (CRLF);
9121590Srgrimes			}
9131590Srgrimes			/* FALLTHROUGH */
9141590Srgrimes
9151590Srgrimes		case STR1:
9161590Srgrimes		case ZSTR1:
9171590Srgrimes		dostr1:
9181590Srgrimes			if (cbuf[cpos] == ' ') {
9191590Srgrimes				cpos++;
9201590Srgrimes				state = state == OSTR ? STR2 : ++state;
9211590Srgrimes				return (SP);
9221590Srgrimes			}
9231590Srgrimes			break;
9241590Srgrimes
9251590Srgrimes		case ZSTR2:
9261590Srgrimes			if (cbuf[cpos] == '\n') {
9271590Srgrimes				state = CMD;
9281590Srgrimes				return (CRLF);
9291590Srgrimes			}
9301590Srgrimes			/* FALLTHROUGH */
9311590Srgrimes
9321590Srgrimes		case STR2:
9331590Srgrimes			cp = &cbuf[cpos];
9341590Srgrimes			n = strlen(cp);
9351590Srgrimes			cpos += n - 1;
9361590Srgrimes			/*
9371590Srgrimes			 * Make sure the string is nonempty and \n terminated.
9381590Srgrimes			 */
9391590Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
9401590Srgrimes				cbuf[cpos] = '\0';
9411590Srgrimes				*(char **)&yylval = copy(cp);
9421590Srgrimes				cbuf[cpos] = '\n';
9431590Srgrimes				state = ARGS;
9441590Srgrimes				return (STRING);
9451590Srgrimes			}
9461590Srgrimes			break;
9471590Srgrimes
9481590Srgrimes		case NSTR:
9491590Srgrimes			if (cbuf[cpos] == ' ') {
9501590Srgrimes				cpos++;
9511590Srgrimes				return (SP);
9521590Srgrimes			}
9531590Srgrimes			if (isdigit(cbuf[cpos])) {
9541590Srgrimes				cp = &cbuf[cpos];
9551590Srgrimes				while (isdigit(cbuf[++cpos]))
9561590Srgrimes					;
9571590Srgrimes				c = cbuf[cpos];
9581590Srgrimes				cbuf[cpos] = '\0';
9591590Srgrimes				yylval = atoi(cp);
9601590Srgrimes				cbuf[cpos] = c;
9611590Srgrimes				state = STR1;
9621590Srgrimes				return (NUMBER);
9631590Srgrimes			}
9641590Srgrimes			state = STR1;
9651590Srgrimes			goto dostr1;
9661590Srgrimes
9671590Srgrimes		case ARGS:
9681590Srgrimes			if (isdigit(cbuf[cpos])) {
9691590Srgrimes				cp = &cbuf[cpos];
9701590Srgrimes				while (isdigit(cbuf[++cpos]))
9711590Srgrimes					;
9721590Srgrimes				c = cbuf[cpos];
9731590Srgrimes				cbuf[cpos] = '\0';
9741590Srgrimes				yylval = atoi(cp);
9751590Srgrimes				cbuf[cpos] = c;
9761590Srgrimes				return (NUMBER);
9771590Srgrimes			}
9781590Srgrimes			switch (cbuf[cpos++]) {
9791590Srgrimes
9801590Srgrimes			case '\n':
9811590Srgrimes				state = CMD;
9821590Srgrimes				return (CRLF);
9831590Srgrimes
9841590Srgrimes			case ' ':
9851590Srgrimes				return (SP);
9861590Srgrimes
9871590Srgrimes			case ',':
9881590Srgrimes				return (COMMA);
9891590Srgrimes
9901590Srgrimes			case 'A':
9911590Srgrimes			case 'a':
9921590Srgrimes				return (A);
9931590Srgrimes
9941590Srgrimes			case 'B':
9951590Srgrimes			case 'b':
9961590Srgrimes				return (B);
9971590Srgrimes
9981590Srgrimes			case 'C':
9991590Srgrimes			case 'c':
10001590Srgrimes				return (C);
10011590Srgrimes
10021590Srgrimes			case 'E':
10031590Srgrimes			case 'e':
10041590Srgrimes				return (E);
10051590Srgrimes
10061590Srgrimes			case 'F':
10071590Srgrimes			case 'f':
10081590Srgrimes				return (F);
10091590Srgrimes
10101590Srgrimes			case 'I':
10111590Srgrimes			case 'i':
10121590Srgrimes				return (I);
10131590Srgrimes
10141590Srgrimes			case 'L':
10151590Srgrimes			case 'l':
10161590Srgrimes				return (L);
10171590Srgrimes
10181590Srgrimes			case 'N':
10191590Srgrimes			case 'n':
10201590Srgrimes				return (N);
10211590Srgrimes
10221590Srgrimes			case 'P':
10231590Srgrimes			case 'p':
10241590Srgrimes				return (P);
10251590Srgrimes
10261590Srgrimes			case 'R':
10271590Srgrimes			case 'r':
10281590Srgrimes				return (R);
10291590Srgrimes
10301590Srgrimes			case 'S':
10311590Srgrimes			case 's':
10321590Srgrimes				return (S);
10331590Srgrimes
10341590Srgrimes			case 'T':
10351590Srgrimes			case 't':
10361590Srgrimes				return (T);
10371590Srgrimes
10381590Srgrimes			}
10391590Srgrimes			break;
10401590Srgrimes
10411590Srgrimes		default:
10421590Srgrimes			fatal("Unknown state in scanner.");
10431590Srgrimes		}
10441590Srgrimes		yyerror((char *) 0);
10451590Srgrimes		state = CMD;
10461590Srgrimes		longjmp(errcatch,0);
10471590Srgrimes	}
10481590Srgrimes}
10491590Srgrimes
10501590Srgrimesupper(s)
10511590Srgrimes	register char *s;
10521590Srgrimes{
10531590Srgrimes	while (*s != '\0') {
10541590Srgrimes		if (islower(*s))
10551590Srgrimes			*s = toupper(*s);
10561590Srgrimes		s++;
10571590Srgrimes	}
10581590Srgrimes}
10591590Srgrimes
10601590Srgrimeschar *
10611590Srgrimescopy(s)
10621590Srgrimes	char *s;
10631590Srgrimes{
10641590Srgrimes	char *p;
10651590Srgrimes	extern char *malloc(), *strcpy();
10661590Srgrimes
10671590Srgrimes	p = malloc((unsigned) strlen(s) + 1);
10681590Srgrimes	if (p == NULL)
10691590Srgrimes		fatal("Ran out of memory.");
10701590Srgrimes	(void) strcpy(p, s);
10711590Srgrimes	return (p);
10721590Srgrimes}
10731590Srgrimes
10741590Srgrimeshelp(ctab, s)
10751590Srgrimes	struct tab *ctab;
10761590Srgrimes	char *s;
10771590Srgrimes{
10781590Srgrimes	register struct tab *c;
10791590Srgrimes	register int width, NCMDS;
10801590Srgrimes	char *type;
10811590Srgrimes
10821590Srgrimes	if (ctab == sitetab)
10831590Srgrimes		type = "SITE ";
10841590Srgrimes	else
10851590Srgrimes		type = "";
10861590Srgrimes	width = 0, NCMDS = 0;
10871590Srgrimes	for (c = ctab; c->name != NULL; c++) {
10881590Srgrimes		int len = strlen(c->name);
10891590Srgrimes
10901590Srgrimes		if (len > width)
10911590Srgrimes			width = len;
10921590Srgrimes		NCMDS++;
10931590Srgrimes	}
10941590Srgrimes	width = (width + 8) &~ 7;
10951590Srgrimes	if (s == 0) {
10961590Srgrimes		register int i, j, w;
10971590Srgrimes		int columns, lines;
10981590Srgrimes
10991590Srgrimes		lreply(214, "The following %scommands are recognized %s.",
11001590Srgrimes		    type, "(* =>'s unimplemented)");
11011590Srgrimes		columns = 76 / width;
11021590Srgrimes		if (columns == 0)
11031590Srgrimes			columns = 1;
11041590Srgrimes		lines = (NCMDS + columns - 1) / columns;
11051590Srgrimes		for (i = 0; i < lines; i++) {
11061590Srgrimes			printf("   ");
11071590Srgrimes			for (j = 0; j < columns; j++) {
11081590Srgrimes				c = ctab + j * lines + i;
11091590Srgrimes				printf("%s%c", c->name,
11101590Srgrimes					c->implemented ? ' ' : '*');
11111590Srgrimes				if (c + lines >= &ctab[NCMDS])
11121590Srgrimes					break;
11131590Srgrimes				w = strlen(c->name) + 1;
11141590Srgrimes				while (w < width) {
11151590Srgrimes					putchar(' ');
11161590Srgrimes					w++;
11171590Srgrimes				}
11181590Srgrimes			}
11191590Srgrimes			printf("\r\n");
11201590Srgrimes		}
11211590Srgrimes		(void) fflush(stdout);
11221590Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
11231590Srgrimes		return;
11241590Srgrimes	}
11251590Srgrimes	upper(s);
11261590Srgrimes	c = lookup(ctab, s);
11271590Srgrimes	if (c == (struct tab *)0) {
11281590Srgrimes		reply(502, "Unknown command %s.", s);
11291590Srgrimes		return;
11301590Srgrimes	}
11311590Srgrimes	if (c->implemented)
11321590Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
11331590Srgrimes	else
11341590Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
11351590Srgrimes		    c->name, c->help);
11361590Srgrimes}
11371590Srgrimes
11381590Srgrimessizecmd(filename)
11391590Srgrimeschar *filename;
11401590Srgrimes{
11411590Srgrimes	switch (type) {
11421590Srgrimes	case TYPE_L:
11431590Srgrimes	case TYPE_I: {
11441590Srgrimes		struct stat stbuf;
11451590Srgrimes		if (stat(filename, &stbuf) < 0 ||
11461590Srgrimes		    (stbuf.st_mode&S_IFMT) != S_IFREG)
11471590Srgrimes			reply(550, "%s: not a plain file.", filename);
11481590Srgrimes		else
11491590Srgrimes			reply(213, "%lu", stbuf.st_size);
11501590Srgrimes		break;}
11511590Srgrimes	case TYPE_A: {
11521590Srgrimes		FILE *fin;
11531590Srgrimes		register int c, count;
11541590Srgrimes		struct stat stbuf;
11551590Srgrimes		fin = fopen(filename, "r");
11561590Srgrimes		if (fin == NULL) {
11571590Srgrimes			perror_reply(550, filename);
11581590Srgrimes			return;
11591590Srgrimes		}
11601590Srgrimes		if (fstat(fileno(fin), &stbuf) < 0 ||
11611590Srgrimes		    (stbuf.st_mode&S_IFMT) != S_IFREG) {
11621590Srgrimes			reply(550, "%s: not a plain file.", filename);
11631590Srgrimes			(void) fclose(fin);
11641590Srgrimes			return;
11651590Srgrimes		}
11661590Srgrimes
11671590Srgrimes		count = 0;
11681590Srgrimes		while((c=getc(fin)) != EOF) {
11691590Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
11701590Srgrimes				count++;
11711590Srgrimes			count++;
11721590Srgrimes		}
11731590Srgrimes		(void) fclose(fin);
11741590Srgrimes
11751590Srgrimes		reply(213, "%ld", count);
11761590Srgrimes		break;}
11771590Srgrimes	default:
11781590Srgrimes		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
11791590Srgrimes	}
11801590Srgrimes}
1181