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