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