Sync usage with man page.
[netbsd-mini2440.git] / usr.bin / yacc / test / ftp.y
blob55864a0df9ba6929ac2f27760cef308c6f2dc6f6
1 /* $NetBSD: ftp.y,v 1.5 1997/01/09 20:23:33 tls Exp $ */
3 /*
4 * Copyright (c) 1985, 1988 Regents of the University of California.
5 * All rights reserved.
7 * Redistribution and use in source and binary forms are permitted
8 * provided that the above copyright notice and this paragraph are
9 * duplicated in all such forms and that any documentation,
10 * advertising materials, and other materials related to such
11 * distribution and use acknowledge that the software was developed
12 * by the University of California, Berkeley. The name of the
13 * University may not be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 * from: @(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89
20 * $NetBSD: ftp.y,v 1.5 1997/01/09 20:23:33 tls Exp $
24 * Grammar for FTP commands.
25 * See RFC 959.
30 #ifndef lint
31 /*static char sccsid[] = "from: @(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89";*/
32 static char rcsid[] = "$NetBSD: ftp.y,v 1.5 1997/01/09 20:23:33 tls Exp $";
33 #endif /* not lint */
35 #include <sys/param.h>
36 #include <sys/socket.h>
38 #include <netinet/in.h>
40 #include <arpa/ftp.h>
42 #include <stdio.h>
43 #include <signal.h>
44 #include <ctype.h>
45 #include <pwd.h>
46 #include <setjmp.h>
47 #include <syslog.h>
48 #include <sys/stat.h>
49 #include <time.h>
51 extern struct sockaddr_in data_dest;
52 extern int logged_in;
53 extern struct passwd *pw;
54 extern int guest;
55 extern int logging;
56 extern int type;
57 extern int form;
58 extern int debug;
59 extern int timeout;
60 extern int maxtimeout;
61 extern int pdata;
62 extern char hostname[], remotehost[];
63 extern char proctitle[];
64 extern char *globerr;
65 extern int usedefault;
66 extern int transflag;
67 extern char tmpline[];
68 char **glob();
70 static int cmd_type;
71 static int cmd_form;
72 static int cmd_bytesz;
73 char cbuf[512];
74 char *fromname;
76 char *index();
79 %token
80 A B C E F I
81 L N P R S T
83 SP CRLF COMMA STRING NUMBER
85 USER PASS ACCT REIN QUIT PORT
86 PASV TYPE STRU MODE RETR STOR
87 APPE MLFL MAIL MSND MSOM MSAM
88 MRSQ MRCP ALLO REST RNFR RNTO
89 ABOR DELE CWD LIST NLST SITE
90 STAT HELP NOOP MKD RMD PWD
91 CDUP STOU SMNT SYST SIZE MDTM
93 UMASK IDLE CHMOD
95 LEXERR
97 %start cmd_list
101 cmd_list: /* empty */
102 | cmd_list cmd
104 fromname = (char *) 0;
106 | cmd_list rcmd
109 cmd: USER SP username CRLF
111 user((char *) $3);
112 free((char *) $3);
114 | PASS SP password CRLF
116 pass((char *) $3);
117 free((char *) $3);
119 | PORT SP host_port CRLF
121 usedefault = 0;
122 if (pdata >= 0) {
123 (void) close(pdata);
124 pdata = -1;
126 reply(200, "PORT command successful.");
128 | PASV CRLF
130 passive();
132 | TYPE SP type_code CRLF
134 switch (cmd_type) {
136 case TYPE_A:
137 if (cmd_form == FORM_N) {
138 reply(200, "Type set to A.");
139 type = cmd_type;
140 form = cmd_form;
141 } else
142 reply(504, "Form must be N.");
143 break;
145 case TYPE_E:
146 reply(504, "Type E not implemented.");
147 break;
149 case TYPE_I:
150 reply(200, "Type set to I.");
151 type = cmd_type;
152 break;
154 case TYPE_L:
155 #if NBBY == 8
156 if (cmd_bytesz == 8) {
157 reply(200,
158 "Type set to L (byte size 8).");
159 type = cmd_type;
160 } else
161 reply(504, "Byte size must be 8.");
162 #else /* NBBY == 8 */
163 UNIMPLEMENTED for NBBY != 8
164 #endif /* NBBY == 8 */
167 | STRU SP struct_code CRLF
169 switch ($3) {
171 case STRU_F:
172 reply(200, "STRU F ok.");
173 break;
175 default:
176 reply(504, "Unimplemented STRU type.");
179 | MODE SP mode_code CRLF
181 switch ($3) {
183 case MODE_S:
184 reply(200, "MODE S ok.");
185 break;
187 default:
188 reply(502, "Unimplemented MODE type.");
191 | ALLO SP NUMBER CRLF
193 reply(202, "ALLO command ignored.");
195 | ALLO SP NUMBER SP R SP NUMBER CRLF
197 reply(202, "ALLO command ignored.");
199 | RETR check_login SP pathname CRLF
201 if ($2 && $4 != NULL)
202 retrieve((char *) 0, (char *) $4);
203 if ($4 != NULL)
204 free((char *) $4);
206 | STOR check_login SP pathname CRLF
208 if ($2 && $4 != NULL)
209 store((char *) $4, "w", 0);
210 if ($4 != NULL)
211 free((char *) $4);
213 | APPE check_login SP pathname CRLF
215 if ($2 && $4 != NULL)
216 store((char *) $4, "a", 0);
217 if ($4 != NULL)
218 free((char *) $4);
220 | NLST check_login CRLF
222 if ($2)
223 send_file_list(".");
225 | NLST check_login SP STRING CRLF
227 if ($2 && $4 != NULL)
228 send_file_list((char *) $4);
229 if ($4 != NULL)
230 free((char *) $4);
232 | LIST check_login CRLF
234 if ($2)
235 retrieve("/bin/ls -lgA", "");
237 | LIST check_login SP pathname CRLF
239 if ($2 && $4 != NULL)
240 retrieve("/bin/ls -lgA %s", (char *) $4);
241 if ($4 != NULL)
242 free((char *) $4);
244 | STAT check_login SP pathname CRLF
246 if ($2 && $4 != NULL)
247 statfilecmd((char *) $4);
248 if ($4 != NULL)
249 free((char *) $4);
251 | STAT CRLF
253 statcmd();
255 | DELE check_login SP pathname CRLF
257 if ($2 && $4 != NULL)
258 delete((char *) $4);
259 if ($4 != NULL)
260 free((char *) $4);
262 | RNTO SP pathname CRLF
264 if (fromname) {
265 renamecmd(fromname, (char *) $3);
266 free(fromname);
267 fromname = (char *) 0;
268 } else {
269 reply(503, "Bad sequence of commands.");
271 free((char *) $3);
273 | ABOR CRLF
275 reply(225, "ABOR command successful.");
277 | CWD check_login CRLF
279 if ($2)
280 cwd(pw->pw_dir);
282 | CWD check_login SP pathname CRLF
284 if ($2 && $4 != NULL)
285 cwd((char *) $4);
286 if ($4 != NULL)
287 free((char *) $4);
289 | HELP CRLF
291 help(cmdtab, (char *) 0);
293 | HELP SP STRING CRLF
295 register char *cp = (char *)$3;
297 if (strncasecmp(cp, "SITE", 4) == 0) {
298 cp = (char *)$3 + 4;
299 if (*cp == ' ')
300 cp++;
301 if (*cp)
302 help(sitetab, cp);
303 else
304 help(sitetab, (char *) 0);
305 } else
306 help(cmdtab, (char *) $3);
308 | NOOP CRLF
310 reply(200, "NOOP command successful.");
312 | MKD check_login SP pathname CRLF
314 if ($2 && $4 != NULL)
315 makedir((char *) $4);
316 if ($4 != NULL)
317 free((char *) $4);
319 | RMD check_login SP pathname CRLF
321 if ($2 && $4 != NULL)
322 removedir((char *) $4);
323 if ($4 != NULL)
324 free((char *) $4);
326 | PWD check_login CRLF
328 if ($2)
329 pwd();
331 | CDUP check_login CRLF
333 if ($2)
334 cwd("..");
336 | SITE SP HELP CRLF
338 help(sitetab, (char *) 0);
340 | SITE SP HELP SP STRING CRLF
342 help(sitetab, (char *) $5);
344 | SITE SP UMASK check_login CRLF
346 int oldmask;
348 if ($4) {
349 oldmask = umask(0);
350 (void) umask(oldmask);
351 reply(200, "Current UMASK is %03o", oldmask);
354 | SITE SP UMASK check_login SP octal_number CRLF
356 int oldmask;
358 if ($4) {
359 if (($6 == -1) || ($6 > 0777)) {
360 reply(501, "Bad UMASK value");
361 } else {
362 oldmask = umask($6);
363 reply(200,
364 "UMASK set to %03o (was %03o)",
365 $6, oldmask);
369 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
371 if ($4 && ($8 != NULL)) {
372 if ($6 > 0777)
373 reply(501,
374 "CHMOD: Mode value must be between 0 and 0777");
375 else if (chmod((char *) $8, $6) < 0)
376 perror_reply(550, (char *) $8);
377 else
378 reply(200, "CHMOD command successful.");
380 if ($8 != NULL)
381 free((char *) $8);
383 | SITE SP IDLE CRLF
385 reply(200,
386 "Current IDLE time limit is %d seconds; max %d",
387 timeout, maxtimeout);
389 | SITE SP IDLE SP NUMBER CRLF
391 if ($5 < 30 || $5 > maxtimeout) {
392 reply(501,
393 "Maximum IDLE time must be between 30 and %d seconds",
394 maxtimeout);
395 } else {
396 timeout = $5;
397 (void) alarm((unsigned) timeout);
398 reply(200,
399 "Maximum IDLE time set to %d seconds",
400 timeout);
403 | STOU check_login SP pathname CRLF
405 if ($2 && $4 != NULL)
406 store((char *) $4, "w", 1);
407 if ($4 != NULL)
408 free((char *) $4);
410 | SYST CRLF
412 #ifdef unix
413 #ifdef BSD
414 reply(215, "UNIX Type: L%d Version: BSD-%d",
415 NBBY, BSD);
416 #else /* BSD */
417 reply(215, "UNIX Type: L%d", NBBY);
418 #endif /* BSD */
419 #else /* unix */
420 reply(215, "UNKNOWN Type: L%d", NBBY);
421 #endif /* unix */
425 * SIZE is not in RFC959, but Postel has blessed it and
426 * it will be in the updated RFC.
428 * Return size of file in a format suitable for
429 * using with RESTART (we just count bytes).
431 | SIZE check_login SP pathname CRLF
433 if ($2 && $4 != NULL)
434 sizecmd((char *) $4);
435 if ($4 != NULL)
436 free((char *) $4);
440 * MDTM is not in RFC959, but Postel has blessed it and
441 * it will be in the updated RFC.
443 * Return modification time of file as an ISO 3307
444 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
445 * where xxx is the fractional second (of any precision,
446 * not necessarily 3 digits)
448 | MDTM check_login SP pathname CRLF
450 if ($2 && $4 != NULL) {
451 struct stat stbuf;
452 if (stat((char *) $4, &stbuf) < 0)
453 perror_reply(550, "%s", (char *) $4);
454 else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
455 reply(550, "%s: not a plain file.",
456 (char *) $4);
457 } else {
458 register struct tm *t;
459 struct tm *gmtime();
460 t = gmtime(&stbuf.st_mtime);
461 reply(213,
462 "%04d%02d%02d%02d%02d%02d",
463 1900 + t->tm_year,
464 t->tm_mon+1, t->tm_mday,
465 t->tm_hour, t->tm_min, t->tm_sec);
468 if ($4 != NULL)
469 free((char *) $4);
471 | QUIT CRLF
473 reply(221, "Goodbye.");
474 dologout(0);
476 | error CRLF
478 yyerrok;
481 rcmd: RNFR check_login SP pathname CRLF
483 char *renamefrom();
485 if ($2 && $4) {
486 fromname = renamefrom((char *) $4);
487 if (fromname == (char *) 0 && $4) {
488 free((char *) $4);
494 username: STRING
497 password: /* empty */
499 *(char **)&($$) = "";
501 | STRING
504 byte_size: NUMBER
507 host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
508 NUMBER COMMA NUMBER
510 register char *a, *p;
512 a = (char *)&data_dest.sin_addr;
513 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
514 p = (char *)&data_dest.sin_port;
515 p[0] = $9; p[1] = $11;
516 data_dest.sin_family = AF_INET;
520 form_code: N
522 $$ = FORM_N;
526 $$ = FORM_T;
530 $$ = FORM_C;
534 type_code: A
536 cmd_type = TYPE_A;
537 cmd_form = FORM_N;
539 | A SP form_code
541 cmd_type = TYPE_A;
542 cmd_form = $3;
546 cmd_type = TYPE_E;
547 cmd_form = FORM_N;
549 | E SP form_code
551 cmd_type = TYPE_E;
552 cmd_form = $3;
556 cmd_type = TYPE_I;
560 cmd_type = TYPE_L;
561 cmd_bytesz = NBBY;
563 | L SP byte_size
565 cmd_type = TYPE_L;
566 cmd_bytesz = $3;
568 /* this is for a bug in the BBN ftp */
569 | L byte_size
571 cmd_type = TYPE_L;
572 cmd_bytesz = $2;
576 struct_code: F
578 $$ = STRU_F;
582 $$ = STRU_R;
586 $$ = STRU_P;
590 mode_code: S
592 $$ = MODE_S;
596 $$ = MODE_B;
600 $$ = MODE_C;
604 pathname: pathstring
607 * Problem: this production is used for all pathname
608 * processing, but only gives a 550 error reply.
609 * This is a valid reply in some cases but not in others.
611 if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
612 *(char **)&($$) = *glob((char *) $1);
613 if (globerr != NULL) {
614 reply(550, globerr);
615 $$ = NULL;
617 free((char *) $1);
618 } else
619 $$ = $1;
623 pathstring: STRING
626 octal_number: NUMBER
628 register int ret, dec, multby, digit;
631 * Convert a number that was read as decimal number
632 * to what it would be if it had been read as octal.
634 dec = $1;
635 multby = 1;
636 ret = 0;
637 while (dec) {
638 digit = dec%10;
639 if (digit > 7) {
640 ret = -1;
641 break;
643 ret += digit * multby;
644 multby *= 8;
645 dec /= 10;
647 $$ = ret;
651 check_login: /* empty */
653 if (logged_in)
654 $$ = 1;
655 else {
656 reply(530, "Please login with USER and PASS.");
657 $$ = 0;
664 extern jmp_buf errcatch;
666 #define CMD 0 /* beginning of command */
667 #define ARGS 1 /* expect miscellaneous arguments */
668 #define STR1 2 /* expect SP followed by STRING */
669 #define STR2 3 /* expect STRING */
670 #define OSTR 4 /* optional SP then STRING */
671 #define ZSTR1 5 /* SP then optional STRING */
672 #define ZSTR2 6 /* optional STRING after SP */
673 #define SITECMD 7 /* SITE command */
674 #define NSTR 8 /* Number followed by a string */
676 struct tab {
677 char *name;
678 short token;
679 short state;
680 short implemented; /* 1 if command is implemented */
681 char *help;
684 struct tab cmdtab[] = { /* In order defined in RFC 765 */
685 { "USER", USER, STR1, 1, "<sp> username" },
686 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
687 { "ACCT", ACCT, STR1, 0, "(specify account)" },
688 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
689 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
690 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
691 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
692 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
693 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
694 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
695 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
696 { "RETR", RETR, STR1, 1, "<sp> file-name" },
697 { "STOR", STOR, STR1, 1, "<sp> file-name" },
698 { "APPE", APPE, STR1, 1, "<sp> file-name" },
699 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
700 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
701 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
702 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
703 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
704 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
705 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
706 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
707 { "REST", REST, ARGS, 0, "(restart command)" },
708 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
709 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
710 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
711 { "DELE", DELE, STR1, 1, "<sp> file-name" },
712 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
713 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
714 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
715 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
716 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
717 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
718 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
719 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
720 { "NOOP", NOOP, ARGS, 1, "" },
721 { "MKD", MKD, STR1, 1, "<sp> path-name" },
722 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
723 { "RMD", RMD, STR1, 1, "<sp> path-name" },
724 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
725 { "PWD", PWD, ARGS, 1, "(return current directory)" },
726 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
727 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
728 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
729 { "STOU", STOU, STR1, 1, "<sp> file-name" },
730 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
731 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
732 { NULL, 0, 0, 0, 0 }
735 struct tab sitetab[] = {
736 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
737 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
738 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
739 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
740 { NULL, 0, 0, 0, 0 }
743 struct tab *
744 lookup(p, cmd)
745 register struct tab *p;
746 char *cmd;
749 for (; p->name != NULL; p++)
750 if (strcmp(cmd, p->name) == 0)
751 return (p);
752 return (0);
755 #include <arpa/telnet.h>
758 * getline - a hacked up version of fgets to ignore TELNET escape codes.
760 char *
761 getline(s, n, iop)
762 char *s;
763 register FILE *iop;
765 register c;
766 register char *cs;
768 cs = s;
769 /* tmpline may contain saved command from urgent mode interruption */
770 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
771 *cs++ = tmpline[c];
772 if (tmpline[c] == '\n') {
773 *cs++ = '\0';
774 if (debug)
775 syslog(LOG_DEBUG, "command: %s", s);
776 tmpline[0] = '\0';
777 return(s);
779 if (c == 0)
780 tmpline[0] = '\0';
782 while ((c = getc(iop)) != EOF) {
783 c &= 0377;
784 if (c == IAC) {
785 if ((c = getc(iop)) != EOF) {
786 c &= 0377;
787 switch (c) {
788 case WILL:
789 case WONT:
790 c = getc(iop);
791 printf("%c%c%c", IAC, DONT, 0377&c);
792 (void) fflush(stdout);
793 continue;
794 case DO:
795 case DONT:
796 c = getc(iop);
797 printf("%c%c%c", IAC, WONT, 0377&c);
798 (void) fflush(stdout);
799 continue;
800 case IAC:
801 break;
802 default:
803 continue; /* ignore command */
807 *cs++ = c;
808 if (--n <= 0 || c == '\n')
809 break;
811 if (c == EOF && cs == s)
812 return (NULL);
813 *cs++ = '\0';
814 if (debug)
815 syslog(LOG_DEBUG, "command: %s", s);
816 return (s);
819 static int
820 toolong()
822 time_t now;
823 extern char *ctime();
824 extern time_t time();
826 reply(421,
827 "Timeout (%d seconds): closing control connection.", timeout);
828 (void) time(&now);
829 if (logging) {
830 syslog(LOG_INFO,
831 "User %s timed out after %d seconds at %s",
832 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
834 dologout(1);
837 yylex()
839 static int cpos, state;
840 register char *cp, *cp2;
841 register struct tab *p;
842 int n;
843 char c, *strpbrk();
844 char *copy();
846 for (;;) {
847 switch (state) {
849 case CMD:
850 (void) signal(SIGALRM, toolong);
851 (void) alarm((unsigned) timeout);
852 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
853 reply(221, "You could at least say goodbye.");
854 dologout(0);
856 (void) alarm(0);
857 #ifdef SETPROCTITLE
858 if (strncasecmp(cbuf, "PASS", 4) != NULL)
859 setproctitle("%s: %s", proctitle, cbuf);
860 #endif /* SETPROCTITLE */
861 if ((cp = index(cbuf, '\r'))) {
862 *cp++ = '\n';
863 *cp = '\0';
865 if ((cp = strpbrk(cbuf, " \n")))
866 cpos = cp - cbuf;
867 if (cpos == 0)
868 cpos = 4;
869 c = cbuf[cpos];
870 cbuf[cpos] = '\0';
871 upper(cbuf);
872 p = lookup(cmdtab, cbuf);
873 cbuf[cpos] = c;
874 if (p != 0) {
875 if (p->implemented == 0) {
876 nack(p->name);
877 longjmp(errcatch,0);
878 /* NOTREACHED */
880 state = p->state;
881 *(char **)&yylval = p->name;
882 return (p->token);
884 break;
886 case SITECMD:
887 if (cbuf[cpos] == ' ') {
888 cpos++;
889 return (SP);
891 cp = &cbuf[cpos];
892 if ((cp2 = strpbrk(cp, " \n")))
893 cpos = cp2 - cbuf;
894 c = cbuf[cpos];
895 cbuf[cpos] = '\0';
896 upper(cp);
897 p = lookup(sitetab, cp);
898 cbuf[cpos] = c;
899 if (p != 0) {
900 if (p->implemented == 0) {
901 state = CMD;
902 nack(p->name);
903 longjmp(errcatch,0);
904 /* NOTREACHED */
906 state = p->state;
907 *(char **)&yylval = p->name;
908 return (p->token);
910 state = CMD;
911 break;
913 case OSTR:
914 if (cbuf[cpos] == '\n') {
915 state = CMD;
916 return (CRLF);
918 /* FALLTHROUGH */
920 case STR1:
921 case ZSTR1:
922 dostr1:
923 if (cbuf[cpos] == ' ') {
924 cpos++;
925 state = state == OSTR ? STR2 : ++state;
926 return (SP);
928 break;
930 case ZSTR2:
931 if (cbuf[cpos] == '\n') {
932 state = CMD;
933 return (CRLF);
935 /* FALLTHROUGH */
937 case STR2:
938 cp = &cbuf[cpos];
939 n = strlen(cp);
940 cpos += n - 1;
942 * Make sure the string is nonempty and \n terminated.
944 if (n > 1 && cbuf[cpos] == '\n') {
945 cbuf[cpos] = '\0';
946 *(char **)&yylval = copy(cp);
947 cbuf[cpos] = '\n';
948 state = ARGS;
949 return (STRING);
951 break;
953 case NSTR:
954 if (cbuf[cpos] == ' ') {
955 cpos++;
956 return (SP);
958 if (isdigit(cbuf[cpos])) {
959 cp = &cbuf[cpos];
960 while (isdigit(cbuf[++cpos]))
962 c = cbuf[cpos];
963 cbuf[cpos] = '\0';
964 yylval = atoi(cp);
965 cbuf[cpos] = c;
966 state = STR1;
967 return (NUMBER);
969 state = STR1;
970 goto dostr1;
972 case ARGS:
973 if (isdigit(cbuf[cpos])) {
974 cp = &cbuf[cpos];
975 while (isdigit(cbuf[++cpos]))
977 c = cbuf[cpos];
978 cbuf[cpos] = '\0';
979 yylval = atoi(cp);
980 cbuf[cpos] = c;
981 return (NUMBER);
983 switch (cbuf[cpos++]) {
985 case '\n':
986 state = CMD;
987 return (CRLF);
989 case ' ':
990 return (SP);
992 case ',':
993 return (COMMA);
995 case 'A':
996 case 'a':
997 return (A);
999 case 'B':
1000 case 'b':
1001 return (B);
1003 case 'C':
1004 case 'c':
1005 return (C);
1007 case 'E':
1008 case 'e':
1009 return (E);
1011 case 'F':
1012 case 'f':
1013 return (F);
1015 case 'I':
1016 case 'i':
1017 return (I);
1019 case 'L':
1020 case 'l':
1021 return (L);
1023 case 'N':
1024 case 'n':
1025 return (N);
1027 case 'P':
1028 case 'p':
1029 return (P);
1031 case 'R':
1032 case 'r':
1033 return (R);
1035 case 'S':
1036 case 's':
1037 return (S);
1039 case 'T':
1040 case 't':
1041 return (T);
1044 break;
1046 default:
1047 fatal("Unknown state in scanner.");
1049 yyerror((char *) 0);
1050 state = CMD;
1051 longjmp(errcatch,0);
1055 upper(s)
1056 register char *s;
1058 while (*s != '\0') {
1059 if (islower(*s))
1060 *s = toupper(*s);
1061 s++;
1065 char *
1066 copy(s)
1067 char *s;
1069 char *p;
1070 extern char *malloc(), *strcpy();
1072 p = malloc((unsigned) strlen(s) + 1);
1073 if (p == NULL)
1074 fatal("Ran out of memory.");
1075 (void) strcpy(p, s);
1076 return (p);
1079 help(ctab, s)
1080 struct tab *ctab;
1081 char *s;
1083 register struct tab *c;
1084 register int width, NCMDS;
1085 char *type;
1087 if (ctab == sitetab)
1088 type = "SITE ";
1089 else
1090 type = "";
1091 width = 0, NCMDS = 0;
1092 for (c = ctab; c->name != NULL; c++) {
1093 int len = strlen(c->name);
1095 if (len > width)
1096 width = len;
1097 NCMDS++;
1099 width = (width + 8) &~ 7;
1100 if (s == 0) {
1101 register int i, j, w;
1102 int columns, lines;
1104 lreply(214, "The following %scommands are recognized %s.",
1105 type, "(* =>'s unimplemented)");
1106 columns = 76 / width;
1107 if (columns == 0)
1108 columns = 1;
1109 lines = (NCMDS + columns - 1) / columns;
1110 for (i = 0; i < lines; i++) {
1111 printf(" ");
1112 for (j = 0; j < columns; j++) {
1113 c = ctab + j * lines + i;
1114 printf("%s%c", c->name,
1115 c->implemented ? ' ' : '*');
1116 if (c + lines >= &ctab[NCMDS])
1117 break;
1118 w = strlen(c->name) + 1;
1119 while (w < width) {
1120 putchar(' ');
1121 w++;
1124 printf("\r\n");
1126 (void) fflush(stdout);
1127 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1128 return;
1130 upper(s);
1131 c = lookup(ctab, s);
1132 if (c == (struct tab *)0) {
1133 reply(502, "Unknown command %s.", s);
1134 return;
1136 if (c->implemented)
1137 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1138 else
1139 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1140 c->name, c->help);
1143 sizecmd(filename)
1144 char *filename;
1146 switch (type) {
1147 case TYPE_L:
1148 case TYPE_I: {
1149 struct stat stbuf;
1150 if (stat(filename, &stbuf) < 0 ||
1151 (stbuf.st_mode&S_IFMT) != S_IFREG)
1152 reply(550, "%s: not a plain file.", filename);
1153 else
1154 reply(213, "%llu", (long long)stbuf.st_size);
1155 break;}
1156 case TYPE_A: {
1157 FILE *fin;
1158 register int c, count;
1159 struct stat stbuf;
1160 fin = fopen(filename, "r");
1161 if (fin == NULL) {
1162 perror_reply(550, filename);
1163 return;
1165 if (fstat(fileno(fin), &stbuf) < 0 ||
1166 (stbuf.st_mode&S_IFMT) != S_IFREG) {
1167 reply(550, "%s: not a plain file.", filename);
1168 (void) fclose(fin);
1169 return;
1172 count = 0;
1173 while((c=getc(fin)) != EOF) {
1174 if (c == '\n') /* will get expanded to \r\n */
1175 count++;
1176 count++;
1178 (void) fclose(fin);
1180 reply(213, "%ld", count);
1181 break;}
1182 default:
1183 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);