1*10276Ssam /* 2*10276Ssam * Grammar for FTP commands. 3*10276Ssam * See RFC 765. 4*10276Ssam */ 5*10276Ssam 6*10276Ssam %{ 7*10276Ssam 8*10276Ssam #ifndef lint 9*10276Ssam static char sccsid[] = "@(#)ftpcmd.y 4.1 83/01/13"; 10*10276Ssam #endif 11*10276Ssam 12*10276Ssam #include <sys/types.h> 13*10276Ssam #include <sys/socket.h> 14*10276Ssam 15*10276Ssam #include <netinet/in.h> 16*10276Ssam 17*10276Ssam #include <stdio.h> 18*10276Ssam #include <ctype.h> 19*10276Ssam #include <pwd.h> 20*10276Ssam #include <setjmp.h> 21*10276Ssam #include "ftp.h" 22*10276Ssam 23*10276Ssam extern struct sockaddr_in data_dest; 24*10276Ssam extern int logged_in; 25*10276Ssam extern struct passwd *pw; 26*10276Ssam extern int guest; 27*10276Ssam extern int logging; 28*10276Ssam extern int type; 29*10276Ssam extern int form; 30*10276Ssam extern int debug; 31*10276Ssam extern char hostname[]; 32*10276Ssam extern char *globerr; 33*10276Ssam char **glob(); 34*10276Ssam 35*10276Ssam static int cmd_type; 36*10276Ssam static int cmd_form; 37*10276Ssam static int cmd_bytesz; 38*10276Ssam 39*10276Ssam static struct passwd nobody = { "?", "?" }; 40*10276Ssam 41*10276Ssam char *index(); 42*10276Ssam %} 43*10276Ssam 44*10276Ssam %token 45*10276Ssam A B C E F I 46*10276Ssam L N P R S T 47*10276Ssam 48*10276Ssam SP CRLF COMMA STRING NUMBER 49*10276Ssam 50*10276Ssam USER PASS ACCT REIN QUIT PORT 51*10276Ssam PASV TYPE STRU MODE RETR STOR 52*10276Ssam APPE MLFL MAIL MSND MSOM MSAM 53*10276Ssam MRSQ MRCP ALLO REST RNFR RNTO 54*10276Ssam ABOR DELE CWD LIST NLST SITE 55*10276Ssam STAT HELP NOOP XMKD XRMD XPWD 56*10276Ssam XCUP 57*10276Ssam 58*10276Ssam LEXERR 59*10276Ssam 60*10276Ssam %start cmd_list 61*10276Ssam 62*10276Ssam %% 63*10276Ssam 64*10276Ssam cmd_list: /* empty */ 65*10276Ssam | cmd_list cmd 66*10276Ssam ; 67*10276Ssam 68*10276Ssam cmd: USER SP username CRLF 69*10276Ssam = { 70*10276Ssam extern struct passwd *getpwnam(); 71*10276Ssam 72*10276Ssam if (strcmp($3, "ftp") == 0 || 73*10276Ssam strcmp($3, "anonymous") == 0) { 74*10276Ssam guest = 1; 75*10276Ssam pw = getpwnam("ftp"); 76*10276Ssam reply(331, 77*10276Ssam "Guest login ok, send ident as password."); 78*10276Ssam } else { 79*10276Ssam guest = 0; 80*10276Ssam pw = getpwnam($3); 81*10276Ssam reply(331, "Password required for %s.", $3); 82*10276Ssam } 83*10276Ssam free($3); 84*10276Ssam if (pw == NULL) 85*10276Ssam pw = &nobody; 86*10276Ssam } 87*10276Ssam | PASS SP password CRLF 88*10276Ssam = { 89*10276Ssam pass($3); 90*10276Ssam free($3); 91*10276Ssam } 92*10276Ssam | PORT SP host_port CRLF 93*10276Ssam = { 94*10276Ssam ack($1); 95*10276Ssam } 96*10276Ssam | TYPE SP type_code CRLF 97*10276Ssam = { 98*10276Ssam switch (cmd_type) { 99*10276Ssam 100*10276Ssam case TYPE_A: 101*10276Ssam if (cmd_form == FORM_N) { 102*10276Ssam reply(200, "Type set to A."); 103*10276Ssam type = cmd_type; 104*10276Ssam form = cmd_form; 105*10276Ssam } else 106*10276Ssam reply(504, "Form must be N."); 107*10276Ssam break; 108*10276Ssam 109*10276Ssam case TYPE_E: 110*10276Ssam reply(504, "Type E not implemented."); 111*10276Ssam break; 112*10276Ssam 113*10276Ssam case TYPE_I: 114*10276Ssam reply(200, "Type set to I."); 115*10276Ssam type = cmd_type; 116*10276Ssam break; 117*10276Ssam 118*10276Ssam case TYPE_L: 119*10276Ssam if (cmd_bytesz == 8) { 120*10276Ssam reply(200, 121*10276Ssam "Type set to L (byte size 8)."); 122*10276Ssam type = cmd_type; 123*10276Ssam } else 124*10276Ssam reply(504, "Byte size must be 8."); 125*10276Ssam } 126*10276Ssam } 127*10276Ssam | STRU SP struct_code CRLF 128*10276Ssam = { 129*10276Ssam switch ($3) { 130*10276Ssam 131*10276Ssam case STRU_F: 132*10276Ssam reply(200, "STRU F ok."); 133*10276Ssam break; 134*10276Ssam 135*10276Ssam default: 136*10276Ssam reply(502, "Unimplemented STRU type."); 137*10276Ssam } 138*10276Ssam } 139*10276Ssam | MODE SP mode_code CRLF 140*10276Ssam = { 141*10276Ssam switch ($3) { 142*10276Ssam 143*10276Ssam case MODE_S: 144*10276Ssam reply(200, "MODE S ok."); 145*10276Ssam break; 146*10276Ssam 147*10276Ssam default: 148*10276Ssam reply(502, "Unimplemented MODE type."); 149*10276Ssam } 150*10276Ssam } 151*10276Ssam | ALLO SP NUMBER CRLF 152*10276Ssam = { 153*10276Ssam ack($1); 154*10276Ssam } 155*10276Ssam | RETR check_login SP pathname CRLF 156*10276Ssam = { 157*10276Ssam if ($2) 158*10276Ssam retrieve(0, $4); 159*10276Ssam free($4); 160*10276Ssam } 161*10276Ssam | STOR check_login SP pathname CRLF 162*10276Ssam = { 163*10276Ssam if ($2) 164*10276Ssam store($4, "w"); 165*10276Ssam free($4); 166*10276Ssam } 167*10276Ssam | APPE check_login SP pathname CRLF 168*10276Ssam = { 169*10276Ssam if ($2) 170*10276Ssam store($4, "a"); 171*10276Ssam free($4); 172*10276Ssam } 173*10276Ssam | NLST check_login CRLF 174*10276Ssam = { 175*10276Ssam if ($2) 176*10276Ssam retrieve("ls", ""); 177*10276Ssam } 178*10276Ssam | NLST check_login SP pathname CRLF 179*10276Ssam = { 180*10276Ssam if ($2) 181*10276Ssam retrieve("ls %s", $4); 182*10276Ssam free($4); 183*10276Ssam } 184*10276Ssam | LIST check_login CRLF 185*10276Ssam = { 186*10276Ssam if ($2) 187*10276Ssam retrieve("ls -l", ""); 188*10276Ssam } 189*10276Ssam | LIST check_login SP pathname CRLF 190*10276Ssam = { 191*10276Ssam if ($2) 192*10276Ssam retrieve("ls -l %s", $4); 193*10276Ssam free($4); 194*10276Ssam } 195*10276Ssam | DELE check_login SP pathname CRLF 196*10276Ssam = { 197*10276Ssam if ($2) 198*10276Ssam delete($4); 199*10276Ssam free($4); 200*10276Ssam } 201*10276Ssam | CWD check_login CRLF 202*10276Ssam = { 203*10276Ssam if ($2) 204*10276Ssam cwd(pw->pw_dir); 205*10276Ssam } 206*10276Ssam | CWD check_login SP pathname CRLF 207*10276Ssam = { 208*10276Ssam if ($2) 209*10276Ssam cwd($4); 210*10276Ssam free($4); 211*10276Ssam } 212*10276Ssam | rename_cmd 213*10276Ssam | HELP CRLF 214*10276Ssam = { 215*10276Ssam help(0); 216*10276Ssam } 217*10276Ssam | HELP SP STRING CRLF 218*10276Ssam = { 219*10276Ssam help($3); 220*10276Ssam } 221*10276Ssam | NOOP CRLF 222*10276Ssam = { 223*10276Ssam ack($1); 224*10276Ssam } 225*10276Ssam | XMKD check_login SP pathname CRLF 226*10276Ssam = { 227*10276Ssam if ($2) 228*10276Ssam do_mkdir($4); 229*10276Ssam free($4); 230*10276Ssam } 231*10276Ssam | XRMD check_login SP pathname CRLF 232*10276Ssam = { 233*10276Ssam if ($2) 234*10276Ssam do_rmdir($4); 235*10276Ssam free($4); 236*10276Ssam } 237*10276Ssam | XPWD check_login CRLF 238*10276Ssam = { 239*10276Ssam if ($2) 240*10276Ssam do_pwd(); 241*10276Ssam } 242*10276Ssam | XCUP check_login CRLF 243*10276Ssam = { 244*10276Ssam if ($2) 245*10276Ssam cwd(".."); 246*10276Ssam } 247*10276Ssam | QUIT CRLF 248*10276Ssam = { 249*10276Ssam reply(221, "Goodbye."); 250*10276Ssam exit(0); 251*10276Ssam } 252*10276Ssam | error CRLF 253*10276Ssam = { 254*10276Ssam yyerrok; 255*10276Ssam } 256*10276Ssam ; 257*10276Ssam 258*10276Ssam username: STRING 259*10276Ssam ; 260*10276Ssam 261*10276Ssam password: STRING 262*10276Ssam ; 263*10276Ssam 264*10276Ssam byte_size: NUMBER 265*10276Ssam ; 266*10276Ssam 267*10276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 268*10276Ssam NUMBER COMMA NUMBER 269*10276Ssam = { 270*10276Ssam register char *a, *p; 271*10276Ssam 272*10276Ssam a = (char *)&data_dest.sin_addr; 273*10276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 274*10276Ssam p = (char *)&data_dest.sin_port; 275*10276Ssam p[0] = $9; p[1] = $11; 276*10276Ssam } 277*10276Ssam ; 278*10276Ssam 279*10276Ssam form_code: N 280*10276Ssam = { 281*10276Ssam $$ = FORM_N; 282*10276Ssam } 283*10276Ssam | T 284*10276Ssam = { 285*10276Ssam $$ = FORM_T; 286*10276Ssam } 287*10276Ssam | C 288*10276Ssam = { 289*10276Ssam $$ = FORM_C; 290*10276Ssam } 291*10276Ssam ; 292*10276Ssam 293*10276Ssam type_code: A 294*10276Ssam = { 295*10276Ssam cmd_type = TYPE_A; 296*10276Ssam cmd_form = FORM_N; 297*10276Ssam } 298*10276Ssam | A SP form_code 299*10276Ssam = { 300*10276Ssam cmd_type = TYPE_A; 301*10276Ssam cmd_form = $3; 302*10276Ssam } 303*10276Ssam | E 304*10276Ssam = { 305*10276Ssam cmd_type = TYPE_E; 306*10276Ssam cmd_form = FORM_N; 307*10276Ssam } 308*10276Ssam | E SP form_code 309*10276Ssam = { 310*10276Ssam cmd_type = TYPE_E; 311*10276Ssam cmd_form = $3; 312*10276Ssam } 313*10276Ssam | I 314*10276Ssam = { 315*10276Ssam cmd_type = TYPE_I; 316*10276Ssam } 317*10276Ssam | L 318*10276Ssam = { 319*10276Ssam cmd_type = TYPE_L; 320*10276Ssam cmd_bytesz = 8; 321*10276Ssam } 322*10276Ssam | L SP byte_size 323*10276Ssam = { 324*10276Ssam cmd_type = TYPE_L; 325*10276Ssam cmd_bytesz = $3; 326*10276Ssam } 327*10276Ssam /* this is for a bug in the BBN ftp */ 328*10276Ssam | L byte_size 329*10276Ssam = { 330*10276Ssam cmd_type = TYPE_L; 331*10276Ssam cmd_bytesz = $2; 332*10276Ssam } 333*10276Ssam ; 334*10276Ssam 335*10276Ssam struct_code: F 336*10276Ssam = { 337*10276Ssam $$ = STRU_F; 338*10276Ssam } 339*10276Ssam | R 340*10276Ssam = { 341*10276Ssam $$ = STRU_R; 342*10276Ssam } 343*10276Ssam | P 344*10276Ssam = { 345*10276Ssam $$ = STRU_P; 346*10276Ssam } 347*10276Ssam ; 348*10276Ssam 349*10276Ssam mode_code: S 350*10276Ssam = { 351*10276Ssam $$ = MODE_S; 352*10276Ssam } 353*10276Ssam | B 354*10276Ssam = { 355*10276Ssam $$ = MODE_B; 356*10276Ssam } 357*10276Ssam | C 358*10276Ssam = { 359*10276Ssam $$ = MODE_C; 360*10276Ssam } 361*10276Ssam ; 362*10276Ssam 363*10276Ssam pathname: pathstring 364*10276Ssam = { 365*10276Ssam if ($1 && strncmp($1, "~", 1) == 0) { 366*10276Ssam $$ = (int)*glob($1); 367*10276Ssam if (globerr != NULL) 368*10276Ssam reply(550, globerr); 369*10276Ssam free($1); 370*10276Ssam } else 371*10276Ssam $$ = $1; 372*10276Ssam } 373*10276Ssam ; 374*10276Ssam 375*10276Ssam pathstring: STRING 376*10276Ssam ; 377*10276Ssam 378*10276Ssam rename_cmd: rename_from rename_to 379*10276Ssam = { 380*10276Ssam if ($1 && $2) 381*10276Ssam renamecmd($1, $2); 382*10276Ssam else 383*10276Ssam reply(503, "Bad sequence of commands."); 384*10276Ssam if ($1) 385*10276Ssam free($1); 386*10276Ssam if ($2) 387*10276Ssam free($2); 388*10276Ssam } 389*10276Ssam ; 390*10276Ssam 391*10276Ssam rename_from: RNFR check_login SP pathname CRLF 392*10276Ssam = { 393*10276Ssam char *from = 0, *renamefrom(); 394*10276Ssam 395*10276Ssam if ($2) 396*10276Ssam from = renamefrom($4); 397*10276Ssam if (from == 0) 398*10276Ssam free($4); 399*10276Ssam $$ = (int)from; 400*10276Ssam } 401*10276Ssam ; 402*10276Ssam 403*10276Ssam rename_to: RNTO SP pathname CRLF 404*10276Ssam = { 405*10276Ssam $$ = $3; 406*10276Ssam } 407*10276Ssam ; 408*10276Ssam 409*10276Ssam check_login: /* empty */ 410*10276Ssam = { 411*10276Ssam if (logged_in) 412*10276Ssam $$ = 1; 413*10276Ssam else { 414*10276Ssam reply(530, "Please login with USER and PASS."); 415*10276Ssam $$ = 0; 416*10276Ssam } 417*10276Ssam } 418*10276Ssam ; 419*10276Ssam 420*10276Ssam %% 421*10276Ssam 422*10276Ssam extern jmp_buf errcatch; 423*10276Ssam 424*10276Ssam #define CMD 0 /* beginning of command */ 425*10276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 426*10276Ssam #define STR1 2 /* expect SP followed by STRING */ 427*10276Ssam #define STR2 3 /* expect STRING */ 428*10276Ssam #define OSTR 4 /* optional STRING */ 429*10276Ssam 430*10276Ssam struct tab { 431*10276Ssam char *name; 432*10276Ssam short token; 433*10276Ssam short state; 434*10276Ssam short implemented; /* 1 if command is implemented */ 435*10276Ssam char *help; 436*10276Ssam }; 437*10276Ssam 438*10276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 439*10276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 440*10276Ssam { "PASS", PASS, STR1, 1, "<sp> password" }, 441*10276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 442*10276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 443*10276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 444*10276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 445*10276Ssam { "PASV", PASV, ARGS, 0, "(set server in passive mode)" }, 446*10276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 447*10276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 448*10276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 449*10276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 450*10276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 451*10276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 452*10276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 453*10276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 454*10276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 455*10276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 456*10276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 457*10276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 458*10276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 459*10276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 460*10276Ssam { "REST", REST, STR1, 0, "(restart command)" }, 461*10276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 462*10276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 463*10276Ssam { "ABOR", ABOR, ARGS, 0, "(abort operation)" }, 464*10276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 465*10276Ssam { "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" }, 466*10276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 467*10276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 468*10276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 469*10276Ssam { "SITE", SITE, STR1, 0, "(get site parameters)" }, 470*10276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 471*10276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 472*10276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 473*10276Ssam { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, 474*10276Ssam { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, 475*10276Ssam { "XPWD", XPWD, ARGS, 1, "(return current directory)" }, 476*10276Ssam { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" }, 477*10276Ssam { NULL, 0, 0, 0, 0 } 478*10276Ssam }; 479*10276Ssam 480*10276Ssam struct tab * 481*10276Ssam lookup(cmd) 482*10276Ssam char *cmd; 483*10276Ssam { 484*10276Ssam register struct tab *p; 485*10276Ssam 486*10276Ssam for (p = cmdtab; p->name != NULL; p++) 487*10276Ssam if (strcmp(cmd, p->name) == 0) 488*10276Ssam return (p); 489*10276Ssam return (0); 490*10276Ssam } 491*10276Ssam 492*10276Ssam #include "../telnet/telnet.h" 493*10276Ssam 494*10276Ssam /* 495*10276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 496*10276Ssam */ 497*10276Ssam char * 498*10276Ssam getline(s, n, iop) 499*10276Ssam char *s; 500*10276Ssam register FILE *iop; 501*10276Ssam { 502*10276Ssam register c; 503*10276Ssam register char *cs; 504*10276Ssam 505*10276Ssam cs = s; 506*10276Ssam while (--n > 0 && (c = getc(iop)) >= 0) { 507*10276Ssam while (c == IAC) { 508*10276Ssam c = getc(iop); /* skip command */ 509*10276Ssam c = getc(iop); /* try next char */ 510*10276Ssam } 511*10276Ssam *cs++ = c; 512*10276Ssam if (c=='\n') 513*10276Ssam break; 514*10276Ssam } 515*10276Ssam if (c < 0 && cs == s) 516*10276Ssam return (NULL); 517*10276Ssam *cs++ = '\0'; 518*10276Ssam fprintf(stderr, "FTPD: command: '%s'\n", s); 519*10276Ssam fflush(stderr); 520*10276Ssam return (s); 521*10276Ssam } 522*10276Ssam 523*10276Ssam yylex() 524*10276Ssam { 525*10276Ssam static char cbuf[512]; 526*10276Ssam static int cpos, state; 527*10276Ssam register char *cp; 528*10276Ssam register struct tab *p; 529*10276Ssam int n; 530*10276Ssam char c; 531*10276Ssam 532*10276Ssam for (;;) { 533*10276Ssam switch (state) { 534*10276Ssam 535*10276Ssam case CMD: 536*10276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 537*10276Ssam reply(221, "You could at least say goodbye."); 538*10276Ssam exit(0); 539*10276Ssam } 540*10276Ssam if (index(cbuf, '\r')) { 541*10276Ssam cp = index(cbuf, '\r'); 542*10276Ssam cp[0] = '\n'; cp[1] = 0; 543*10276Ssam } 544*10276Ssam if (index(cbuf, ' ')) 545*10276Ssam cpos = index(cbuf, ' ') - cbuf; 546*10276Ssam else 547*10276Ssam cpos = 4; 548*10276Ssam c = cbuf[cpos]; 549*10276Ssam cbuf[cpos] = '\0'; 550*10276Ssam upper(cbuf); 551*10276Ssam p = lookup(cbuf); 552*10276Ssam cbuf[cpos] = c; 553*10276Ssam if (p != 0) { 554*10276Ssam if (p->implemented == 0) { 555*10276Ssam nack(p->name); 556*10276Ssam longjmp(errcatch); 557*10276Ssam /* NOTREACHED */ 558*10276Ssam } 559*10276Ssam state = p->state; 560*10276Ssam yylval = (int) p->name; 561*10276Ssam return (p->token); 562*10276Ssam } 563*10276Ssam break; 564*10276Ssam 565*10276Ssam case OSTR: 566*10276Ssam if (cbuf[cpos] == '\n') { 567*10276Ssam state = CMD; 568*10276Ssam return (CRLF); 569*10276Ssam } 570*10276Ssam /* FALL THRU */ 571*10276Ssam 572*10276Ssam case STR1: 573*10276Ssam if (cbuf[cpos] == ' ') { 574*10276Ssam cpos++; 575*10276Ssam state = STR2; 576*10276Ssam return (SP); 577*10276Ssam } 578*10276Ssam break; 579*10276Ssam 580*10276Ssam case STR2: 581*10276Ssam cp = &cbuf[cpos]; 582*10276Ssam n = strlen(cp); 583*10276Ssam cpos += n - 1; 584*10276Ssam /* 585*10276Ssam * Make sure the string is nonempty and \n terminated. 586*10276Ssam */ 587*10276Ssam if (n > 1 && cbuf[cpos] == '\n') { 588*10276Ssam cbuf[cpos] = '\0'; 589*10276Ssam yylval = copy(cp); 590*10276Ssam cbuf[cpos] = '\n'; 591*10276Ssam state = ARGS; 592*10276Ssam return (STRING); 593*10276Ssam } 594*10276Ssam break; 595*10276Ssam 596*10276Ssam case ARGS: 597*10276Ssam if (isdigit(cbuf[cpos])) { 598*10276Ssam cp = &cbuf[cpos]; 599*10276Ssam while (isdigit(cbuf[++cpos])) 600*10276Ssam ; 601*10276Ssam c = cbuf[cpos]; 602*10276Ssam cbuf[cpos] = '\0'; 603*10276Ssam yylval = atoi(cp); 604*10276Ssam cbuf[cpos] = c; 605*10276Ssam return (NUMBER); 606*10276Ssam } 607*10276Ssam switch (cbuf[cpos++]) { 608*10276Ssam 609*10276Ssam case '\n': 610*10276Ssam state = CMD; 611*10276Ssam return (CRLF); 612*10276Ssam 613*10276Ssam case ' ': 614*10276Ssam return (SP); 615*10276Ssam 616*10276Ssam case ',': 617*10276Ssam return (COMMA); 618*10276Ssam 619*10276Ssam case 'A': 620*10276Ssam case 'a': 621*10276Ssam return (A); 622*10276Ssam 623*10276Ssam case 'B': 624*10276Ssam case 'b': 625*10276Ssam return (B); 626*10276Ssam 627*10276Ssam case 'C': 628*10276Ssam case 'c': 629*10276Ssam return (C); 630*10276Ssam 631*10276Ssam case 'E': 632*10276Ssam case 'e': 633*10276Ssam return (E); 634*10276Ssam 635*10276Ssam case 'F': 636*10276Ssam case 'f': 637*10276Ssam return (F); 638*10276Ssam 639*10276Ssam case 'I': 640*10276Ssam case 'i': 641*10276Ssam return (I); 642*10276Ssam 643*10276Ssam case 'L': 644*10276Ssam case 'l': 645*10276Ssam return (L); 646*10276Ssam 647*10276Ssam case 'N': 648*10276Ssam case 'n': 649*10276Ssam return (N); 650*10276Ssam 651*10276Ssam case 'P': 652*10276Ssam case 'p': 653*10276Ssam return (P); 654*10276Ssam 655*10276Ssam case 'R': 656*10276Ssam case 'r': 657*10276Ssam return (R); 658*10276Ssam 659*10276Ssam case 'S': 660*10276Ssam case 's': 661*10276Ssam return (S); 662*10276Ssam 663*10276Ssam case 'T': 664*10276Ssam case 't': 665*10276Ssam return (T); 666*10276Ssam 667*10276Ssam } 668*10276Ssam break; 669*10276Ssam 670*10276Ssam default: 671*10276Ssam fatal("Unknown state in scanner."); 672*10276Ssam } 673*10276Ssam yyerror(); 674*10276Ssam state = CMD; 675*10276Ssam longjmp(errcatch); 676*10276Ssam } 677*10276Ssam } 678*10276Ssam 679*10276Ssam upper(s) 680*10276Ssam char *s; 681*10276Ssam { 682*10276Ssam while (*s != '\0') { 683*10276Ssam if (islower(*s)) 684*10276Ssam *s = toupper(*s); 685*10276Ssam s++; 686*10276Ssam } 687*10276Ssam } 688*10276Ssam 689*10276Ssam copy(s) 690*10276Ssam char *s; 691*10276Ssam { 692*10276Ssam char *p; 693*10276Ssam extern char *malloc(); 694*10276Ssam 695*10276Ssam p = malloc(strlen(s) + 1); 696*10276Ssam if (p == NULL) 697*10276Ssam fatal("Ran out of memory."); 698*10276Ssam strcpy(p, s); 699*10276Ssam return ((int)p); 700*10276Ssam } 701*10276Ssam 702*10276Ssam help(s) 703*10276Ssam char *s; 704*10276Ssam { 705*10276Ssam register struct tab *c; 706*10276Ssam register int width, NCMDS; 707*10276Ssam 708*10276Ssam width = 0, NCMDS = 0; 709*10276Ssam for (c = cmdtab; c->name != NULL; c++) { 710*10276Ssam int len = strlen(c->name); 711*10276Ssam 712*10276Ssam if (c->implemented == 0) 713*10276Ssam len++; 714*10276Ssam if (len > width) 715*10276Ssam width = len; 716*10276Ssam NCMDS++; 717*10276Ssam } 718*10276Ssam width = (width + 8) &~ 7; 719*10276Ssam if (s == 0) { 720*10276Ssam register int i, j, w; 721*10276Ssam int columns, lines; 722*10276Ssam 723*10276Ssam lreply(214, 724*10276Ssam "The following commands are recognized (* =>'s unimplemented)."); 725*10276Ssam columns = 76 / width; 726*10276Ssam if (columns == 0) 727*10276Ssam columns = 1; 728*10276Ssam lines = (NCMDS + columns - 1) / columns; 729*10276Ssam for (i = 0; i < lines; i++) { 730*10276Ssam printf(" "); 731*10276Ssam for (j = 0; j < columns; j++) { 732*10276Ssam c = cmdtab + j * lines + i; 733*10276Ssam printf("%s%c", c->name, 734*10276Ssam c->implemented ? ' ' : '*'); 735*10276Ssam if (c + lines >= &cmdtab[NCMDS]) { 736*10276Ssam printf("\r\n"); 737*10276Ssam break; 738*10276Ssam } 739*10276Ssam w = strlen(c->name); 740*10276Ssam while (w < width) { 741*10276Ssam putchar(' '); 742*10276Ssam w++; 743*10276Ssam } 744*10276Ssam } 745*10276Ssam printf("\r\n"); 746*10276Ssam } 747*10276Ssam fflush(stdout); 748*10276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 749*10276Ssam return; 750*10276Ssam } 751*10276Ssam upper(s); 752*10276Ssam c = lookup(s); 753*10276Ssam if (c == (struct tab *)0) { 754*10276Ssam reply(504, "Unknown command %s.", s); 755*10276Ssam return; 756*10276Ssam } 757*10276Ssam if (c->implemented) 758*10276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 759*10276Ssam else 760*10276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 761*10276Ssam } 762