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