source: trunk/athena/etc/ftpd/ftpcmd.y @ 7298

Revision 7298, 17.6 KB checked in by vrt, 31 years ago (diff)
The RS6000 really hates redeclarations
Line 
1/*
2 * Copyright (c) 1985 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 *
17 *      @(#)ftpcmd.y    5.13 (Berkeley) 11/30/88
18 */
19
20/*
21 * Grammar for FTP commands.
22 * See RFC 765.
23 */
24
25%{
26
27#ifndef lint
28static char sccsid[] = "@(#)ftpcmd.y    5.13 (Berkeley) 11/30/88";
29#endif /* not lint */
30
31#define NULL 0
32#include <sys/types.h>
33#include <sys/socket.h>
34
35#include <netinet/in.h>
36
37#include <arpa/ftp.h>
38
39#include <stdio.h>
40#include <signal.h>
41#include <ctype.h>
42#include <pwd.h>
43#include <setjmp.h>
44#ifndef ultrix
45#include <syslog.h>
46#else
47#include <nsyslog.h>
48#endif
49#ifdef ATHENA
50#include "athena_ftpd.h"
51
52char *athena_etext;
53#endif
54#ifdef POSIX
55#include <string.h>
56#endif
57extern  struct sockaddr_in data_dest;
58extern  int logged_in;
59extern  struct passwd *pw;
60extern  int guest;
61extern  int logging;
62extern  int type;
63extern  int form;
64extern  int debug;
65extern  int timeout;
66extern  int pdata;
67extern  char hostname[];
68extern  char *globerr;
69extern  int usedefault;
70extern  int unique;
71extern  int transflag;
72extern  char tmpline[];
73char    **glob();
74
75static  int cmd_type;
76static  int cmd_form;
77static  int cmd_bytesz;
78char cbuf[512];
79char *fromname;
80extern char *bug_address;
81
82
83%}
84
85%token
86        A       B       C       E       F       I
87        L       N       P       R       S       T
88
89        SP      CRLF    COMMA   STRING  NUMBER
90
91        USER    PASS    ACCT    REIN    QUIT    PORT
92        PASV    TYPE    STRU    MODE    RETR    STOR
93        APPE    MLFL    MAIL    MSND    MSOM    MSAM
94        MRSQ    MRCP    ALLO    REST    RNFR    RNTO
95        ABOR    DELE    CWD     LIST    NLST    SITE
96        STAT    HELP    NOOP    XMKD    XRMD    XPWD
97        XCUP    STOU    ATCH
98
99        LEXERR
100
101%start  cmd_list
102
103%%
104
105cmd_list:       /* empty */
106        |       cmd_list cmd
107                = {
108                        fromname = (char *) 0;
109                }
110        |       cmd_list rcmd
111        ;
112
113cmd:            USER SP username CRLF
114                = {
115                        extern struct passwd *sgetpwnam();
116
117                        logged_in = 0;
118#ifdef ATHENA
119                        if (athena)
120                          {
121                            athena_logout(pw);
122#ifdef _IBMR2
123                            seteuid_rios(0);
124#else
125                            seteuid(0);
126#endif /* _IBMR2 */
127                            pw = NULL;
128                          }
129#endif /* ATHENA */
130                        if (strcmp((char *) $3, "ftp") == 0 ||
131                          strcmp((char *) $3, "anonymous") == 0) {
132                             if (checkuser("ftp") &&
133                                 checkftpusers("anonymous")) {
134                                  if ((pw = sgetpwnam("ftp")) != NULL) {
135                                       guest = 1;
136                                       reply(331,
137                                    "Guest login ok, send ident as password.");
138                                  } else {
139                                       reply(530, "User %s unknown.",$3);
140                                  }
141                             } else {
142                                  reply(530, "Anonymous ftp not allowed.");
143                             }
144                        } else {
145                                guest = 0;
146                                pw = sgetpwnam((char *) $3);
147                                if (pw == NULL)
148                                  reply(530, "User %s unknown.", $3);
149                                else {
150                                  if (checkuser((char *) $3))
151                                    reply(331, "Password required for %s.", $3);
152                                  else {
153                                    reply(530, "User %s access denied.", $3);
154                                    pw = NULL;
155                                  }
156                                }
157                        }
158                        free((char *) $3);
159                }
160        |       PASS SP password CRLF
161                = {
162                        pass((char *) $3);
163                        free((char *) $3);
164                }
165        |       ATCH check_login SP pathname CRLF
166                = {
167#ifdef ATHENA
168                  if (athena)
169                    {
170                        if ($2 && $4 != NULL)
171                          {
172                            athena_etext = athena_attach(pw, (char *) $4,
173                                 (athena_login == LOGIN_KERBEROS) ? 1 : 0);
174                            if (athena_etext == NULL)
175                              ack("ATCH");
176                            else
177                              reply(550, athena_etext);
178                          }
179                        if ($4 != NULL)
180                                free((char *) $4);
181                    }
182                  else
183                    reply(502, "ATCH command not enabled");
184#endif
185                }
186        |       PORT SP host_port CRLF
187                = {
188                        usedefault = 0;
189                        if (pdata > 0) {
190                                (void) close(pdata);
191                        }
192                        pdata = -1;
193                        reply(200, "PORT command successful.");
194                }
195        |       PASV CRLF
196                = {
197                        passive();
198                }
199        |       TYPE SP type_code CRLF
200                = {
201                        switch (cmd_type) {
202
203                        case TYPE_A:
204                                if (cmd_form == FORM_N) {
205                                        reply(200, "Type set to A.");
206                                        type = cmd_type;
207                                        form = cmd_form;
208                                } else
209                                        reply(504, "Form must be N.");
210                                break;
211
212                        case TYPE_E:
213                                reply(504, "Type E not implemented.");
214                                break;
215
216                        case TYPE_I:
217                                reply(200, "Type set to I.");
218                                type = cmd_type;
219                                break;
220
221                        case TYPE_L:
222                                if (cmd_bytesz == 8) {
223                                        reply(200,
224                                            "Type set to L (byte size 8).");
225                                        type = cmd_type;
226                                } else
227                                        reply(504, "Byte size must be 8.");
228                        }
229                }
230        |       STRU SP struct_code CRLF
231                = {
232                        switch ($3) {
233
234                        case STRU_F:
235                                reply(200, "STRU F ok.");
236                                break;
237
238                        default:
239                                reply(504, "Unimplemented STRU type.");
240                        }
241                }
242        |       MODE SP mode_code CRLF
243                = {
244                        switch ($3) {
245
246                        case MODE_S:
247                                reply(200, "MODE S ok.");
248                                break;
249
250                        default:
251                                reply(502, "Unimplemented MODE type.");
252                        }
253                }
254        |       ALLO SP NUMBER CRLF
255                = {
256                        reply(202, "ALLO command ignored.");
257                }
258        |       RETR check_login SP pathname CRLF
259                = {
260                        if ($2 && $4 != NULL)
261                                retrieve((char *) 0, (char *) $4);
262                        if ($4 != NULL)
263                                free((char *) $4);
264                }
265        |       STOR check_login SP pathname CRLF
266                = {
267                        if ($2 && $4 != NULL)
268                                store((char *) $4, "w");
269                        if ($4 != NULL)
270                                free((char *) $4);
271                }
272        |       APPE check_login SP pathname CRLF
273                = {
274                        if ($2 && $4 != NULL)
275                                store((char *) $4, "a");
276                        if ($4 != NULL)
277                                free((char *) $4);
278                }
279        |       NLST check_login CRLF
280                = {
281                        if ($2)
282                                retrieve("/bin/ls", "");
283                }
284        |       NLST check_login SP pathname CRLF
285                = {
286                        if ($2 && $4 != NULL)
287                                retrieve("/bin/ls %s", (char *) $4);
288                        if ($4 != NULL)
289                                free((char *) $4);
290                }
291        |       LIST check_login CRLF
292                = {
293                        if ($2)
294                                retrieve("/bin/ls -lg", "");
295                }
296        |       LIST check_login SP pathname CRLF
297                = {
298                        if ($2 && $4 != NULL)
299                                retrieve("/bin/ls -lg %s", (char *) $4);
300                        if ($4 != NULL)
301                                free((char *) $4);
302                }
303        |       DELE check_login SP pathname CRLF
304                = {
305                        if ($2 && $4 != NULL)
306                                delete((char *) $4);
307                        if ($4 != NULL)
308                                free((char *) $4);
309                }
310        |       RNTO SP pathname CRLF
311                = {
312                        if (fromname) {
313                                renamecmd(fromname, (char *) $3);
314                                free(fromname);
315                                fromname = (char *) 0;
316                        } else {
317                                reply(503, "Bad sequence of commands.");
318                        }
319                        free((char *) $3);
320                }
321        |       ABOR CRLF
322                = {
323                        reply(225, "ABOR command successful.");
324                }
325        |       CWD check_login CRLF
326                = {
327                        if ($2)
328                                cwd(pw->pw_dir);
329                }
330        |       CWD check_login SP pathname CRLF
331                = {
332                        if ($2 && $4 != NULL)
333                                cwd((char *) $4);
334                        if ($4 != NULL)
335                                free((char *) $4);
336                }
337        |       HELP CRLF
338                = {
339                        help((char *) 0);
340                }
341        |       HELP SP STRING CRLF
342                = {
343                        help((char *) $3);
344                }
345        |       NOOP CRLF
346                = {
347                        reply(200, "NOOP command successful.");
348                }
349        |       XMKD check_login SP pathname CRLF
350                = {
351                        if ($2 && $4 != NULL)
352                                makedir((char *) $4);
353                        if ($4 != NULL)
354                                free((char *) $4);
355                }
356        |       XRMD check_login SP pathname CRLF
357                = {
358                        if ($2 && $4 != NULL)
359                                removedir((char *) $4);
360                        if ($4 != NULL)
361                                free((char *) $4);
362                }
363        |       XPWD check_login CRLF
364                = {
365                        if ($2)
366                                pwd();
367                }
368        |       XCUP check_login CRLF
369                = {
370                        if ($2)
371                                cwd("..");
372                }
373        |       STOU check_login SP pathname CRLF
374                = {
375                        if ($2 && $4 != NULL) {
376                                unique++;
377                                store((char *) $4, "w");
378                                unique = 0;
379                        }
380                        if ($4 != NULL)
381                                free((char *) $4);
382                }
383        |       QUIT CRLF
384                = {
385                        reply(221, "Goodbye.");
386                        dologout(0);
387                }
388        |       error CRLF
389                = {
390                        yyerrok;
391                }
392        ;
393
394rcmd:           RNFR check_login SP pathname CRLF
395                = {
396                        char *renamefrom();
397
398                        if ($2 && $4) {
399                                fromname = renamefrom((char *) $4);
400                                if (fromname == (char *) 0 && $4) {
401                                        free((char *) $4);
402                                }
403                        }
404                }
405        ;
406               
407username:       STRING
408        ;
409
410password:       STRING
411        ;
412
413byte_size:      NUMBER
414        ;
415
416host_port:      NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
417                NUMBER COMMA NUMBER
418                = {
419                        register char *a, *p;
420
421                        a = (char *)&data_dest.sin_addr;
422                        a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
423                        p = (char *)&data_dest.sin_port;
424                        p[0] = $9; p[1] = $11;
425                        data_dest.sin_family = AF_INET;
426                }
427        ;
428
429form_code:      N
430        = {
431                $$ = FORM_N;
432        }
433        |       T
434        = {
435                $$ = FORM_T;
436        }
437        |       C
438        = {
439                $$ = FORM_C;
440        }
441        ;
442
443type_code:      A
444        = {
445                cmd_type = TYPE_A;
446                cmd_form = FORM_N;
447        }
448        |       A SP form_code
449        = {
450                cmd_type = TYPE_A;
451                cmd_form = $3;
452        }
453        |       E
454        = {
455                cmd_type = TYPE_E;
456                cmd_form = FORM_N;
457        }
458        |       E SP form_code
459        = {
460                cmd_type = TYPE_E;
461                cmd_form = $3;
462        }
463        |       I
464        = {
465                cmd_type = TYPE_I;
466        }
467        |       L
468        = {
469                cmd_type = TYPE_L;
470                cmd_bytesz = 8;
471        }
472        |       L SP byte_size
473        = {
474                cmd_type = TYPE_L;
475                cmd_bytesz = $3;
476        }
477        /* this is for a bug in the BBN ftp */
478        |       L byte_size
479        = {
480                cmd_type = TYPE_L;
481                cmd_bytesz = $2;
482        }
483        ;
484
485struct_code:    F
486        = {
487                $$ = STRU_F;
488        }
489        |       R
490        = {
491                $$ = STRU_R;
492        }
493        |       P
494        = {
495                $$ = STRU_P;
496        }
497        ;
498
499mode_code:      S
500        = {
501                $$ = MODE_S;
502        }
503        |       B
504        = {
505                $$ = MODE_B;
506        }
507        |       C
508        = {
509                $$ = MODE_C;
510        }
511        ;
512
513pathname:       pathstring
514        = {
515                /*
516                 * Problem: this production is used for all pathname
517                 * processing, but only gives a 550 error reply.
518                 * This is a valid reply in some cases but not in others.
519                 */
520                if ($1 && strncmp((char *) $1, "~", 1) == 0) {
521                        $$ = (int)*glob((char *) $1);
522                        if (globerr != NULL) {
523                                reply(550, globerr);
524                                $$ = NULL;
525                        }
526                        free((char *) $1);
527                } else
528                        $$ = $1;
529        }
530        ;
531
532pathstring:     STRING
533        ;
534
535check_login:    /* empty */
536        = {
537                if (logged_in)
538                        $$ = 1;
539                else {
540                        reply(530, "Please login with USER and PASS.");
541                        $$ = 0;
542                }
543        }
544        ;
545
546%%
547
548extern jmp_buf errcatch;
549
550#define CMD     0       /* beginning of command */
551#define ARGS    1       /* expect miscellaneous arguments */
552#define STR1    2       /* expect SP followed by STRING */
553#define STR2    3       /* expect STRING */
554#define OSTR    4       /* optional STRING */
555
556struct tab {
557        char    *name;
558        short   token;
559        short   state;
560        short   implemented;    /* 1 if command is implemented */
561        char    *help;
562};
563
564struct tab cmdtab[] = {         /* In order defined in RFC 765 */
565        { "USER", USER, STR1, 1,        "<sp> username" },
566        { "PASS", PASS, STR1, 1,        "<sp> password" },
567        { "ACCT", ACCT, STR1, 0,        "(specify account)" },
568        { "REIN", REIN, ARGS, 0,        "(reinitialize server state)" },
569        { "QUIT", QUIT, ARGS, 1,        "(terminate service)", },
570        { "PORT", PORT, ARGS, 1,        "<sp> b0, b1, b2, b3, b4" },
571        { "PASV", PASV, ARGS, 1,        "(set server in passive mode)" },
572        { "TYPE", TYPE, ARGS, 1,        "<sp> [ A | E | I | L ]" },
573        { "STRU", STRU, ARGS, 1,        "(specify file structure)" },
574        { "MODE", MODE, ARGS, 1,        "(specify transfer mode)" },
575        { "RETR", RETR, STR1, 1,        "<sp> file-name" },
576        { "STOR", STOR, STR1, 1,        "<sp> file-name" },
577        { "APPE", APPE, STR1, 1,        "<sp> file-name" },
578        { "MLFL", MLFL, OSTR, 0,        "(mail file)" },
579        { "MAIL", MAIL, OSTR, 0,        "(mail to user)" },
580        { "MSND", MSND, OSTR, 0,        "(mail send to terminal)" },
581        { "MSOM", MSOM, OSTR, 0,        "(mail send to terminal or mailbox)" },
582        { "MSAM", MSAM, OSTR, 0,        "(mail send to terminal and mailbox)" },
583        { "MRSQ", MRSQ, OSTR, 0,        "(mail recipient scheme question)" },
584        { "MRCP", MRCP, STR1, 0,        "(mail recipient)" },
585        { "ALLO", ALLO, ARGS, 1,        "allocate storage (vacuously)" },
586        { "REST", REST, STR1, 0,        "(restart command)" },
587        { "RNFR", RNFR, STR1, 1,        "<sp> file-name" },
588        { "RNTO", RNTO, STR1, 1,        "<sp> file-name" },
589        { "ABOR", ABOR, ARGS, 1,        "(abort operation)" },
590        { "DELE", DELE, STR1, 1,        "<sp> file-name" },
591        { "CWD",  CWD,  OSTR, 1,        "[ <sp> directory-name]" },
592        { "XCWD", CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
593        { "LIST", LIST, OSTR, 1,        "[ <sp> path-name ]" },
594        { "NLST", NLST, OSTR, 1,        "[ <sp> path-name ]" },
595        { "SITE", SITE, STR1, 0,        "(get site parameters)" },
596        { "STAT", STAT, OSTR, 0,        "(get server status)" },
597        { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
598        { "NOOP", NOOP, ARGS, 1,        "" },
599        { "MKD",  XMKD, STR1, 1,        "<sp> path-name" },
600        { "XMKD", XMKD, STR1, 1,        "<sp> path-name" },
601        { "RMD",  XRMD, STR1, 1,        "<sp> path-name" },
602        { "XRMD", XRMD, STR1, 1,        "<sp> path-name" },
603        { "PWD",  XPWD, ARGS, 1,        "(return current directory)" },
604        { "XPWD", XPWD, ARGS, 1,        "(return current directory)" },
605        { "CDUP", XCUP, ARGS, 1,        "(change to parent directory)" },
606        { "XCUP", XCUP, ARGS, 1,        "(change to parent directory)" },
607        { "STOU", STOU, STR1, 1,        "<sp> file-name" },
608#ifdef ATHENA
609        { "ATCH", ATCH, STR1, 1,        "<sp> filesystem-name" },
610#else
611        { "ATCH", ATCH, STR1, 0,        "(attach filsystem)" },
612#endif
613        { NULL,   0,    0,    0,        0 }
614};
615
616struct tab *
617lookup(cmd)
618        char *cmd;
619{
620        register struct tab *p;
621
622        for (p = cmdtab; p->name != NULL; p++)
623                if (strcmp(cmd, p->name) == 0)
624                        return (p);
625        return (0);
626}
627
628#include <arpa/telnet.h>
629
630/*
631 * getline - a hacked up version of fgets to ignore TELNET escape codes.
632 */
633char *
634getline(s, n, iop)
635        char *s;
636        register FILE *iop;
637{
638        register c;
639        register char *cs;
640
641        cs = s;
642/* tmpline may contain saved command from urgent mode interruption */
643        for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
644                *cs++ = tmpline[c];
645                if (tmpline[c] == '\n') {
646                        *cs++ = '\0';
647                        if (debug) {
648                                syslog(LOG_DEBUG, "FTPD: command: %s", s);
649                        }
650                        tmpline[0] = '\0';
651                        return(s);
652                }
653                if (c == 0) {
654                        tmpline[0] = '\0';
655                }
656        }
657        while (--n > 0 && (c = getc(iop)) != EOF) {
658                while ((0377&c) == IAC) {
659                        switch (0377&(c = getc(iop))) {
660                        case WILL:
661                        case WONT:
662                                c = getc(iop);
663                                printf("%c%c%c", IAC, WONT, 0377&c);
664                                (void) fflush(stdout);
665                                break;
666                        case DO:
667                        case DONT:
668                                c = getc(iop);
669                                printf("%c%c%c", IAC, DONT, 0377&c);
670                                (void) fflush(stdout);
671                                break;
672                        default:
673                                break;
674                        }
675                        c = getc(iop); /* try next character */
676                }
677                *cs++ = 0377&c;
678                if ((0377&c) == '\n')
679                        break;
680        }
681        if (c == EOF && cs == s)
682                return (NULL);
683        *cs++ = '\0';
684        if (debug) {
685                cs = strchr(s, '\r');
686                if (cs)
687                        *cs = '\0';
688                if (! strncasecmp(s, "pass", 4))
689                        syslog(LOG_DEBUG, "FTPD: command: PASS xxx");
690                else
691                        syslog(LOG_DEBUG, "FTPD: command: %s", s);
692                if (cs)
693                        *cs = '\r';
694        }
695        return (s);
696}
697
698static int
699toolong()
700{
701        time_t now;
702        extern char *ctime();
703        extern time_t time();
704
705        reply(421,
706          "Timeout (%d seconds): closing control connection.", timeout);
707        (void) time(&now);
708        if (logging) {
709                syslog(LOG_INFO,
710                        "FTPD: User %s timed out after %d seconds at %s",
711                        (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
712        }
713        dologout(1);
714}
715
716yylex()
717{
718        static int cpos, state;
719        register char *cp;
720        register struct tab *p;
721        int n;
722        char c, *strpbrk();
723
724        for (;;) {
725                switch (state) {
726
727                case CMD:
728                        (void) signal(SIGALRM, toolong);
729                        (void) alarm((unsigned) timeout);
730                        if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
731                                reply(221, "You could at least say goodbye.");
732                                dologout(0);
733                        }
734                        (void) alarm(0);
735                        if ((cp = strchr(cbuf, '\r'))) {
736                                *cp++ = '\n'; *cp = '\0';
737                        }
738                        if ((cp = strpbrk(cbuf, " \n")))
739                                cpos = cp - cbuf;
740                        if (cpos == 0) {
741                                cpos = 4;
742                        }
743                        c = cbuf[cpos];
744                        cbuf[cpos] = '\0';
745                        upper(cbuf);
746                        p = lookup(cbuf);
747                        cbuf[cpos] = c;
748                        if (p != 0) {
749                                if (p->implemented == 0) {
750                                        nack(p->name);
751                                        longjmp(errcatch,0);
752                                        /* NOTREACHED */
753                                }
754                                state = p->state;
755                                yylval = (int) p->name;
756                                return (p->token);
757                        }
758                        break;
759
760                case OSTR:
761                        if (cbuf[cpos] == '\n') {
762                                state = CMD;
763                                return (CRLF);
764                        }
765                        /* FALL THRU */
766
767                case STR1:
768                        if (cbuf[cpos] == ' ') {
769                                cpos++;
770                                state = STR2;
771                                return (SP);
772                        }
773                        break;
774
775                case STR2:
776                        cp = &cbuf[cpos];
777                        n = strlen(cp);
778                        cpos += n - 1;
779                        /*
780                         * Make sure the string is nonempty and \n terminated.
781                         */
782                        if (n > 1 && cbuf[cpos] == '\n') {
783                                cbuf[cpos] = '\0';
784                                yylval = copy(cp);
785                                cbuf[cpos] = '\n';
786                                state = ARGS;
787                                return (STRING);
788                        }
789                        break;
790
791                case ARGS:
792                        if (isdigit(cbuf[cpos])) {
793                                cp = &cbuf[cpos];
794                                while (isdigit(cbuf[++cpos]))
795                                        ;
796                                c = cbuf[cpos];
797                                cbuf[cpos] = '\0';
798                                yylval = atoi(cp);
799                                cbuf[cpos] = c;
800                                return (NUMBER);
801                        }
802                        switch (cbuf[cpos++]) {
803
804                        case '\n':
805                                state = CMD;
806                                return (CRLF);
807
808                        case ' ':
809                                return (SP);
810
811                        case ',':
812                                return (COMMA);
813
814                        case 'A':
815                        case 'a':
816                                return (A);
817
818                        case 'B':
819                        case 'b':
820                                return (B);
821
822                        case 'C':
823                        case 'c':
824                                return (C);
825
826                        case 'E':
827                        case 'e':
828                                return (E);
829
830                        case 'F':
831                        case 'f':
832                                return (F);
833
834                        case 'I':
835                        case 'i':
836                                return (I);
837
838                        case 'L':
839                        case 'l':
840                                return (L);
841
842                        case 'N':
843                        case 'n':
844                                return (N);
845
846                        case 'P':
847                        case 'p':
848                                return (P);
849
850                        case 'R':
851                        case 'r':
852                                return (R);
853
854                        case 'S':
855                        case 's':
856                                return (S);
857
858                        case 'T':
859                        case 't':
860                                return (T);
861
862                        }
863                        break;
864
865                default:
866                        fatal("Unknown state in scanner.");
867                }
868                yyerror((char *) 0);
869                state = CMD;
870                longjmp(errcatch,0);
871        }
872}
873
874upper(s)
875        register char *s;
876{
877        while (*s != '\0') {
878                if (islower(*s))
879                        *s = toupper(*s);
880                s++;
881        }
882}
883
884copy(s)
885        char *s;
886{
887        char *p;
888#ifndef _AIX
889        extern char *malloc(), *strcpy();
890#endif
891
892        p = malloc((unsigned) strlen(s) + 1);
893        if (p == NULL)
894                fatal("Ran out of memory.");
895        (void) strcpy(p, s);
896        return ((int)p);
897}
898
899help(s)
900        char *s;
901{
902        register struct tab *c;
903        register int width, NCMDS;
904
905        width = 0, NCMDS = 0;
906        for (c = cmdtab; c->name != NULL; c++) {
907                int len = strlen(c->name) + 1;
908
909                if (len > width)
910                        width = len;
911                NCMDS++;
912        }
913        width = (width + 8) &~ 7;
914        if (s == 0) {
915                register int i, j, w;
916                int columns, lines;
917
918                lreply(214,
919          "The following commands are recognized (* =>'s unimplemented).");
920                columns = 76 / width;
921                if (columns == 0)
922                        columns = 1;
923                lines = (NCMDS + columns - 1) / columns;
924                for (i = 0; i < lines; i++) {
925                        printf("   ");
926                        for (j = 0; j < columns; j++) {
927                                c = cmdtab + j * lines + i;
928                                printf("%s%c", c->name,
929                                        c->implemented ? ' ' : '*');
930                                if (c + lines >= &cmdtab[NCMDS])
931                                        break;
932                                w = strlen(c->name) + 1;
933                                while (w < width) {
934                                        putchar(' ');
935                                        w++;
936                                }
937                        }
938                        printf("\r\n");
939                }
940                (void) fflush(stdout);
941                if (bug_address) {
942                        reply(214, "Direct comments to %s.", bug_address);
943                }
944                else {
945                        reply(214, "Direct comments to ftp-bugs@%s.",
946                              hostname);
947                }
948                return;
949        }
950        upper(s);
951        c = lookup(s);
952        if (c == (struct tab *)0) {
953                reply(502, "Unknown command %s.", s);
954                return;
955        }
956        if (c->implemented)
957                reply(214, "Syntax: %s %s", c->name, c->help);
958        else
959                reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
960}
Note: See TracBrowser for help on using the repository browser.