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