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

Defined functions

_copy defined in line 813; used 1 times
_getline defined in line 566; used 3 times
_help defined in line 826; used 5 times
_lookup defined in line 549; used 2 times
_toolong defined in line 624; used 1 times
_upper defined in line 803; used 3 times
_yylex defined in line 642; never used

Defined variables

_cmdtab defined in line 502; used 4 times

Defined struct's

tab defined in line 494; used 12 times

Defined macros

ARGS defined in line 489; used 15 times
CMD defined in line 488; used 3 times
OSTR defined in line 492; used 12 times
STR1 defined in line 490; used 17 times
STR2 defined in line 491; used 1 times
Last modified: 1986-05-29
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2231
Valid CSS Valid XHTML 1.0 Strict