|
|
1.1 ! root 1: /* ftpcmd.y - grammar for FTP commands */ ! 2: ! 3: /* ! 4: * $Header: /f/osi/ftp-ftam/RCS/ftpcmd.y,v 7.0 89/11/23 21:55:19 mrose Rel $ ! 5: * ! 6: * $Log: ftpcmd.y,v $ ! 7: * Revision 7.0 89/11/23 21:55:19 mrose ! 8: * Release 6.0 ! 9: * ! 10: */ ! 11: ! 12: /* ! 13: * NOTICE ! 14: * ! 15: * Acquisition, use, and distribution of this module and related ! 16: * materials are subject to the restrictions of a license agreement. ! 17: * Consult the Preface in the User's Manual for the full terms of ! 18: * this agreement. ! 19: * ! 20: */ ! 21: ! 22: ! 23: /* ! 24: * Shamelessly taken from UCB ! 25: */ ! 26: ! 27: /* ! 28: * Grammar for FTP commands. ! 29: * See RFC 765. ! 30: */ ! 31: ! 32: %{ ! 33: ! 34: #ifndef lint ! 35: static char *rcsid = "$Header: /f/osi/ftp-ftam/RCS/ftpcmd.y,v 7.0 89/11/23 21:55:19 mrose Rel $"; ! 36: #endif ! 37: ! 38: #include "config.h" ! 39: #include "internet.h" ! 40: ! 41: #include <arpa/ftp.h> ! 42: ! 43: #include <stdio.h> ! 44: #include <signal.h> ! 45: #include <ctype.h> ! 46: #include <pwd.h> ! 47: #include <setjmp.h> ! 48: #include "manifest.h" ! 49: #include "general.h" ! 50: void advise(); ! 51: time_t time (); ! 52: ! 53: extern struct sockaddr_in data_dest; ! 54: extern int logged_in; ! 55: extern int logging; ! 56: extern int type; ! 57: extern int form; ! 58: extern int debug; ! 59: extern int timeout; ! 60: extern char hostname[]; ! 61: extern int usedefault; ! 62: extern char *osi_host; ! 63: extern char *ftp_user; ! 64: extern char *ftp_passwd; ! 65: extern char *ftp_account; ! 66: extern int verbose; ! 67: ! 68: static int cmd_type; ! 69: static int cmd_form; ! 70: static int cmd_bytesz; ! 71: ! 72: char *index(); ! 73: char *savestr(); ! 74: %} ! 75: ! 76: %token ! 77: A B C E F I ! 78: L N P R S T ! 79: ! 80: SP CRLF COMMA STRING NUMBER ! 81: ! 82: USER PASS ACCT REIN QUIT PORT ! 83: PASV TYPE STRU MODE RETR STOR ! 84: APPE MLFL MAIL MSND MSOM MSAM ! 85: MRSQ MRCP ALLO REST RNFR RNTO ! 86: ABOR DELE CWD LIST NLST SITE ! 87: STAT HELP NOOP XMKD XRMD XPWD ! 88: XCUP ! 89: ! 90: LEXERR ! 91: ! 92: %start cmd_list ! 93: ! 94: %% ! 95: ! 96: cmd_list: /* empty */ ! 97: | cmd_list cmd ! 98: ; ! 99: ! 100: cmd: USER SP username CRLF ! 101: = { ! 102: /* remote host information may appear in user ! 103: * name as user@osihost. Save user and hostname until ! 104: * all neccessary information is gathered. ! 105: */ ! 106: ftp_user = savestr((char*)$3); ! 107: osi_host = index(ftp_user,'@'); ! 108: if (osi_host) *osi_host++ = '\0'; ! 109: ! 110: if (strcmp(ftp_user, "ftp") == 0 || ! 111: strcmp(ftp_user, "anonymous") == 0) { ! 112: if (osi_host) osi_host = NULL; ! 113: free(ftp_user); ! 114: ftp_user = "ANON"; ! 115: reply(331, ! 116: "Guest login ok, send ident as password."); ! 117: } else if (checkuser(ftp_user)) { ! 118: reply(331, "Password required for \"%s\".", $3); ! 119: } else ! 120: reply(500,"User disallowed"); ! 121: free((char*)$3); ! 122: } ! 123: | PASS SP password CRLF ! 124: = { ! 125: /* Try and login. dologin() checks if it has ! 126: * all the neccessary information to try and login. ! 127: * Appropriate response codes are generated. ! 128: */ ! 129: ftp_passwd = savestr((char*)$3); ! 130: logged_in = dologin(); ! 131: free((char*)$3); ! 132: } ! 133: | SITE SP osi_hostname CRLF ! 134: = { ! 135: osi_host = savestr((char*)$3); ! 136: logged_in = dologin(); ! 137: free((char*)$3); ! 138: } ! 139: | ACCT SP account CRLF ! 140: = { ! 141: ftp_account = savestr((char*)$3); ! 142: logged_in = dologin(); ! 143: free((char*)$3); ! 144: } ! 145: | PORT SP host_port CRLF ! 146: = { ! 147: usedefault = 0; ! 148: ack((char*)$1); ! 149: } ! 150: | TYPE SP type_code CRLF ! 151: = { ! 152: /* The ISODE supports three file types: ! 153: * binary ! 154: * text ! 155: * directory ! 156: * Binary and Text are selected here. ! 157: * Directory file types are used for LIST and NLST ! 158: */ ! 159: switch (cmd_type) { ! 160: ! 161: case TYPE_A: ! 162: if (cmd_form == FORM_N && ! 163: f_type(TYPE_A) != NOTOK) { ! 164: reply(200, "Type set to A."); ! 165: type = cmd_type; ! 166: form = cmd_form; ! 167: } else ! 168: reply(504, "TYPE set error."); ! 169: break; ! 170: ! 171: case TYPE_E: ! 172: reply(504, "Type E not implemented."); ! 173: break; ! 174: ! 175: case TYPE_I: ! 176: if (f_type(TYPE_I) == OK){ ! 177: reply(200, "Type set to I."); ! 178: type = cmd_type; ! 179: } else ! 180: reply(504, "TYPE set error."); ! 181: break; ! 182: ! 183: case TYPE_L: ! 184: if (cmd_bytesz == 8 && f_type(TYPE_L) == OK) { ! 185: reply(200, ! 186: "Type set to L (byte size 8)."); ! 187: type = cmd_type; ! 188: } else ! 189: reply(504, "TYPE set error."); ! 190: } ! 191: } ! 192: | STRU SP struct_code CRLF ! 193: = { ! 194: switch ($3) { ! 195: ! 196: case STRU_F: ! 197: reply(200, "STRU F ok."); ! 198: break; ! 199: ! 200: default: ! 201: reply(502, "Unimplemented STRU type."); ! 202: } ! 203: } ! 204: | MODE SP mode_code CRLF ! 205: = { ! 206: switch ($3) { ! 207: ! 208: case MODE_S: ! 209: reply(200, "MODE S ok."); ! 210: break; ! 211: ! 212: default: ! 213: reply(502, "Unimplemented MODE type."); ! 214: } ! 215: } ! 216: | ALLO SP NUMBER CRLF ! 217: = { ! 218: ack((char*)$1); ! 219: } ! 220: | RETR check_login SP pathname CRLF ! 221: = { ! 222: if ($2 && $4 != NULL) ! 223: retrieve((char*)$4); ! 224: if ($4 != NULL) ! 225: free((char*)$4); ! 226: } ! 227: | STOR check_login SP pathname CRLF ! 228: = { ! 229: if ($2 && $4 != NULL) ! 230: ftp_store((char*)$4, "w"); ! 231: if ($4 != NULL) ! 232: free((char*)$4); ! 233: } ! 234: | APPE check_login SP pathname CRLF ! 235: = { ! 236: if ($2 && $4 != NULL) ! 237: ftp_store((char *)$4, "a"); ! 238: if ($4 != NULL) ! 239: free((char*)$4); ! 240: } ! 241: | NLST check_login CRLF ! 242: = { ! 243: if ($2) ! 244: directory("NLST","."); ! 245: } ! 246: | NLST check_login SP pathname CRLF ! 247: = { ! 248: if ($2 && $4 != NULL) ! 249: directory("NLST", (char*)$4); ! 250: if ($4 != NULL) ! 251: free((char*)$4); ! 252: } ! 253: | LIST check_login CRLF ! 254: = { ! 255: if ($2) ! 256: directory("LIST", "."); ! 257: } ! 258: | LIST check_login SP pathname CRLF ! 259: = { ! 260: if ($2 && $4 != NULL) ! 261: directory("LIST", (char*)$4); ! 262: if ($4 != NULL) ! 263: free((char*)$4); ! 264: } ! 265: | DELE check_login SP pathname CRLF ! 266: = { ! 267: if ($2 && $4 != NULL) ! 268: ftp_delete((char*)$4); ! 269: if ($4 != NULL) ! 270: free((char*)$4); ! 271: } ! 272: | rename_cmd ! 273: | HELP CRLF ! 274: = { ! 275: help((char*)0); ! 276: } ! 277: | HELP SP STRING CRLF ! 278: = { ! 279: help((char*)$3); ! 280: } ! 281: | NOOP CRLF ! 282: = { ! 283: ack((char*)$1); ! 284: } ! 285: | XMKD check_login SP pathname CRLF ! 286: = { ! 287: if ($2 && $4 != NULL) ! 288: (void)makedir((char*)$4); ! 289: if ($4 != NULL) ! 290: free((char*)$4); ! 291: } ! 292: | XRMD check_login SP pathname CRLF ! 293: = { ! 294: if ($2 && $4 != NULL) ! 295: removedir((char*)$4); ! 296: if ($4 != NULL) ! 297: free((char*)$4); ! 298: } ! 299: | QUIT CRLF ! 300: = { ! 301: dologout(0); ! 302: } ! 303: | error CRLF ! 304: = { ! 305: yyerrok; ! 306: } ! 307: ; ! 308: ! 309: username: STRING ! 310: ; ! 311: ! 312: password: STRING ! 313: ; ! 314: ! 315: osi_hostname: STRING ! 316: ; ! 317: account: 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: $$ = $1; ! 423: } ! 424: ; ! 425: ! 426: pathstring: STRING ! 427: ; ! 428: ! 429: rename_cmd: rename_from rename_to ! 430: = { ! 431: if ($1 && $2) ! 432: renamecmd((char*)$1, (char*)$2); ! 433: else ! 434: reply(503, "Bad sequence of commands."); ! 435: if ($1) ! 436: free((char*)$1); ! 437: if ($2) ! 438: free((char*)$2); ! 439: } ! 440: ; ! 441: ! 442: rename_from: RNFR check_login SP pathname CRLF ! 443: = { ! 444: char *from = 0, *renamefrom(); ! 445: ! 446: if ($2 && $4) ! 447: from = renamefrom((char*)$4); ! 448: if (from == 0 && $4) ! 449: free((char*)$4); ! 450: $$ = (int)from; ! 451: } ! 452: ; ! 453: ! 454: rename_to: RNTO SP pathname CRLF ! 455: = { ! 456: $$ = $3; ! 457: } ! 458: ; ! 459: ! 460: check_login: /* empty */ ! 461: = { ! 462: if (logged_in) ! 463: $$ = 1; ! 464: else { ! 465: reply(530, "Please login with USER and PASS."); ! 466: $$ = 0; ! 467: } ! 468: } ! 469: ; ! 470: ! 471: %% ! 472: ! 473: extern jmp_buf errcatch; ! 474: ! 475: #define CMD 0 /* beginning of command */ ! 476: #define ARGS 1 /* expect miscellaneous arguments */ ! 477: #define STR1 2 /* expect SP followed by STRING */ ! 478: #define STR2 3 /* expect STRING */ ! 479: #define OSTR 4 /* optional STRING */ ! 480: ! 481: struct tab { ! 482: char *name; ! 483: short token; ! 484: short state; ! 485: short implemented; /* 1 if command is implemented */ ! 486: char *help; ! 487: }; ! 488: ! 489: struct tab cmdtab[] = { /* In order defined in RFC 765 */ ! 490: { "USER", USER, STR1, 1, "<sp> username" }, ! 491: { "PASS", PASS, STR1, 1, "<sp> password" }, ! 492: { "ACCT", ACCT, STR1, 1, "(specify account)" }, ! 493: { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, ! 494: { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, ! 495: { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, ! 496: { "PASV", PASV, ARGS, 0, "(set server in passive mode)" }, ! 497: { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, ! 498: { "STRU", STRU, ARGS, 1, "(specify file structure)" }, ! 499: { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, ! 500: { "RETR", RETR, STR1, 1, "<sp> file-name" }, ! 501: { "STOR", STOR, STR1, 1, "<sp> file-name" }, ! 502: { "APPE", APPE, STR1, 1, "<sp> file-name" }, ! 503: { "MLFL", MLFL, OSTR, 0, "(mail file)" }, ! 504: { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, ! 505: { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, ! 506: { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, ! 507: { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, ! 508: { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, ! 509: { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, ! 510: { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, ! 511: { "REST", REST, STR1, 0, "(restart command)" }, ! 512: { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, ! 513: { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, ! 514: { "ABOR", ABOR, ARGS, 0, "(abort operation)" }, ! 515: { "DELE", DELE, STR1, 1, "<sp> file-name" }, ! 516: /* Most directory oriented commands (except XMKD and XRMD) are ! 517: * not supported. The ISODE FTAM requires more knowledge ! 518: * about the remote filesystem type than is available through FTP. ! 519: */ ! 520: { "CWD", CWD, OSTR, 0, "[ <sp> directory-name]" }, ! 521: { "XCWD", CWD, OSTR, 0, "[ <sp> directory-name ]" }, ! 522: { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, ! 523: { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, ! 524: { "SITE", SITE, STR1, 1, "(get site parameters)" }, ! 525: { "STAT", STAT, OSTR, 0, "(get server status)" }, ! 526: { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, ! 527: { "NOOP", NOOP, ARGS, 1, "" }, ! 528: { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, ! 529: { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, ! 530: { "XPWD", XPWD, ARGS, 0, "(return current directory)" }, ! 531: { "XCUP", XCUP, ARGS, 0, "(change to parent directory)" }, ! 532: { NULL, 0, 0, 0, 0 } ! 533: }; ! 534: ! 535: struct tab * ! 536: lookup(cmd) ! 537: char *cmd; ! 538: { ! 539: register struct tab *p; ! 540: ! 541: for (p = cmdtab; p->name != NULL; p++) ! 542: if (strcmp(cmd, p->name) == 0) ! 543: return (p); ! 544: return (0); ! 545: } ! 546: ! 547: #include <arpa/telnet.h> ! 548: ! 549: /* ! 550: * getline - a hacked up version of fgets to ignore TELNET escape codes. ! 551: */ ! 552: char * ! 553: getline(s, n, iop) ! 554: char *s; ! 555: register FILE *iop; ! 556: { ! 557: register c; ! 558: register char *cs; ! 559: ! 560: cs = s; ! 561: while (--n > 0 && (c = getc(iop)) >= 0) { ! 562: while (c == IAC) { ! 563: c = getc(iop); /* skip command */ ! 564: c = getc(iop); /* try next char */ ! 565: } ! 566: *cs++ = c; ! 567: if (c=='\n') ! 568: break; ! 569: } ! 570: if (c < 0 && cs == s) ! 571: exit(1); ! 572: *cs++ = '\0'; ! 573: if (verbose) ! 574: advise (NULLCP, "---> %s", s); ! 575: return (s); ! 576: } ! 577: ! 578: static SFD ! 579: toolong() ! 580: { ! 581: long now; ! 582: extern char *ctime(); ! 583: ! 584: reply(421, ! 585: "Timeout (%d seconds): closing control connection.", timeout); ! 586: (void)time(&now); ! 587: if (logging) ! 588: advise(NULLCP, ! 589: "user %s timed out after %d seconds at %s", ! 590: ftp_user, timeout, ctime(&now)); ! 591: dologout(1); ! 592: } ! 593: ! 594: yylex() ! 595: { ! 596: static char cbuf[512]; ! 597: static int cpos, state; ! 598: register char *cp; ! 599: register struct tab *p; ! 600: int n; ! 601: char c; ! 602: ! 603: for (;;) { ! 604: switch (state) { ! 605: ! 606: case CMD: ! 607: (void)signal(SIGALRM, toolong); ! 608: (void)alarm((unsigned)timeout); ! 609: if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { ! 610: dologout(0); ! 611: } ! 612: (void)alarm(0); ! 613: if (index(cbuf, '\r')) { ! 614: cp = index(cbuf, '\r'); ! 615: cp[0] = '\n'; cp[1] = 0; ! 616: } ! 617: if (index(cbuf, ' ')) ! 618: cpos = index(cbuf, ' ') - cbuf; ! 619: else ! 620: cpos = 4; ! 621: c = cbuf[cpos]; ! 622: cbuf[cpos] = '\0'; ! 623: upper(cbuf); ! 624: p = lookup(cbuf); ! 625: cbuf[cpos] = c; ! 626: if (p != 0) { ! 627: if (p->implemented == 0) { ! 628: nack(p->name); ! 629: longjmp(errcatch,NOTOK); ! 630: /* NOTREACHED */ ! 631: } ! 632: state = p->state; ! 633: yylval = (int) p->name; ! 634: return (p->token); ! 635: } ! 636: break; ! 637: ! 638: case OSTR: ! 639: if (cbuf[cpos] == '\n') { ! 640: state = CMD; ! 641: return (CRLF); ! 642: } ! 643: /* FALL THRU */ ! 644: ! 645: case STR1: ! 646: if (cbuf[cpos] == ' ') { ! 647: /* trim leading blanks */ ! 648: for(;cbuf[cpos] == ' ';cpos++); ! 649: state = STR2; ! 650: return (SP); ! 651: } ! 652: break; ! 653: ! 654: case STR2: ! 655: cp = &cbuf[cpos]; ! 656: n = strlen(cp); ! 657: cpos += n - 1; ! 658: /* ! 659: * Make sure the string is nonempty and \n terminated. ! 660: */ ! 661: if (n > 1 && cbuf[cpos] == '\n') { ! 662: cbuf[cpos] = '\0'; ! 663: yylval = copy(cp); ! 664: cbuf[cpos] = '\n'; ! 665: state = ARGS; ! 666: return (STRING); ! 667: } ! 668: break; ! 669: ! 670: case ARGS: ! 671: if (isdigit(cbuf[cpos])) { ! 672: cp = &cbuf[cpos]; ! 673: while (isdigit(cbuf[++cpos])) ! 674: ; ! 675: c = cbuf[cpos]; ! 676: cbuf[cpos] = '\0'; ! 677: yylval = atoi(cp); ! 678: cbuf[cpos] = c; ! 679: return (NUMBER); ! 680: } ! 681: switch (cbuf[cpos++]) { ! 682: ! 683: case '\n': ! 684: state = CMD; ! 685: return (CRLF); ! 686: ! 687: case ' ': ! 688: return (SP); ! 689: ! 690: case ',': ! 691: return (COMMA); ! 692: ! 693: case 'A': ! 694: case 'a': ! 695: return (A); ! 696: ! 697: case 'B': ! 698: case 'b': ! 699: return (B); ! 700: ! 701: case 'C': ! 702: case 'c': ! 703: return (C); ! 704: ! 705: case 'E': ! 706: case 'e': ! 707: return (E); ! 708: ! 709: case 'F': ! 710: case 'f': ! 711: return (F); ! 712: ! 713: case 'I': ! 714: case 'i': ! 715: return (I); ! 716: ! 717: case 'L': ! 718: case 'l': ! 719: return (L); ! 720: ! 721: case 'N': ! 722: case 'n': ! 723: return (N); ! 724: ! 725: case 'P': ! 726: case 'p': ! 727: return (P); ! 728: ! 729: case 'R': ! 730: case 'r': ! 731: return (R); ! 732: ! 733: case 'S': ! 734: case 's': ! 735: return (S); ! 736: ! 737: case 'T': ! 738: case 't': ! 739: return (T); ! 740: ! 741: } ! 742: break; ! 743: ! 744: default: ! 745: fatal("Unknown state in scanner."); ! 746: } ! 747: yyerror(""); ! 748: state = CMD; ! 749: longjmp(errcatch,NOTOK); ! 750: } ! 751: } ! 752: ! 753: upper(s) ! 754: char *s; ! 755: { ! 756: while (*s != '\0') { ! 757: if (islower(*s)) ! 758: *s = toupper(*s); ! 759: s++; ! 760: } ! 761: } ! 762: ! 763: copy(s) ! 764: char *s; ! 765: { ! 766: char *p; ! 767: extern char *malloc(); ! 768: ! 769: p = malloc((unsigned) (strlen(s) + 1)); ! 770: if (p == NULL) ! 771: fatal("Ran out of memory."); ! 772: (void)strcpy(p, s); ! 773: return ((int)p); ! 774: } ! 775: ! 776: help(s) ! 777: char *s; ! 778: { ! 779: register struct tab *c; ! 780: register int width, NCMDS; ! 781: ! 782: width = 0, NCMDS = 0; ! 783: for (c = cmdtab; c->name != NULL; c++) { ! 784: int len = strlen(c->name); ! 785: ! 786: if (c->implemented == 0) ! 787: len++; ! 788: if (len > width) ! 789: width = len; ! 790: NCMDS++; ! 791: } ! 792: width = (width + 8) &~ 7; ! 793: if (s == 0) { ! 794: register int i, j, w; ! 795: int columns, lines; ! 796: ! 797: lreply(214, ! 798: "The following commands are recognized (* =>'s unimplemented)."); ! 799: columns = 76 / width; ! 800: if (columns == 0) ! 801: columns = 1; ! 802: lines = (NCMDS + columns - 1) / columns; ! 803: for (i = 0; i < lines; i++) { ! 804: printf(" "); ! 805: for (j = 0; j < columns; j++) { ! 806: c = cmdtab + j * lines + i; ! 807: printf("%s%c", c->name, ! 808: c->implemented ? ' ' : '*'); ! 809: if (c + lines >= &cmdtab[NCMDS]) ! 810: break; ! 811: w = strlen(c->name); ! 812: while (w < width) { ! 813: putchar(' '); ! 814: w++; ! 815: } ! 816: } ! 817: printf("\r\n"); ! 818: } ! 819: (void)fflush(stdout); ! 820: reply(214, "Direct comments to ftp-bugs@%s.", hostname); ! 821: return; ! 822: } ! 823: upper(s); ! 824: c = lookup(s); ! 825: if (c == (struct tab *)0) { ! 826: reply(504, "Unknown command %s.", s); ! 827: return; ! 828: } ! 829: if (c->implemented) ! 830: reply(214, "Syntax: %s %s", c->name, c->help); ! 831: else ! 832: reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); ! 833: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.