110276Ssam /* 210276Ssam * Grammar for FTP commands. 310276Ssam * See RFC 765. 410276Ssam */ 510276Ssam 610276Ssam %{ 710276Ssam 810276Ssam #ifndef lint 9*10694Ssam static char sccsid[] = "@(#)ftpcmd.y 4.7 83/02/02"; 1010276Ssam #endif 1110276Ssam 1210276Ssam #include <sys/types.h> 1310276Ssam #include <sys/socket.h> 1410276Ssam 1510276Ssam #include <netinet/in.h> 1610276Ssam 1710276Ssam #include <stdio.h> 1810276Ssam #include <ctype.h> 1910276Ssam #include <pwd.h> 2010276Ssam #include <setjmp.h> 2110276Ssam #include "ftp.h" 2210276Ssam 2310276Ssam extern struct sockaddr_in data_dest; 2410276Ssam extern int logged_in; 2510276Ssam extern struct passwd *pw; 2610276Ssam extern int guest; 2710276Ssam extern int logging; 2810276Ssam extern int type; 2910276Ssam extern int form; 3010276Ssam extern int debug; 3110276Ssam extern char hostname[]; 3210276Ssam extern char *globerr; 3310320Ssam extern int usedefault; 3410276Ssam char **glob(); 3510276Ssam 3610276Ssam static int cmd_type; 3710276Ssam static int cmd_form; 3810276Ssam static int cmd_bytesz; 3910276Ssam 4010276Ssam char *index(); 4110276Ssam %} 4210276Ssam 4310276Ssam %token 4410276Ssam A B C E F I 4510276Ssam L N P R S T 4610276Ssam 4710276Ssam SP CRLF COMMA STRING NUMBER 4810276Ssam 4910276Ssam USER PASS ACCT REIN QUIT PORT 5010276Ssam PASV TYPE STRU MODE RETR STOR 5110276Ssam APPE MLFL MAIL MSND MSOM MSAM 5210276Ssam MRSQ MRCP ALLO REST RNFR RNTO 5310276Ssam ABOR DELE CWD LIST NLST SITE 5410276Ssam STAT HELP NOOP XMKD XRMD XPWD 5510276Ssam XCUP 5610276Ssam 5710276Ssam LEXERR 5810276Ssam 5910276Ssam %start cmd_list 6010276Ssam 6110276Ssam %% 6210276Ssam 6310276Ssam cmd_list: /* empty */ 6410276Ssam | cmd_list cmd 6510276Ssam ; 6610276Ssam 6710276Ssam cmd: USER SP username CRLF 6810276Ssam = { 6910276Ssam extern struct passwd *getpwnam(); 7010276Ssam 7110324Ssam if (strcmp($3, "ftp") == 0 || 7210324Ssam strcmp($3, "anonymous") == 0) { 7310324Ssam if ((pw = getpwnam("ftp")) != NULL) { 7410324Ssam guest = 1; 7510324Ssam reply(331, 7610276Ssam "Guest login ok, send ident as password."); 7710324Ssam } 78*10694Ssam } else if (checkuser($3)) { 7910276Ssam guest = 0; 8010276Ssam pw = getpwnam($3); 8110276Ssam reply(331, "Password required for %s.", $3); 8210276Ssam } 8310319Ssam if (pw == NULL) 8410319Ssam reply(530, "User %s unknown.", $3); 8510276Ssam free($3); 8610276Ssam } 8710276Ssam | PASS SP password CRLF 8810276Ssam = { 8910276Ssam pass($3); 9010276Ssam free($3); 9110276Ssam } 9210276Ssam | PORT SP host_port CRLF 9310276Ssam = { 9410320Ssam usedefault = 0; 9510276Ssam ack($1); 9610276Ssam } 9710276Ssam | TYPE SP type_code CRLF 9810276Ssam = { 9910276Ssam switch (cmd_type) { 10010276Ssam 10110276Ssam case TYPE_A: 10210276Ssam if (cmd_form == FORM_N) { 10310276Ssam reply(200, "Type set to A."); 10410276Ssam type = cmd_type; 10510276Ssam form = cmd_form; 10610276Ssam } else 10710276Ssam reply(504, "Form must be N."); 10810276Ssam break; 10910276Ssam 11010276Ssam case TYPE_E: 11110276Ssam reply(504, "Type E not implemented."); 11210276Ssam break; 11310276Ssam 11410276Ssam case TYPE_I: 11510276Ssam reply(200, "Type set to I."); 11610276Ssam type = cmd_type; 11710276Ssam break; 11810276Ssam 11910276Ssam case TYPE_L: 12010276Ssam if (cmd_bytesz == 8) { 12110276Ssam reply(200, 12210276Ssam "Type set to L (byte size 8)."); 12310276Ssam type = cmd_type; 12410276Ssam } else 12510276Ssam reply(504, "Byte size must be 8."); 12610276Ssam } 12710276Ssam } 12810276Ssam | STRU SP struct_code CRLF 12910276Ssam = { 13010276Ssam switch ($3) { 13110276Ssam 13210276Ssam case STRU_F: 13310276Ssam reply(200, "STRU F ok."); 13410276Ssam break; 13510276Ssam 13610276Ssam default: 13710276Ssam reply(502, "Unimplemented STRU type."); 13810276Ssam } 13910276Ssam } 14010276Ssam | MODE SP mode_code CRLF 14110276Ssam = { 14210276Ssam switch ($3) { 14310276Ssam 14410276Ssam case MODE_S: 14510276Ssam reply(200, "MODE S ok."); 14610276Ssam break; 14710276Ssam 14810276Ssam default: 14910276Ssam reply(502, "Unimplemented MODE type."); 15010276Ssam } 15110276Ssam } 15210276Ssam | ALLO SP NUMBER CRLF 15310276Ssam = { 15410276Ssam ack($1); 15510276Ssam } 15610276Ssam | RETR check_login SP pathname CRLF 15710276Ssam = { 15810302Ssam if ($2 && $4 != NULL) 15910276Ssam retrieve(0, $4); 16010302Ssam if ($4 != NULL) 16110302Ssam free($4); 16210276Ssam } 16310276Ssam | STOR check_login SP pathname CRLF 16410276Ssam = { 16510302Ssam if ($2 && $4 != NULL) 16610276Ssam store($4, "w"); 16710302Ssam if ($4 != NULL) 16810302Ssam free($4); 16910276Ssam } 17010276Ssam | APPE check_login SP pathname CRLF 17110276Ssam = { 17210302Ssam if ($2 && $4 != NULL) 17310276Ssam store($4, "a"); 17410302Ssam if ($4 != NULL) 17510302Ssam free($4); 17610276Ssam } 17710276Ssam | NLST check_login CRLF 17810276Ssam = { 17910276Ssam if ($2) 18010318Ssam retrieve("/bin/ls -C", ""); 18110276Ssam } 18210276Ssam | NLST check_login SP pathname CRLF 18310276Ssam = { 18410302Ssam if ($2 && $4 != NULL) 18510318Ssam retrieve("/bin/ls -C %s", $4); 18610302Ssam if ($4 != NULL) 18710302Ssam free($4); 18810276Ssam } 18910276Ssam | LIST check_login CRLF 19010276Ssam = { 19110276Ssam if ($2) 19210318Ssam retrieve("/bin/ls -lg", ""); 19310276Ssam } 19410276Ssam | LIST check_login SP pathname CRLF 19510276Ssam = { 19610302Ssam if ($2 && $4 != NULL) 19710318Ssam retrieve("/bin/ls -lg %s", $4); 19810302Ssam if ($4 != NULL) 19910302Ssam free($4); 20010276Ssam } 20110276Ssam | DELE check_login SP pathname CRLF 20210276Ssam = { 20310302Ssam if ($2 && $4 != NULL) 20410276Ssam delete($4); 20510302Ssam if ($4 != NULL) 20610302Ssam free($4); 20710276Ssam } 20810276Ssam | CWD check_login CRLF 20910276Ssam = { 21010276Ssam if ($2) 21110276Ssam cwd(pw->pw_dir); 21210276Ssam } 21310276Ssam | CWD check_login SP pathname CRLF 21410276Ssam = { 21510302Ssam if ($2 && $4 != NULL) 21610276Ssam cwd($4); 21710302Ssam if ($4 != NULL) 21810302Ssam free($4); 21910276Ssam } 22010276Ssam | rename_cmd 22110276Ssam | HELP CRLF 22210276Ssam = { 22310276Ssam help(0); 22410276Ssam } 22510276Ssam | HELP SP STRING CRLF 22610276Ssam = { 22710276Ssam help($3); 22810276Ssam } 22910276Ssam | NOOP CRLF 23010276Ssam = { 23110276Ssam ack($1); 23210276Ssam } 23310276Ssam | XMKD check_login SP pathname CRLF 23410276Ssam = { 23510302Ssam if ($2 && $4 != NULL) 23610302Ssam makedir($4); 23710302Ssam if ($4 != NULL) 23810302Ssam free($4); 23910276Ssam } 24010276Ssam | XRMD check_login SP pathname CRLF 24110276Ssam = { 24210302Ssam if ($2 && $4 != NULL) 24310302Ssam removedir($4); 24410302Ssam if ($4 != NULL) 24510302Ssam free($4); 24610276Ssam } 24710276Ssam | XPWD check_login CRLF 24810276Ssam = { 24910276Ssam if ($2) 25010302Ssam pwd(); 25110276Ssam } 25210276Ssam | XCUP check_login CRLF 25310276Ssam = { 25410276Ssam if ($2) 25510276Ssam cwd(".."); 25610276Ssam } 25710276Ssam | QUIT CRLF 25810276Ssam = { 25910276Ssam reply(221, "Goodbye."); 26010276Ssam exit(0); 26110276Ssam } 26210276Ssam | error CRLF 26310276Ssam = { 26410276Ssam yyerrok; 26510276Ssam } 26610276Ssam ; 26710276Ssam 26810276Ssam username: STRING 26910276Ssam ; 27010276Ssam 27110276Ssam password: STRING 27210276Ssam ; 27310276Ssam 27410276Ssam byte_size: NUMBER 27510276Ssam ; 27610276Ssam 27710276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 27810276Ssam NUMBER COMMA NUMBER 27910276Ssam = { 28010276Ssam register char *a, *p; 28110276Ssam 28210276Ssam a = (char *)&data_dest.sin_addr; 28310276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 28410276Ssam p = (char *)&data_dest.sin_port; 28510276Ssam p[0] = $9; p[1] = $11; 28610324Ssam data_dest.sin_family = AF_INET; 28710276Ssam } 28810276Ssam ; 28910276Ssam 29010276Ssam form_code: N 29110276Ssam = { 29210276Ssam $$ = FORM_N; 29310276Ssam } 29410276Ssam | T 29510276Ssam = { 29610276Ssam $$ = FORM_T; 29710276Ssam } 29810276Ssam | C 29910276Ssam = { 30010276Ssam $$ = FORM_C; 30110276Ssam } 30210276Ssam ; 30310276Ssam 30410276Ssam type_code: A 30510276Ssam = { 30610276Ssam cmd_type = TYPE_A; 30710276Ssam cmd_form = FORM_N; 30810276Ssam } 30910276Ssam | A SP form_code 31010276Ssam = { 31110276Ssam cmd_type = TYPE_A; 31210276Ssam cmd_form = $3; 31310276Ssam } 31410276Ssam | E 31510276Ssam = { 31610276Ssam cmd_type = TYPE_E; 31710276Ssam cmd_form = FORM_N; 31810276Ssam } 31910276Ssam | E SP form_code 32010276Ssam = { 32110276Ssam cmd_type = TYPE_E; 32210276Ssam cmd_form = $3; 32310276Ssam } 32410276Ssam | I 32510276Ssam = { 32610276Ssam cmd_type = TYPE_I; 32710276Ssam } 32810276Ssam | L 32910276Ssam = { 33010276Ssam cmd_type = TYPE_L; 33110276Ssam cmd_bytesz = 8; 33210276Ssam } 33310276Ssam | L SP byte_size 33410276Ssam = { 33510276Ssam cmd_type = TYPE_L; 33610276Ssam cmd_bytesz = $3; 33710276Ssam } 33810276Ssam /* this is for a bug in the BBN ftp */ 33910276Ssam | L byte_size 34010276Ssam = { 34110276Ssam cmd_type = TYPE_L; 34210276Ssam cmd_bytesz = $2; 34310276Ssam } 34410276Ssam ; 34510276Ssam 34610276Ssam struct_code: F 34710276Ssam = { 34810276Ssam $$ = STRU_F; 34910276Ssam } 35010276Ssam | R 35110276Ssam = { 35210276Ssam $$ = STRU_R; 35310276Ssam } 35410276Ssam | P 35510276Ssam = { 35610276Ssam $$ = STRU_P; 35710276Ssam } 35810276Ssam ; 35910276Ssam 36010276Ssam mode_code: S 36110276Ssam = { 36210276Ssam $$ = MODE_S; 36310276Ssam } 36410276Ssam | B 36510276Ssam = { 36610276Ssam $$ = MODE_B; 36710276Ssam } 36810276Ssam | C 36910276Ssam = { 37010276Ssam $$ = MODE_C; 37110276Ssam } 37210276Ssam ; 37310276Ssam 37410276Ssam pathname: pathstring 37510276Ssam = { 37610276Ssam if ($1 && strncmp($1, "~", 1) == 0) { 37710276Ssam $$ = (int)*glob($1); 37810302Ssam if (globerr != NULL) { 37910276Ssam reply(550, globerr); 38010302Ssam $$ = NULL; 38110302Ssam } 38210276Ssam free($1); 38310276Ssam } else 38410276Ssam $$ = $1; 38510276Ssam } 38610276Ssam ; 38710276Ssam 38810276Ssam pathstring: STRING 38910276Ssam ; 39010276Ssam 39110276Ssam rename_cmd: rename_from rename_to 39210276Ssam = { 39310276Ssam if ($1 && $2) 39410276Ssam renamecmd($1, $2); 39510276Ssam else 39610276Ssam reply(503, "Bad sequence of commands."); 39710276Ssam if ($1) 39810276Ssam free($1); 39910276Ssam if ($2) 40010276Ssam free($2); 40110276Ssam } 40210276Ssam ; 40310276Ssam 40410276Ssam rename_from: RNFR check_login SP pathname CRLF 40510276Ssam = { 40610276Ssam char *from = 0, *renamefrom(); 40710276Ssam 40810302Ssam if ($2 && $4) 40910276Ssam from = renamefrom($4); 41010302Ssam if (from == 0 && $4) 41110276Ssam free($4); 41210276Ssam $$ = (int)from; 41310276Ssam } 41410276Ssam ; 41510276Ssam 41610276Ssam rename_to: RNTO SP pathname CRLF 41710276Ssam = { 41810276Ssam $$ = $3; 41910276Ssam } 42010276Ssam ; 42110276Ssam 42210276Ssam check_login: /* empty */ 42310276Ssam = { 42410276Ssam if (logged_in) 42510276Ssam $$ = 1; 42610276Ssam else { 42710276Ssam reply(530, "Please login with USER and PASS."); 42810276Ssam $$ = 0; 42910276Ssam } 43010276Ssam } 43110276Ssam ; 43210276Ssam 43310276Ssam %% 43410276Ssam 43510276Ssam extern jmp_buf errcatch; 43610276Ssam 43710276Ssam #define CMD 0 /* beginning of command */ 43810276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 43910276Ssam #define STR1 2 /* expect SP followed by STRING */ 44010276Ssam #define STR2 3 /* expect STRING */ 44110276Ssam #define OSTR 4 /* optional STRING */ 44210276Ssam 44310276Ssam struct tab { 44410276Ssam char *name; 44510276Ssam short token; 44610276Ssam short state; 44710276Ssam short implemented; /* 1 if command is implemented */ 44810276Ssam char *help; 44910276Ssam }; 45010276Ssam 45110276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 45210276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 45310276Ssam { "PASS", PASS, STR1, 1, "<sp> password" }, 45410276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 45510276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 45610276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 45710276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 45810276Ssam { "PASV", PASV, ARGS, 0, "(set server in passive mode)" }, 45910276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 46010276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 46110276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 46210276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 46310276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 46410276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 46510276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 46610276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 46710276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 46810276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 46910276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 47010276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 47110276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 47210276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 47310276Ssam { "REST", REST, STR1, 0, "(restart command)" }, 47410276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 47510276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 47610276Ssam { "ABOR", ABOR, ARGS, 0, "(abort operation)" }, 47710276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 47810276Ssam { "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" }, 47910276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 48010276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 48110276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 48210276Ssam { "SITE", SITE, STR1, 0, "(get site parameters)" }, 48310276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 48410276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 48510276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 48610276Ssam { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, 48710276Ssam { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, 48810276Ssam { "XPWD", XPWD, ARGS, 1, "(return current directory)" }, 48910276Ssam { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" }, 49010276Ssam { NULL, 0, 0, 0, 0 } 49110276Ssam }; 49210276Ssam 49310276Ssam struct tab * 49410276Ssam lookup(cmd) 49510276Ssam char *cmd; 49610276Ssam { 49710276Ssam register struct tab *p; 49810276Ssam 49910276Ssam for (p = cmdtab; p->name != NULL; p++) 50010276Ssam if (strcmp(cmd, p->name) == 0) 50110276Ssam return (p); 50210276Ssam return (0); 50310276Ssam } 50410276Ssam 50510276Ssam #include "../telnet/telnet.h" 50610276Ssam 50710276Ssam /* 50810276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 50910276Ssam */ 51010276Ssam char * 51110276Ssam getline(s, n, iop) 51210276Ssam char *s; 51310276Ssam register FILE *iop; 51410276Ssam { 51510276Ssam register c; 51610276Ssam register char *cs; 51710276Ssam 51810276Ssam cs = s; 51910276Ssam while (--n > 0 && (c = getc(iop)) >= 0) { 52010276Ssam while (c == IAC) { 52110276Ssam c = getc(iop); /* skip command */ 52210276Ssam c = getc(iop); /* try next char */ 52310276Ssam } 52410276Ssam *cs++ = c; 52510276Ssam if (c=='\n') 52610276Ssam break; 52710276Ssam } 52810276Ssam if (c < 0 && cs == s) 52910276Ssam return (NULL); 53010276Ssam *cs++ = '\0'; 53110302Ssam fprintf(stderr, "FTPD: command: %s", s); 53210302Ssam if (c != '\n') 53310302Ssam putc('\n', stderr); 53410276Ssam fflush(stderr); 53510276Ssam return (s); 53610276Ssam } 53710276Ssam 53810276Ssam yylex() 53910276Ssam { 54010276Ssam static char cbuf[512]; 54110276Ssam static int cpos, state; 54210276Ssam register char *cp; 54310276Ssam register struct tab *p; 54410276Ssam int n; 54510276Ssam char c; 54610276Ssam 54710276Ssam for (;;) { 54810276Ssam switch (state) { 54910276Ssam 55010276Ssam case CMD: 55110276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 55210276Ssam reply(221, "You could at least say goodbye."); 55310276Ssam exit(0); 55410276Ssam } 55510276Ssam if (index(cbuf, '\r')) { 55610276Ssam cp = index(cbuf, '\r'); 55710276Ssam cp[0] = '\n'; cp[1] = 0; 55810276Ssam } 55910276Ssam if (index(cbuf, ' ')) 56010276Ssam cpos = index(cbuf, ' ') - cbuf; 56110276Ssam else 56210276Ssam cpos = 4; 56310276Ssam c = cbuf[cpos]; 56410276Ssam cbuf[cpos] = '\0'; 56510276Ssam upper(cbuf); 56610276Ssam p = lookup(cbuf); 56710276Ssam cbuf[cpos] = c; 56810276Ssam if (p != 0) { 56910276Ssam if (p->implemented == 0) { 57010276Ssam nack(p->name); 57110276Ssam longjmp(errcatch); 57210276Ssam /* NOTREACHED */ 57310276Ssam } 57410276Ssam state = p->state; 57510276Ssam yylval = (int) p->name; 57610276Ssam return (p->token); 57710276Ssam } 57810276Ssam break; 57910276Ssam 58010276Ssam case OSTR: 58110276Ssam if (cbuf[cpos] == '\n') { 58210276Ssam state = CMD; 58310276Ssam return (CRLF); 58410276Ssam } 58510276Ssam /* FALL THRU */ 58610276Ssam 58710276Ssam case STR1: 58810276Ssam if (cbuf[cpos] == ' ') { 58910276Ssam cpos++; 59010276Ssam state = STR2; 59110276Ssam return (SP); 59210276Ssam } 59310276Ssam break; 59410276Ssam 59510276Ssam case STR2: 59610276Ssam cp = &cbuf[cpos]; 59710276Ssam n = strlen(cp); 59810276Ssam cpos += n - 1; 59910276Ssam /* 60010276Ssam * Make sure the string is nonempty and \n terminated. 60110276Ssam */ 60210276Ssam if (n > 1 && cbuf[cpos] == '\n') { 60310276Ssam cbuf[cpos] = '\0'; 60410276Ssam yylval = copy(cp); 60510276Ssam cbuf[cpos] = '\n'; 60610276Ssam state = ARGS; 60710276Ssam return (STRING); 60810276Ssam } 60910276Ssam break; 61010276Ssam 61110276Ssam case ARGS: 61210276Ssam if (isdigit(cbuf[cpos])) { 61310276Ssam cp = &cbuf[cpos]; 61410276Ssam while (isdigit(cbuf[++cpos])) 61510276Ssam ; 61610276Ssam c = cbuf[cpos]; 61710276Ssam cbuf[cpos] = '\0'; 61810276Ssam yylval = atoi(cp); 61910276Ssam cbuf[cpos] = c; 62010276Ssam return (NUMBER); 62110276Ssam } 62210276Ssam switch (cbuf[cpos++]) { 62310276Ssam 62410276Ssam case '\n': 62510276Ssam state = CMD; 62610276Ssam return (CRLF); 62710276Ssam 62810276Ssam case ' ': 62910276Ssam return (SP); 63010276Ssam 63110276Ssam case ',': 63210276Ssam return (COMMA); 63310276Ssam 63410276Ssam case 'A': 63510276Ssam case 'a': 63610276Ssam return (A); 63710276Ssam 63810276Ssam case 'B': 63910276Ssam case 'b': 64010276Ssam return (B); 64110276Ssam 64210276Ssam case 'C': 64310276Ssam case 'c': 64410276Ssam return (C); 64510276Ssam 64610276Ssam case 'E': 64710276Ssam case 'e': 64810276Ssam return (E); 64910276Ssam 65010276Ssam case 'F': 65110276Ssam case 'f': 65210276Ssam return (F); 65310276Ssam 65410276Ssam case 'I': 65510276Ssam case 'i': 65610276Ssam return (I); 65710276Ssam 65810276Ssam case 'L': 65910276Ssam case 'l': 66010276Ssam return (L); 66110276Ssam 66210276Ssam case 'N': 66310276Ssam case 'n': 66410276Ssam return (N); 66510276Ssam 66610276Ssam case 'P': 66710276Ssam case 'p': 66810276Ssam return (P); 66910276Ssam 67010276Ssam case 'R': 67110276Ssam case 'r': 67210276Ssam return (R); 67310276Ssam 67410276Ssam case 'S': 67510276Ssam case 's': 67610276Ssam return (S); 67710276Ssam 67810276Ssam case 'T': 67910276Ssam case 't': 68010276Ssam return (T); 68110276Ssam 68210276Ssam } 68310276Ssam break; 68410276Ssam 68510276Ssam default: 68610276Ssam fatal("Unknown state in scanner."); 68710276Ssam } 68810276Ssam yyerror(); 68910276Ssam state = CMD; 69010276Ssam longjmp(errcatch); 69110276Ssam } 69210276Ssam } 69310276Ssam 69410276Ssam upper(s) 69510276Ssam char *s; 69610276Ssam { 69710276Ssam while (*s != '\0') { 69810276Ssam if (islower(*s)) 69910276Ssam *s = toupper(*s); 70010276Ssam s++; 70110276Ssam } 70210276Ssam } 70310276Ssam 70410276Ssam copy(s) 70510276Ssam char *s; 70610276Ssam { 70710276Ssam char *p; 70810276Ssam extern char *malloc(); 70910276Ssam 71010276Ssam p = malloc(strlen(s) + 1); 71110276Ssam if (p == NULL) 71210276Ssam fatal("Ran out of memory."); 71310276Ssam strcpy(p, s); 71410276Ssam return ((int)p); 71510276Ssam } 71610276Ssam 71710276Ssam help(s) 71810276Ssam char *s; 71910276Ssam { 72010276Ssam register struct tab *c; 72110276Ssam register int width, NCMDS; 72210276Ssam 72310276Ssam width = 0, NCMDS = 0; 72410276Ssam for (c = cmdtab; c->name != NULL; c++) { 72510276Ssam int len = strlen(c->name); 72610276Ssam 72710276Ssam if (c->implemented == 0) 72810276Ssam len++; 72910276Ssam if (len > width) 73010276Ssam width = len; 73110276Ssam NCMDS++; 73210276Ssam } 73310276Ssam width = (width + 8) &~ 7; 73410276Ssam if (s == 0) { 73510276Ssam register int i, j, w; 73610276Ssam int columns, lines; 73710276Ssam 73810276Ssam lreply(214, 73910276Ssam "The following commands are recognized (* =>'s unimplemented)."); 74010276Ssam columns = 76 / width; 74110276Ssam if (columns == 0) 74210276Ssam columns = 1; 74310276Ssam lines = (NCMDS + columns - 1) / columns; 74410276Ssam for (i = 0; i < lines; i++) { 74510276Ssam printf(" "); 74610276Ssam for (j = 0; j < columns; j++) { 74710276Ssam c = cmdtab + j * lines + i; 74810276Ssam printf("%s%c", c->name, 74910276Ssam c->implemented ? ' ' : '*'); 75010302Ssam if (c + lines >= &cmdtab[NCMDS]) 75110276Ssam break; 75210276Ssam w = strlen(c->name); 75310276Ssam while (w < width) { 75410276Ssam putchar(' '); 75510276Ssam w++; 75610276Ssam } 75710276Ssam } 75810276Ssam printf("\r\n"); 75910276Ssam } 76010276Ssam fflush(stdout); 76110276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 76210276Ssam return; 76310276Ssam } 76410276Ssam upper(s); 76510276Ssam c = lookup(s); 76610276Ssam if (c == (struct tab *)0) { 76710276Ssam reply(504, "Unknown command %s.", s); 76810276Ssam return; 76910276Ssam } 77010276Ssam if (c->implemented) 77110276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 77210276Ssam else 77310276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 77410276Ssam } 775