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