|
|
1.1 ! root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.