110276Ssam /* 210276Ssam * Grammar for FTP commands. 310276Ssam * See RFC 765. 410276Ssam */ 510276Ssam 610276Ssam %{ 710276Ssam 810276Ssam #ifndef lint 9*13246Ssam static char sccsid[] = "@(#)ftpcmd.y 4.11 83/06/22"; 1010276Ssam #endif 1110276Ssam 1210276Ssam #include <sys/types.h> 1310276Ssam #include <sys/socket.h> 1410276Ssam 1510276Ssam #include <netinet/in.h> 1610276Ssam 1713033Ssam #include <arpa/ftp.h> 1813033Ssam 1910276Ssam #include <stdio.h> 2011652Ssam #include <signal.h> 2110276Ssam #include <ctype.h> 2210276Ssam #include <pwd.h> 2310276Ssam #include <setjmp.h> 2410276Ssam 2510276Ssam extern struct sockaddr_in data_dest; 2610276Ssam extern int logged_in; 2710276Ssam extern struct passwd *pw; 2810276Ssam extern int guest; 2910276Ssam extern int logging; 3010276Ssam extern int type; 3110276Ssam extern int form; 3210276Ssam extern int debug; 3311652Ssam extern int timeout; 3410276Ssam extern char hostname[]; 3510276Ssam extern char *globerr; 3610320Ssam extern int usedefault; 3710276Ssam char **glob(); 3810276Ssam 3910276Ssam static int cmd_type; 4010276Ssam static int cmd_form; 4110276Ssam static int cmd_bytesz; 4210276Ssam 4310276Ssam char *index(); 4410276Ssam %} 4510276Ssam 4610276Ssam %token 4710276Ssam A B C E F I 4810276Ssam L N P R S T 4910276Ssam 5010276Ssam SP CRLF COMMA STRING NUMBER 5110276Ssam 5210276Ssam USER PASS ACCT REIN QUIT PORT 5310276Ssam PASV TYPE STRU MODE RETR STOR 5410276Ssam APPE MLFL MAIL MSND MSOM MSAM 5510276Ssam MRSQ MRCP ALLO REST RNFR RNTO 5610276Ssam ABOR DELE CWD LIST NLST SITE 5710276Ssam STAT HELP NOOP XMKD XRMD XPWD 5810276Ssam XCUP 5910276Ssam 6010276Ssam LEXERR 6110276Ssam 6210276Ssam %start cmd_list 6310276Ssam 6410276Ssam %% 6510276Ssam 6610276Ssam cmd_list: /* empty */ 6710276Ssam | cmd_list cmd 6810276Ssam ; 6910276Ssam 7010276Ssam cmd: USER SP username CRLF 7110276Ssam = { 7210276Ssam extern struct passwd *getpwnam(); 7310276Ssam 7410324Ssam if (strcmp($3, "ftp") == 0 || 7510324Ssam strcmp($3, "anonymous") == 0) { 7610324Ssam if ((pw = getpwnam("ftp")) != NULL) { 7710324Ssam guest = 1; 7810324Ssam reply(331, 7910276Ssam "Guest login ok, send ident as password."); 8010324Ssam } 8110694Ssam } else if (checkuser($3)) { 8210276Ssam guest = 0; 8310276Ssam pw = getpwnam($3); 8410276Ssam reply(331, "Password required for %s.", $3); 8510276Ssam } 8610319Ssam if (pw == NULL) 8710319Ssam reply(530, "User %s unknown.", $3); 8810276Ssam free($3); 8910276Ssam } 9010276Ssam | PASS SP password CRLF 9110276Ssam = { 9210276Ssam pass($3); 9310276Ssam free($3); 9410276Ssam } 9510276Ssam | PORT SP host_port CRLF 9610276Ssam = { 9710320Ssam usedefault = 0; 9810276Ssam ack($1); 9910276Ssam } 10010276Ssam | TYPE SP type_code CRLF 10110276Ssam = { 10210276Ssam switch (cmd_type) { 10310276Ssam 10410276Ssam case TYPE_A: 10510276Ssam if (cmd_form == FORM_N) { 10610276Ssam reply(200, "Type set to A."); 10710276Ssam type = cmd_type; 10810276Ssam form = cmd_form; 10910276Ssam } else 11010276Ssam reply(504, "Form must be N."); 11110276Ssam break; 11210276Ssam 11310276Ssam case TYPE_E: 11410276Ssam reply(504, "Type E not implemented."); 11510276Ssam break; 11610276Ssam 11710276Ssam case TYPE_I: 11810276Ssam reply(200, "Type set to I."); 11910276Ssam type = cmd_type; 12010276Ssam break; 12110276Ssam 12210276Ssam case TYPE_L: 12310276Ssam if (cmd_bytesz == 8) { 12410276Ssam reply(200, 12510276Ssam "Type set to L (byte size 8)."); 12610276Ssam type = cmd_type; 12710276Ssam } else 12810276Ssam reply(504, "Byte size must be 8."); 12910276Ssam } 13010276Ssam } 13110276Ssam | STRU SP struct_code CRLF 13210276Ssam = { 13310276Ssam switch ($3) { 13410276Ssam 13510276Ssam case STRU_F: 13610276Ssam reply(200, "STRU F ok."); 13710276Ssam break; 13810276Ssam 13910276Ssam default: 14010276Ssam reply(502, "Unimplemented STRU type."); 14110276Ssam } 14210276Ssam } 14310276Ssam | MODE SP mode_code CRLF 14410276Ssam = { 14510276Ssam switch ($3) { 14610276Ssam 14710276Ssam case MODE_S: 14810276Ssam reply(200, "MODE S ok."); 14910276Ssam break; 15010276Ssam 15110276Ssam default: 15210276Ssam reply(502, "Unimplemented MODE type."); 15310276Ssam } 15410276Ssam } 15510276Ssam | ALLO SP NUMBER CRLF 15610276Ssam = { 15710276Ssam ack($1); 15810276Ssam } 15910276Ssam | RETR check_login SP pathname CRLF 16010276Ssam = { 16110302Ssam if ($2 && $4 != NULL) 16210276Ssam retrieve(0, $4); 16310302Ssam if ($4 != NULL) 16410302Ssam free($4); 16510276Ssam } 16610276Ssam | STOR check_login SP pathname CRLF 16710276Ssam = { 16810302Ssam if ($2 && $4 != NULL) 16910276Ssam store($4, "w"); 17010302Ssam if ($4 != NULL) 17110302Ssam free($4); 17210276Ssam } 17310276Ssam | APPE check_login SP pathname CRLF 17410276Ssam = { 17510302Ssam if ($2 && $4 != NULL) 17610276Ssam store($4, "a"); 17710302Ssam if ($4 != NULL) 17810302Ssam free($4); 17910276Ssam } 18010276Ssam | NLST check_login CRLF 18110276Ssam = { 18210276Ssam if ($2) 18311217Ssam retrieve("/bin/ls", ""); 18410276Ssam } 18510276Ssam | NLST check_login SP pathname CRLF 18610276Ssam = { 18710302Ssam if ($2 && $4 != NULL) 18811217Ssam retrieve("/bin/ls %s", $4); 18910302Ssam if ($4 != NULL) 19010302Ssam free($4); 19110276Ssam } 19210276Ssam | LIST check_login CRLF 19310276Ssam = { 19410276Ssam if ($2) 19510318Ssam retrieve("/bin/ls -lg", ""); 19610276Ssam } 19710276Ssam | LIST check_login SP pathname CRLF 19810276Ssam = { 19910302Ssam if ($2 && $4 != NULL) 20010318Ssam retrieve("/bin/ls -lg %s", $4); 20110302Ssam if ($4 != NULL) 20210302Ssam free($4); 20310276Ssam } 20410276Ssam | DELE check_login SP pathname CRLF 20510276Ssam = { 20610302Ssam if ($2 && $4 != NULL) 20710276Ssam delete($4); 20810302Ssam if ($4 != NULL) 20910302Ssam free($4); 21010276Ssam } 21110276Ssam | CWD check_login CRLF 21210276Ssam = { 21310276Ssam if ($2) 21410276Ssam cwd(pw->pw_dir); 21510276Ssam } 21610276Ssam | CWD check_login SP pathname CRLF 21710276Ssam = { 21810302Ssam if ($2 && $4 != NULL) 21910276Ssam cwd($4); 22010302Ssam if ($4 != NULL) 22110302Ssam free($4); 22210276Ssam } 22310276Ssam | rename_cmd 22410276Ssam | HELP CRLF 22510276Ssam = { 22610276Ssam help(0); 22710276Ssam } 22810276Ssam | HELP SP STRING CRLF 22910276Ssam = { 23010276Ssam help($3); 23110276Ssam } 23210276Ssam | NOOP CRLF 23310276Ssam = { 23410276Ssam ack($1); 23510276Ssam } 23610276Ssam | XMKD check_login SP pathname CRLF 23710276Ssam = { 23810302Ssam if ($2 && $4 != NULL) 23910302Ssam makedir($4); 24010302Ssam if ($4 != NULL) 24110302Ssam free($4); 24210276Ssam } 24310276Ssam | XRMD check_login SP pathname CRLF 24410276Ssam = { 24510302Ssam if ($2 && $4 != NULL) 24610302Ssam removedir($4); 24710302Ssam if ($4 != NULL) 24810302Ssam free($4); 24910276Ssam } 25010276Ssam | XPWD check_login CRLF 25110276Ssam = { 25210276Ssam if ($2) 25310302Ssam pwd(); 25410276Ssam } 25510276Ssam | XCUP check_login CRLF 25610276Ssam = { 25710276Ssam if ($2) 25810276Ssam cwd(".."); 25910276Ssam } 26010276Ssam | QUIT CRLF 26110276Ssam = { 26210276Ssam reply(221, "Goodbye."); 263*13246Ssam dologout(0); 26410276Ssam } 26510276Ssam | error CRLF 26610276Ssam = { 26710276Ssam yyerrok; 26810276Ssam } 26910276Ssam ; 27010276Ssam 27110276Ssam username: STRING 27210276Ssam ; 27310276Ssam 27410276Ssam password: STRING 27510276Ssam ; 27610276Ssam 27710276Ssam byte_size: NUMBER 27810276Ssam ; 27910276Ssam 28010276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 28110276Ssam NUMBER COMMA NUMBER 28210276Ssam = { 28310276Ssam register char *a, *p; 28410276Ssam 28510276Ssam a = (char *)&data_dest.sin_addr; 28610276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 28710276Ssam p = (char *)&data_dest.sin_port; 28810276Ssam p[0] = $9; p[1] = $11; 28910324Ssam data_dest.sin_family = AF_INET; 29010276Ssam } 29110276Ssam ; 29210276Ssam 29310276Ssam form_code: N 29410276Ssam = { 29510276Ssam $$ = FORM_N; 29610276Ssam } 29710276Ssam | T 29810276Ssam = { 29910276Ssam $$ = FORM_T; 30010276Ssam } 30110276Ssam | C 30210276Ssam = { 30310276Ssam $$ = FORM_C; 30410276Ssam } 30510276Ssam ; 30610276Ssam 30710276Ssam type_code: A 30810276Ssam = { 30910276Ssam cmd_type = TYPE_A; 31010276Ssam cmd_form = FORM_N; 31110276Ssam } 31210276Ssam | A SP form_code 31310276Ssam = { 31410276Ssam cmd_type = TYPE_A; 31510276Ssam cmd_form = $3; 31610276Ssam } 31710276Ssam | E 31810276Ssam = { 31910276Ssam cmd_type = TYPE_E; 32010276Ssam cmd_form = FORM_N; 32110276Ssam } 32210276Ssam | E SP form_code 32310276Ssam = { 32410276Ssam cmd_type = TYPE_E; 32510276Ssam cmd_form = $3; 32610276Ssam } 32710276Ssam | I 32810276Ssam = { 32910276Ssam cmd_type = TYPE_I; 33010276Ssam } 33110276Ssam | L 33210276Ssam = { 33310276Ssam cmd_type = TYPE_L; 33410276Ssam cmd_bytesz = 8; 33510276Ssam } 33610276Ssam | L SP byte_size 33710276Ssam = { 33810276Ssam cmd_type = TYPE_L; 33910276Ssam cmd_bytesz = $3; 34010276Ssam } 34110276Ssam /* this is for a bug in the BBN ftp */ 34210276Ssam | L byte_size 34310276Ssam = { 34410276Ssam cmd_type = TYPE_L; 34510276Ssam cmd_bytesz = $2; 34610276Ssam } 34710276Ssam ; 34810276Ssam 34910276Ssam struct_code: F 35010276Ssam = { 35110276Ssam $$ = STRU_F; 35210276Ssam } 35310276Ssam | R 35410276Ssam = { 35510276Ssam $$ = STRU_R; 35610276Ssam } 35710276Ssam | P 35810276Ssam = { 35910276Ssam $$ = STRU_P; 36010276Ssam } 36110276Ssam ; 36210276Ssam 36310276Ssam mode_code: S 36410276Ssam = { 36510276Ssam $$ = MODE_S; 36610276Ssam } 36710276Ssam | B 36810276Ssam = { 36910276Ssam $$ = MODE_B; 37010276Ssam } 37110276Ssam | C 37210276Ssam = { 37310276Ssam $$ = MODE_C; 37410276Ssam } 37510276Ssam ; 37610276Ssam 37710276Ssam pathname: pathstring 37810276Ssam = { 37910276Ssam if ($1 && strncmp($1, "~", 1) == 0) { 38010276Ssam $$ = (int)*glob($1); 38110302Ssam if (globerr != NULL) { 38210276Ssam reply(550, globerr); 38310302Ssam $$ = NULL; 38410302Ssam } 38510276Ssam free($1); 38610276Ssam } else 38710276Ssam $$ = $1; 38810276Ssam } 38910276Ssam ; 39010276Ssam 39110276Ssam pathstring: STRING 39210276Ssam ; 39310276Ssam 39410276Ssam rename_cmd: rename_from rename_to 39510276Ssam = { 39610276Ssam if ($1 && $2) 39710276Ssam renamecmd($1, $2); 39810276Ssam else 39910276Ssam reply(503, "Bad sequence of commands."); 40010276Ssam if ($1) 40110276Ssam free($1); 40210276Ssam if ($2) 40310276Ssam free($2); 40410276Ssam } 40510276Ssam ; 40610276Ssam 40710276Ssam rename_from: RNFR check_login SP pathname CRLF 40810276Ssam = { 40910276Ssam char *from = 0, *renamefrom(); 41010276Ssam 41110302Ssam if ($2 && $4) 41210276Ssam from = renamefrom($4); 41310302Ssam if (from == 0 && $4) 41410276Ssam free($4); 41510276Ssam $$ = (int)from; 41610276Ssam } 41710276Ssam ; 41810276Ssam 41910276Ssam rename_to: RNTO SP pathname CRLF 42010276Ssam = { 42110276Ssam $$ = $3; 42210276Ssam } 42310276Ssam ; 42410276Ssam 42510276Ssam check_login: /* empty */ 42610276Ssam = { 42710276Ssam if (logged_in) 42810276Ssam $$ = 1; 42910276Ssam else { 43010276Ssam reply(530, "Please login with USER and PASS."); 43110276Ssam $$ = 0; 43210276Ssam } 43310276Ssam } 43410276Ssam ; 43510276Ssam 43610276Ssam %% 43710276Ssam 43810276Ssam extern jmp_buf errcatch; 43910276Ssam 44010276Ssam #define CMD 0 /* beginning of command */ 44110276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 44210276Ssam #define STR1 2 /* expect SP followed by STRING */ 44310276Ssam #define STR2 3 /* expect STRING */ 44410276Ssam #define OSTR 4 /* optional STRING */ 44510276Ssam 44610276Ssam struct tab { 44710276Ssam char *name; 44810276Ssam short token; 44910276Ssam short state; 45010276Ssam short implemented; /* 1 if command is implemented */ 45110276Ssam char *help; 45210276Ssam }; 45310276Ssam 45410276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 45510276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 45610276Ssam { "PASS", PASS, STR1, 1, "<sp> password" }, 45710276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 45810276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 45910276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 46010276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 46110276Ssam { "PASV", PASV, ARGS, 0, "(set server in passive mode)" }, 46210276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 46310276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 46410276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 46510276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 46610276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 46710276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 46810276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 46910276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 47010276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 47110276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 47210276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 47310276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 47410276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 47510276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 47610276Ssam { "REST", REST, STR1, 0, "(restart command)" }, 47710276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 47810276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 47910276Ssam { "ABOR", ABOR, ARGS, 0, "(abort operation)" }, 48010276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 48110276Ssam { "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" }, 48210276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 48310276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 48410276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 48510276Ssam { "SITE", SITE, STR1, 0, "(get site parameters)" }, 48610276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 48710276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 48810276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 48910276Ssam { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, 49010276Ssam { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, 49110276Ssam { "XPWD", XPWD, ARGS, 1, "(return current directory)" }, 49210276Ssam { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" }, 49310276Ssam { NULL, 0, 0, 0, 0 } 49410276Ssam }; 49510276Ssam 49610276Ssam struct tab * 49710276Ssam lookup(cmd) 49810276Ssam char *cmd; 49910276Ssam { 50010276Ssam register struct tab *p; 50110276Ssam 50210276Ssam for (p = cmdtab; p->name != NULL; p++) 50310276Ssam if (strcmp(cmd, p->name) == 0) 50410276Ssam return (p); 50510276Ssam return (0); 50610276Ssam } 50710276Ssam 50813033Ssam #include <arpa/telnet.h> 50910276Ssam 51010276Ssam /* 51110276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 51210276Ssam */ 51310276Ssam char * 51410276Ssam getline(s, n, iop) 51510276Ssam char *s; 51610276Ssam register FILE *iop; 51710276Ssam { 51810276Ssam register c; 51910276Ssam register char *cs; 52010276Ssam 52110276Ssam cs = s; 52210276Ssam while (--n > 0 && (c = getc(iop)) >= 0) { 52310276Ssam while (c == IAC) { 52410276Ssam c = getc(iop); /* skip command */ 52510276Ssam c = getc(iop); /* try next char */ 52610276Ssam } 52710276Ssam *cs++ = c; 52810276Ssam if (c=='\n') 52910276Ssam break; 53010276Ssam } 53110276Ssam if (c < 0 && cs == s) 53210276Ssam return (NULL); 53310276Ssam *cs++ = '\0'; 53411652Ssam if (debug) { 53511652Ssam fprintf(stderr, "FTPD: command: %s", s); 53611652Ssam if (c != '\n') 53711652Ssam putc('\n', stderr); 53811652Ssam fflush(stderr); 53911652Ssam } 54010276Ssam return (s); 54110276Ssam } 54210276Ssam 54311652Ssam static int 54411652Ssam toolong() 54511652Ssam { 54611652Ssam long now; 54711652Ssam extern char *ctime(); 54811652Ssam 54911652Ssam reply(421, 55011652Ssam "Timeout (%d seconds): closing control connection.", timeout); 55111652Ssam time(&now); 55211652Ssam if (logging) { 55311652Ssam fprintf(stderr, 55411652Ssam "FTPD: User %s timed out after %d seconds at %s", 55511652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 55611652Ssam fflush(stderr); 55711652Ssam } 558*13246Ssam dologout(1); 55911652Ssam } 56011652Ssam 56110276Ssam yylex() 56210276Ssam { 56310276Ssam static char cbuf[512]; 56410276Ssam static int cpos, state; 56510276Ssam register char *cp; 56610276Ssam register struct tab *p; 56710276Ssam int n; 56810276Ssam char c; 56910276Ssam 57010276Ssam for (;;) { 57110276Ssam switch (state) { 57210276Ssam 57310276Ssam case CMD: 57411652Ssam signal(SIGALRM, toolong); 57511652Ssam alarm(timeout); 57610276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 57710276Ssam reply(221, "You could at least say goodbye."); 578*13246Ssam dologout(0); 57910276Ssam } 58011652Ssam alarm(0); 58110276Ssam if (index(cbuf, '\r')) { 58210276Ssam cp = index(cbuf, '\r'); 58310276Ssam cp[0] = '\n'; cp[1] = 0; 58410276Ssam } 58510276Ssam if (index(cbuf, ' ')) 58610276Ssam cpos = index(cbuf, ' ') - cbuf; 58710276Ssam else 58810276Ssam cpos = 4; 58910276Ssam c = cbuf[cpos]; 59010276Ssam cbuf[cpos] = '\0'; 59110276Ssam upper(cbuf); 59210276Ssam p = lookup(cbuf); 59310276Ssam cbuf[cpos] = c; 59410276Ssam if (p != 0) { 59510276Ssam if (p->implemented == 0) { 59610276Ssam nack(p->name); 59710276Ssam longjmp(errcatch); 59810276Ssam /* NOTREACHED */ 59910276Ssam } 60010276Ssam state = p->state; 60110276Ssam yylval = (int) p->name; 60210276Ssam return (p->token); 60310276Ssam } 60410276Ssam break; 60510276Ssam 60610276Ssam case OSTR: 60710276Ssam if (cbuf[cpos] == '\n') { 60810276Ssam state = CMD; 60910276Ssam return (CRLF); 61010276Ssam } 61110276Ssam /* FALL THRU */ 61210276Ssam 61310276Ssam case STR1: 61410276Ssam if (cbuf[cpos] == ' ') { 61510276Ssam cpos++; 61610276Ssam state = STR2; 61710276Ssam return (SP); 61810276Ssam } 61910276Ssam break; 62010276Ssam 62110276Ssam case STR2: 62210276Ssam cp = &cbuf[cpos]; 62310276Ssam n = strlen(cp); 62410276Ssam cpos += n - 1; 62510276Ssam /* 62610276Ssam * Make sure the string is nonempty and \n terminated. 62710276Ssam */ 62810276Ssam if (n > 1 && cbuf[cpos] == '\n') { 62910276Ssam cbuf[cpos] = '\0'; 63010276Ssam yylval = copy(cp); 63110276Ssam cbuf[cpos] = '\n'; 63210276Ssam state = ARGS; 63310276Ssam return (STRING); 63410276Ssam } 63510276Ssam break; 63610276Ssam 63710276Ssam case ARGS: 63810276Ssam if (isdigit(cbuf[cpos])) { 63910276Ssam cp = &cbuf[cpos]; 64010276Ssam while (isdigit(cbuf[++cpos])) 64110276Ssam ; 64210276Ssam c = cbuf[cpos]; 64310276Ssam cbuf[cpos] = '\0'; 64410276Ssam yylval = atoi(cp); 64510276Ssam cbuf[cpos] = c; 64610276Ssam return (NUMBER); 64710276Ssam } 64810276Ssam switch (cbuf[cpos++]) { 64910276Ssam 65010276Ssam case '\n': 65110276Ssam state = CMD; 65210276Ssam return (CRLF); 65310276Ssam 65410276Ssam case ' ': 65510276Ssam return (SP); 65610276Ssam 65710276Ssam case ',': 65810276Ssam return (COMMA); 65910276Ssam 66010276Ssam case 'A': 66110276Ssam case 'a': 66210276Ssam return (A); 66310276Ssam 66410276Ssam case 'B': 66510276Ssam case 'b': 66610276Ssam return (B); 66710276Ssam 66810276Ssam case 'C': 66910276Ssam case 'c': 67010276Ssam return (C); 67110276Ssam 67210276Ssam case 'E': 67310276Ssam case 'e': 67410276Ssam return (E); 67510276Ssam 67610276Ssam case 'F': 67710276Ssam case 'f': 67810276Ssam return (F); 67910276Ssam 68010276Ssam case 'I': 68110276Ssam case 'i': 68210276Ssam return (I); 68310276Ssam 68410276Ssam case 'L': 68510276Ssam case 'l': 68610276Ssam return (L); 68710276Ssam 68810276Ssam case 'N': 68910276Ssam case 'n': 69010276Ssam return (N); 69110276Ssam 69210276Ssam case 'P': 69310276Ssam case 'p': 69410276Ssam return (P); 69510276Ssam 69610276Ssam case 'R': 69710276Ssam case 'r': 69810276Ssam return (R); 69910276Ssam 70010276Ssam case 'S': 70110276Ssam case 's': 70210276Ssam return (S); 70310276Ssam 70410276Ssam case 'T': 70510276Ssam case 't': 70610276Ssam return (T); 70710276Ssam 70810276Ssam } 70910276Ssam break; 71010276Ssam 71110276Ssam default: 71210276Ssam fatal("Unknown state in scanner."); 71310276Ssam } 71410276Ssam yyerror(); 71510276Ssam state = CMD; 71610276Ssam longjmp(errcatch); 71710276Ssam } 71810276Ssam } 71910276Ssam 72010276Ssam upper(s) 72110276Ssam char *s; 72210276Ssam { 72310276Ssam while (*s != '\0') { 72410276Ssam if (islower(*s)) 72510276Ssam *s = toupper(*s); 72610276Ssam s++; 72710276Ssam } 72810276Ssam } 72910276Ssam 73010276Ssam copy(s) 73110276Ssam char *s; 73210276Ssam { 73310276Ssam char *p; 73410276Ssam extern char *malloc(); 73510276Ssam 73610276Ssam p = malloc(strlen(s) + 1); 73710276Ssam if (p == NULL) 73810276Ssam fatal("Ran out of memory."); 73910276Ssam strcpy(p, s); 74010276Ssam return ((int)p); 74110276Ssam } 74210276Ssam 74310276Ssam help(s) 74410276Ssam char *s; 74510276Ssam { 74610276Ssam register struct tab *c; 74710276Ssam register int width, NCMDS; 74810276Ssam 74910276Ssam width = 0, NCMDS = 0; 75010276Ssam for (c = cmdtab; c->name != NULL; c++) { 75110276Ssam int len = strlen(c->name); 75210276Ssam 75310276Ssam if (c->implemented == 0) 75410276Ssam len++; 75510276Ssam if (len > width) 75610276Ssam width = len; 75710276Ssam NCMDS++; 75810276Ssam } 75910276Ssam width = (width + 8) &~ 7; 76010276Ssam if (s == 0) { 76110276Ssam register int i, j, w; 76210276Ssam int columns, lines; 76310276Ssam 76410276Ssam lreply(214, 76510276Ssam "The following commands are recognized (* =>'s unimplemented)."); 76610276Ssam columns = 76 / width; 76710276Ssam if (columns == 0) 76810276Ssam columns = 1; 76910276Ssam lines = (NCMDS + columns - 1) / columns; 77010276Ssam for (i = 0; i < lines; i++) { 77110276Ssam printf(" "); 77210276Ssam for (j = 0; j < columns; j++) { 77310276Ssam c = cmdtab + j * lines + i; 77410276Ssam printf("%s%c", c->name, 77510276Ssam c->implemented ? ' ' : '*'); 77610302Ssam if (c + lines >= &cmdtab[NCMDS]) 77710276Ssam break; 77810276Ssam w = strlen(c->name); 77910276Ssam while (w < width) { 78010276Ssam putchar(' '); 78110276Ssam w++; 78210276Ssam } 78310276Ssam } 78410276Ssam printf("\r\n"); 78510276Ssam } 78610276Ssam fflush(stdout); 78710276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 78810276Ssam return; 78910276Ssam } 79010276Ssam upper(s); 79110276Ssam c = lookup(s); 79210276Ssam if (c == (struct tab *)0) { 79310276Ssam reply(504, "Unknown command %s.", s); 79410276Ssam return; 79510276Ssam } 79610276Ssam if (c->implemented) 79710276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 79810276Ssam else 79910276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 80010276Ssam } 801