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