110276Ssam /* 266711Spendry * Copyright (c) 1985, 1988, 1993, 1994 361425Sbostic * The Regents of the University of California. All rights reserved. 433738Sbostic * 542666Sbostic * %sccs.include.redist.c% 634769Sbostic * 7*66727Spendry * @(#)ftpcmd.y 8.3 (Berkeley) 04/06/94 822501Sdist */ 922501Sdist 1022501Sdist /* 1110276Ssam * Grammar for FTP commands. 1236933Skarels * See RFC 959. 1310276Ssam */ 1410276Ssam 1510276Ssam %{ 1610276Ssam 1710276Ssam #ifndef lint 18*66727Spendry static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 04/06/94"; 1933738Sbostic #endif /* not lint */ 2010276Ssam 2136552Sbostic #include <sys/param.h> 2210276Ssam #include <sys/socket.h> 2346669Sbostic #include <sys/stat.h> 2454528Sandrew 2510276Ssam #include <netinet/in.h> 2613033Ssam #include <arpa/ftp.h> 2754528Sandrew 2866711Spendry #include <ctype.h> 2966711Spendry #include <errno.h> 3066711Spendry #include <glob.h> 3166711Spendry #include <pwd.h> 3266711Spendry #include <setjmp.h> 3311652Ssam #include <signal.h> 3466711Spendry #include <stdio.h> 3566711Spendry #include <stdlib.h> 3666711Spendry #include <string.h> 3726494Sminshall #include <syslog.h> 3836933Skarels #include <time.h> 3946669Sbostic #include <unistd.h> 4066711Spendry 4154528Sandrew #include "extern.h" 4210276Ssam 4310276Ssam extern struct sockaddr_in data_dest; 4410276Ssam extern int logged_in; 4510276Ssam extern struct passwd *pw; 4610276Ssam extern int guest; 4710276Ssam extern int logging; 4810276Ssam extern int type; 4910276Ssam extern int form; 5010276Ssam extern int debug; 5111652Ssam extern int timeout; 5236933Skarels extern int maxtimeout; 5326045Sminshall extern int pdata; 5436620Srick extern char hostname[], remotehost[]; 5536933Skarels extern char proctitle[]; 5610320Ssam extern int usedefault; 5726045Sminshall extern int transflag; 5826045Sminshall extern char tmpline[]; 5910276Ssam 6038135Srick off_t restart_point; 6138135Srick 6210276Ssam static int cmd_type; 6310276Ssam static int cmd_form; 6410276Ssam static int cmd_bytesz; 6536304Skarels char cbuf[512]; 6636304Skarels char *fromname; 6710276Ssam 6810276Ssam %} 6910276Ssam 7066711Spendry %union { 7166711Spendry int i; 7266711Spendry char *s; 7366711Spendry } 7466711Spendry 7510276Ssam %token 7610276Ssam A B C E F I 7710276Ssam L N P R S T 7810276Ssam 7966711Spendry SP CRLF COMMA 8010276Ssam 8110276Ssam USER PASS ACCT REIN QUIT PORT 8210276Ssam PASV TYPE STRU MODE RETR STOR 8310276Ssam APPE MLFL MAIL MSND MSOM MSAM 8410276Ssam MRSQ MRCP ALLO REST RNFR RNTO 8510276Ssam ABOR DELE CWD LIST NLST SITE 8636620Srick STAT HELP NOOP MKD RMD PWD 8736933Skarels CDUP STOU SMNT SYST SIZE MDTM 8810276Ssam 8936933Skarels UMASK IDLE CHMOD 9036933Skarels 9110276Ssam LEXERR 9210276Ssam 9366711Spendry %token <s> STRING 9466711Spendry %token <i> NUMBER 9566711Spendry 9666711Spendry %type <i> check_login octal_number byte_size 9766711Spendry %type <i> struct_code mode_code type_code form_code 9866711Spendry %type <s> pathstring pathname password username 9966711Spendry 10010276Ssam %start cmd_list 10110276Ssam 10210276Ssam %% 10310276Ssam 10466711Spendry cmd_list 10566711Spendry : /* empty */ 10666711Spendry | cmd_list cmd 10766711Spendry { 10830945Scsvsj fromname = (char *) 0; 10938135Srick restart_point = (off_t) 0; 11030945Scsvsj } 11166711Spendry | cmd_list rcmd 11210276Ssam ; 11310276Ssam 11466711Spendry cmd 11566711Spendry : USER SP username CRLF 11666711Spendry { 11766711Spendry user($3); 11866711Spendry free($3); 11910276Ssam } 12066711Spendry | PASS SP password CRLF 12166711Spendry { 12266711Spendry pass($3); 12366711Spendry free($3); 12410276Ssam } 12566711Spendry | PORT SP host_port CRLF 12666711Spendry { 12710320Ssam usedefault = 0; 12836304Skarels if (pdata >= 0) { 12926045Sminshall (void) close(pdata); 13036304Skarels pdata = -1; 13126045Sminshall } 13227107Smckusick reply(200, "PORT command successful."); 13310276Ssam } 13466711Spendry | PASV CRLF 13566711Spendry { 13626045Sminshall passive(); 13726045Sminshall } 13866711Spendry | TYPE SP type_code CRLF 13966711Spendry { 14010276Ssam switch (cmd_type) { 14110276Ssam 14210276Ssam case TYPE_A: 14310276Ssam if (cmd_form == FORM_N) { 14410276Ssam reply(200, "Type set to A."); 14510276Ssam type = cmd_type; 14610276Ssam form = cmd_form; 14710276Ssam } else 14810276Ssam reply(504, "Form must be N."); 14910276Ssam break; 15010276Ssam 15110276Ssam case TYPE_E: 15210276Ssam reply(504, "Type E not implemented."); 15310276Ssam break; 15410276Ssam 15510276Ssam case TYPE_I: 15610276Ssam reply(200, "Type set to I."); 15710276Ssam type = cmd_type; 15810276Ssam break; 15910276Ssam 16010276Ssam case TYPE_L: 16136933Skarels #if NBBY == 8 16236933Skarels if (cmd_bytesz == 8) { 16310276Ssam reply(200, 16436933Skarels "Type set to L (byte size 8)."); 16510276Ssam type = cmd_type; 16610276Ssam } else 16736933Skarels reply(504, "Byte size must be 8."); 16836933Skarels #else /* NBBY == 8 */ 16936933Skarels UNIMPLEMENTED for NBBY != 8 17036933Skarels #endif /* NBBY == 8 */ 17110276Ssam } 17210276Ssam } 17366711Spendry | STRU SP struct_code CRLF 17466711Spendry { 17510276Ssam switch ($3) { 17610276Ssam 17710276Ssam case STRU_F: 17810276Ssam reply(200, "STRU F ok."); 17910276Ssam break; 18010276Ssam 18110276Ssam default: 18227107Smckusick reply(504, "Unimplemented STRU type."); 18310276Ssam } 18410276Ssam } 18566711Spendry | MODE SP mode_code CRLF 18666711Spendry { 18710276Ssam switch ($3) { 18810276Ssam 18910276Ssam case MODE_S: 19010276Ssam reply(200, "MODE S ok."); 19110276Ssam break; 19210276Ssam 19310276Ssam default: 19410276Ssam reply(502, "Unimplemented MODE type."); 19510276Ssam } 19610276Ssam } 19766711Spendry | ALLO SP NUMBER CRLF 19866711Spendry { 19927107Smckusick reply(202, "ALLO command ignored."); 20010276Ssam } 20166711Spendry | ALLO SP NUMBER SP R SP NUMBER CRLF 20266711Spendry { 20336933Skarels reply(202, "ALLO command ignored."); 20436933Skarels } 20566711Spendry | RETR check_login SP pathname CRLF 20666711Spendry { 20710302Ssam if ($2 && $4 != NULL) 20866711Spendry retrieve((char *) 0, $4); 20910302Ssam if ($4 != NULL) 21066711Spendry free($4); 21110276Ssam } 21266711Spendry | STOR check_login SP pathname CRLF 21366711Spendry { 21410302Ssam if ($2 && $4 != NULL) 21566711Spendry store($4, "w", 0); 21610302Ssam if ($4 != NULL) 21766711Spendry free($4); 21810276Ssam } 21966711Spendry | APPE check_login SP pathname CRLF 22066711Spendry { 22110302Ssam if ($2 && $4 != NULL) 22266711Spendry store($4, "a", 0); 22310302Ssam if ($4 != NULL) 22466711Spendry free($4); 22510276Ssam } 22666711Spendry | NLST check_login CRLF 22766711Spendry { 22810276Ssam if ($2) 22936620Srick send_file_list("."); 23010276Ssam } 23166711Spendry | NLST check_login SP STRING CRLF 23266711Spendry { 23352999Sbostic if ($2 && $4 != NULL) 23466711Spendry send_file_list($4); 23510302Ssam if ($4 != NULL) 23666711Spendry free($4); 23710276Ssam } 23866711Spendry | LIST check_login CRLF 23966711Spendry { 24010276Ssam if ($2) 24136620Srick retrieve("/bin/ls -lgA", ""); 24210276Ssam } 24366711Spendry | LIST check_login SP pathname CRLF 24466711Spendry { 24510302Ssam if ($2 && $4 != NULL) 24666711Spendry retrieve("/bin/ls -lgA %s", $4); 24710302Ssam if ($4 != NULL) 24866711Spendry free($4); 24910276Ssam } 25066711Spendry | STAT check_login SP pathname CRLF 25166711Spendry { 25236933Skarels if ($2 && $4 != NULL) 25366711Spendry statfilecmd($4); 25436933Skarels if ($4 != NULL) 25566711Spendry free($4); 25636933Skarels } 25766711Spendry | STAT CRLF 25866711Spendry { 25936933Skarels statcmd(); 26036933Skarels } 26166711Spendry | DELE check_login SP pathname CRLF 26266711Spendry { 26310302Ssam if ($2 && $4 != NULL) 26466711Spendry delete($4); 26510302Ssam if ($4 != NULL) 26666711Spendry free($4); 26710276Ssam } 26866711Spendry | RNTO SP pathname CRLF 26966711Spendry { 27030945Scsvsj if (fromname) { 27166711Spendry renamecmd(fromname, $3); 27230945Scsvsj free(fromname); 27330945Scsvsj fromname = (char *) 0; 27430945Scsvsj } else { 27530945Scsvsj reply(503, "Bad sequence of commands."); 27630945Scsvsj } 27766711Spendry free($3); 27830945Scsvsj } 27966711Spendry | ABOR CRLF 28066711Spendry { 28127107Smckusick reply(225, "ABOR command successful."); 28226045Sminshall } 28366711Spendry | CWD check_login CRLF 28466711Spendry { 28510276Ssam if ($2) 28610276Ssam cwd(pw->pw_dir); 28710276Ssam } 28866711Spendry | CWD check_login SP pathname CRLF 28966711Spendry { 29010302Ssam if ($2 && $4 != NULL) 29166711Spendry cwd($4); 29210302Ssam if ($4 != NULL) 29366711Spendry free($4); 29410276Ssam } 29566711Spendry | HELP CRLF 29666711Spendry { 29736933Skarels help(cmdtab, (char *) 0); 29810276Ssam } 29966711Spendry | HELP SP STRING CRLF 30066711Spendry { 30166711Spendry char *cp = $3; 30236933Skarels 30336933Skarels if (strncasecmp(cp, "SITE", 4) == 0) { 30466711Spendry cp = $3 + 4; 30536933Skarels if (*cp == ' ') 30636933Skarels cp++; 30736933Skarels if (*cp) 30836933Skarels help(sitetab, cp); 30936933Skarels else 31036933Skarels help(sitetab, (char *) 0); 31136933Skarels } else 31266711Spendry help(cmdtab, $3); 31310276Ssam } 31466711Spendry | NOOP CRLF 31566711Spendry { 31627107Smckusick reply(200, "NOOP command successful."); 31710276Ssam } 31866711Spendry | MKD check_login SP pathname CRLF 31966711Spendry { 32010302Ssam if ($2 && $4 != NULL) 32166711Spendry makedir($4); 32210302Ssam if ($4 != NULL) 32366711Spendry free($4); 32410276Ssam } 32566711Spendry | RMD check_login SP pathname CRLF 32666711Spendry { 32710302Ssam if ($2 && $4 != NULL) 32866711Spendry removedir($4); 32910302Ssam if ($4 != NULL) 33066711Spendry free($4); 33110276Ssam } 33266711Spendry | PWD check_login CRLF 33366711Spendry { 33410276Ssam if ($2) 33510302Ssam pwd(); 33610276Ssam } 33766711Spendry | CDUP check_login CRLF 33866711Spendry { 33910276Ssam if ($2) 34010276Ssam cwd(".."); 34110276Ssam } 34266711Spendry | SITE SP HELP CRLF 34366711Spendry { 34436933Skarels help(sitetab, (char *) 0); 34536933Skarels } 34666711Spendry | SITE SP HELP SP STRING CRLF 34766711Spendry { 34866711Spendry help(sitetab, $5); 34936933Skarels } 35066711Spendry | SITE SP UMASK check_login CRLF 35166711Spendry { 35236933Skarels int oldmask; 35336933Skarels 35436933Skarels if ($4) { 35536933Skarels oldmask = umask(0); 35636933Skarels (void) umask(oldmask); 35736933Skarels reply(200, "Current UMASK is %03o", oldmask); 35836933Skarels } 35936933Skarels } 36066711Spendry | SITE SP UMASK check_login SP octal_number CRLF 36166711Spendry { 36236933Skarels int oldmask; 36336933Skarels 36436933Skarels if ($4) { 36536933Skarels if (($6 == -1) || ($6 > 0777)) { 36636933Skarels reply(501, "Bad UMASK value"); 36736933Skarels } else { 36836933Skarels oldmask = umask($6); 36936933Skarels reply(200, 37036933Skarels "UMASK set to %03o (was %03o)", 37136933Skarels $6, oldmask); 37236933Skarels } 37336933Skarels } 37436933Skarels } 37566711Spendry | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 37666711Spendry { 37736933Skarels if ($4 && ($8 != NULL)) { 37836933Skarels if ($6 > 0777) 37936933Skarels reply(501, 38036933Skarels "CHMOD: Mode value must be between 0 and 0777"); 38166711Spendry else if (chmod($8, $6) < 0) 38266711Spendry perror_reply(550, $8); 38336933Skarels else 38436933Skarels reply(200, "CHMOD command successful."); 38536933Skarels } 38636933Skarels if ($8 != NULL) 38766711Spendry free($8); 38836933Skarels } 38966711Spendry | SITE SP IDLE CRLF 39066711Spendry { 39136933Skarels reply(200, 39236933Skarels "Current IDLE time limit is %d seconds; max %d", 39336933Skarels timeout, maxtimeout); 39436933Skarels } 39566711Spendry | SITE SP IDLE SP NUMBER CRLF 39666711Spendry { 39736933Skarels if ($5 < 30 || $5 > maxtimeout) { 39836933Skarels reply(501, 39936933Skarels "Maximum IDLE time must be between 30 and %d seconds", 40036933Skarels maxtimeout); 40136933Skarels } else { 40236933Skarels timeout = $5; 40336933Skarels (void) alarm((unsigned) timeout); 40436933Skarels reply(200, 40536933Skarels "Maximum IDLE time set to %d seconds", 40636933Skarels timeout); 40736933Skarels } 40836933Skarels } 40966711Spendry | STOU check_login SP pathname CRLF 41066711Spendry { 41136304Skarels if ($2 && $4 != NULL) 41266711Spendry store($4, "w", 1); 41326045Sminshall if ($4 != NULL) 41466711Spendry free($4); 41526045Sminshall } 41666711Spendry | SYST CRLF 41766711Spendry { 41836640Srick #ifdef unix 41936933Skarels #ifdef BSD 42036552Sbostic reply(215, "UNIX Type: L%d Version: BSD-%d", 42136552Sbostic NBBY, BSD); 42236933Skarels #else /* BSD */ 42336933Skarels reply(215, "UNIX Type: L%d", NBBY); 42436933Skarels #endif /* BSD */ 42536933Skarels #else /* unix */ 42636933Skarels reply(215, "UNKNOWN Type: L%d", NBBY); 42736933Skarels #endif /* unix */ 42836552Sbostic } 42936933Skarels 43036933Skarels /* 43136933Skarels * SIZE is not in RFC959, but Postel has blessed it and 43236933Skarels * it will be in the updated RFC. 43336933Skarels * 43436933Skarels * Return size of file in a format suitable for 43536933Skarels * using with RESTART (we just count bytes). 43636933Skarels */ 43766711Spendry | SIZE check_login SP pathname CRLF 43866711Spendry { 43936933Skarels if ($2 && $4 != NULL) 44066711Spendry sizecmd($4); 44136933Skarels if ($4 != NULL) 44266711Spendry free($4); 44336933Skarels } 44436933Skarels 44536933Skarels /* 44636933Skarels * MDTM is not in RFC959, but Postel has blessed it and 44736933Skarels * it will be in the updated RFC. 44836933Skarels * 44936933Skarels * Return modification time of file as an ISO 3307 45036933Skarels * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 45136933Skarels * where xxx is the fractional second (of any precision, 45236933Skarels * not necessarily 3 digits) 45336933Skarels */ 45466711Spendry | MDTM check_login SP pathname CRLF 45566711Spendry { 45636933Skarels if ($2 && $4 != NULL) { 45736933Skarels struct stat stbuf; 45866711Spendry if (stat($4, &stbuf) < 0) 45954528Sandrew reply(550, "%s: %s", 46066711Spendry $4, strerror(errno)); 46166711Spendry else if (!S_ISREG(stbuf.st_mode)) { 46266711Spendry reply(550, "%s: not a plain file.", $4); 46336933Skarels } else { 46466711Spendry struct tm *t; 46536933Skarels t = gmtime(&stbuf.st_mtime); 46636933Skarels reply(213, 46736933Skarels "19%02d%02d%02d%02d%02d%02d", 46836933Skarels t->tm_year, t->tm_mon+1, t->tm_mday, 46936933Skarels t->tm_hour, t->tm_min, t->tm_sec); 47036933Skarels } 47136933Skarels } 47236933Skarels if ($4 != NULL) 47366711Spendry free($4); 47436933Skarels } 47566711Spendry | QUIT CRLF 47666711Spendry { 47710276Ssam reply(221, "Goodbye."); 47813246Ssam dologout(0); 47910276Ssam } 48066711Spendry | error CRLF 48166711Spendry { 48210276Ssam yyerrok; 48310276Ssam } 48410276Ssam ; 48566711Spendry rcmd 48666711Spendry : RNFR check_login SP pathname CRLF 48766711Spendry { 48830945Scsvsj char *renamefrom(); 48930945Scsvsj 49038135Srick restart_point = (off_t) 0; 49130945Scsvsj if ($2 && $4) { 49266711Spendry fromname = renamefrom($4); 49330945Scsvsj if (fromname == (char *) 0 && $4) { 49466711Spendry free($4); 49530945Scsvsj } 49630945Scsvsj } 49730945Scsvsj } 49866711Spendry | REST SP byte_size CRLF 49966711Spendry { 50038135Srick fromname = (char *) 0; 501*66727Spendry restart_point = $3; /* XXX $3 is only "int" */ 502*66727Spendry reply(350, "Restarting at %qd. %s", restart_point, 50338135Srick "Send STORE or RETRIEVE to initiate transfer."); 50438135Srick } 50530945Scsvsj ; 50652999Sbostic 50766711Spendry username 50866711Spendry : STRING 50910276Ssam ; 51010276Ssam 51166711Spendry password 51266711Spendry : /* empty */ 51366711Spendry { 51466711Spendry $$ = (char *)calloc(1, sizeof(char)); 51536304Skarels } 51666711Spendry | STRING 51710276Ssam ; 51810276Ssam 51966711Spendry byte_size 52066711Spendry : NUMBER 52110276Ssam ; 52210276Ssam 52366711Spendry host_port 52466711Spendry : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 52510276Ssam NUMBER COMMA NUMBER 52666711Spendry { 52766711Spendry char *a, *p; 52810276Ssam 52910276Ssam a = (char *)&data_dest.sin_addr; 53010276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 53110276Ssam p = (char *)&data_dest.sin_port; 53210276Ssam p[0] = $9; p[1] = $11; 53310324Ssam data_dest.sin_family = AF_INET; 53410276Ssam } 53510276Ssam ; 53610276Ssam 53766711Spendry form_code 53866711Spendry : N 53966711Spendry { 54066711Spendry $$ = FORM_N; 54166711Spendry } 54266711Spendry | T 54366711Spendry { 54466711Spendry $$ = FORM_T; 54566711Spendry } 54666711Spendry | C 54766711Spendry { 54866711Spendry $$ = FORM_C; 54966711Spendry } 55010276Ssam ; 55110276Ssam 55266711Spendry type_code 55366711Spendry : A 55466711Spendry { 55566711Spendry cmd_type = TYPE_A; 55666711Spendry cmd_form = FORM_N; 55766711Spendry } 55866711Spendry | A SP form_code 55966711Spendry { 56066711Spendry cmd_type = TYPE_A; 56166711Spendry cmd_form = $3; 56266711Spendry } 56366711Spendry | E 56466711Spendry { 56566711Spendry cmd_type = TYPE_E; 56666711Spendry cmd_form = FORM_N; 56766711Spendry } 56866711Spendry | E SP form_code 56966711Spendry { 57066711Spendry cmd_type = TYPE_E; 57166711Spendry cmd_form = $3; 57266711Spendry } 57366711Spendry | I 57466711Spendry { 57566711Spendry cmd_type = TYPE_I; 57666711Spendry } 57766711Spendry | L 57866711Spendry { 57966711Spendry cmd_type = TYPE_L; 58066711Spendry cmd_bytesz = NBBY; 58166711Spendry } 58266711Spendry | L SP byte_size 58366711Spendry { 58466711Spendry cmd_type = TYPE_L; 58566711Spendry cmd_bytesz = $3; 58666711Spendry } 58766711Spendry /* this is for a bug in the BBN ftp */ 58866711Spendry | L byte_size 58966711Spendry { 59066711Spendry cmd_type = TYPE_L; 59166711Spendry cmd_bytesz = $2; 59266711Spendry } 59310276Ssam ; 59410276Ssam 59566711Spendry struct_code 59666711Spendry : F 59766711Spendry { 59866711Spendry $$ = STRU_F; 59966711Spendry } 60066711Spendry | R 60166711Spendry { 60266711Spendry $$ = STRU_R; 60366711Spendry } 60466711Spendry | P 60566711Spendry { 60666711Spendry $$ = STRU_P; 60766711Spendry } 60810276Ssam ; 60910276Ssam 61066711Spendry mode_code 61166711Spendry : S 61266711Spendry { 61366711Spendry $$ = MODE_S; 61466711Spendry } 61566711Spendry | B 61666711Spendry { 61766711Spendry $$ = MODE_B; 61866711Spendry } 61966711Spendry | C 62066711Spendry { 62166711Spendry $$ = MODE_C; 62266711Spendry } 62310276Ssam ; 62410276Ssam 62566711Spendry pathname 62666711Spendry : pathstring 62766711Spendry { 62866711Spendry /* 62966711Spendry * Problem: this production is used for all pathname 63066711Spendry * processing, but only gives a 550 error reply. 63166711Spendry * This is a valid reply in some cases but not in others. 63266711Spendry */ 63366711Spendry if (logged_in && $1 && *$1 == '~') { 63466711Spendry glob_t gl; 635*66727Spendry int flags = 636*66727Spendry GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 63766711Spendry 63866711Spendry memset(&gl, 0, sizeof(gl)); 639*66727Spendry if (glob($1, flags, NULL, &gl) || 640*66727Spendry gl.gl_pathc == 0) { 64166711Spendry reply(550, "not found"); 64266711Spendry $$ = NULL; 64366711Spendry } else { 64466711Spendry $$ = strdup(gl.gl_pathv[0]); 64566711Spendry } 64666711Spendry globfree(&gl); 64766711Spendry free($1); 64866711Spendry } else 64966711Spendry $$ = $1; 65066711Spendry } 65110276Ssam ; 65210276Ssam 65366711Spendry pathstring 65466711Spendry : STRING 65510276Ssam ; 65610276Ssam 65766711Spendry octal_number 65866711Spendry : NUMBER 65966711Spendry { 66066711Spendry int ret, dec, multby, digit; 66136933Skarels 66266711Spendry /* 66366711Spendry * Convert a number that was read as decimal number 66466711Spendry * to what it would be if it had been read as octal. 66566711Spendry */ 66666711Spendry dec = $1; 66766711Spendry multby = 1; 66866711Spendry ret = 0; 66966711Spendry while (dec) { 67066711Spendry digit = dec%10; 67166711Spendry if (digit > 7) { 67266711Spendry ret = -1; 67366711Spendry break; 67466711Spendry } 67566711Spendry ret += digit * multby; 67666711Spendry multby *= 8; 67766711Spendry dec /= 10; 67836933Skarels } 67966711Spendry $$ = ret; 68036933Skarels } 68136933Skarels ; 68236933Skarels 68366711Spendry 68466711Spendry check_login 68566711Spendry : /* empty */ 68666711Spendry { 68766711Spendry if (logged_in) 68866711Spendry $$ = 1; 68966711Spendry else { 69066711Spendry reply(530, "Please login with USER and PASS."); 69166711Spendry $$ = 0; 69266711Spendry } 69310276Ssam } 69410276Ssam ; 69510276Ssam 69610276Ssam %% 69710276Ssam 69810276Ssam extern jmp_buf errcatch; 69910276Ssam 70010276Ssam #define CMD 0 /* beginning of command */ 70110276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 70210276Ssam #define STR1 2 /* expect SP followed by STRING */ 70310276Ssam #define STR2 3 /* expect STRING */ 70436304Skarels #define OSTR 4 /* optional SP then STRING */ 70536304Skarels #define ZSTR1 5 /* SP then optional STRING */ 70636304Skarels #define ZSTR2 6 /* optional STRING after SP */ 70736933Skarels #define SITECMD 7 /* SITE command */ 70836933Skarels #define NSTR 8 /* Number followed by a string */ 70910276Ssam 71010276Ssam struct tab { 71110276Ssam char *name; 71210276Ssam short token; 71310276Ssam short state; 71410276Ssam short implemented; /* 1 if command is implemented */ 71510276Ssam char *help; 71610276Ssam }; 71710276Ssam 71810276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 71910276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 72036304Skarels { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 72110276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 72236933Skarels { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 72310276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 72410276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 72510276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 72626045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 72710276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 72810276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 72910276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 73010276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 73110276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 73210276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 73310276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 73410276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 73510276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 73610276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 73710276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 73810276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 73910276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 74010276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 74154058Sandrew { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 74210276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 74310276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 74426045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 74510276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 74636304Skarels { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 74710276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 74810276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 74910276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 75036933Skarels { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 75136552Sbostic { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 75236933Skarels { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 75310276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 75410276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 75536620Srick { "MKD", MKD, STR1, 1, "<sp> path-name" }, 75636620Srick { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 75736620Srick { "RMD", RMD, STR1, 1, "<sp> path-name" }, 75836620Srick { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 75936620Srick { "PWD", PWD, ARGS, 1, "(return current directory)" }, 76036620Srick { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 76136620Srick { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 76236620Srick { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 76326045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 76436933Skarels { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 76536933Skarels { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 76610276Ssam { NULL, 0, 0, 0, 0 } 76710276Ssam }; 76810276Ssam 76936933Skarels struct tab sitetab[] = { 77036933Skarels { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 77136933Skarels { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 77236933Skarels { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 77336933Skarels { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 77436933Skarels { NULL, 0, 0, 0, 0 } 77536933Skarels }; 77636933Skarels 77754528Sandrew static char *copy __P((char *)); 77854528Sandrew static void help __P((struct tab *, char *)); 77954528Sandrew static struct tab * 78054528Sandrew lookup __P((struct tab *, char *)); 78154528Sandrew static void sizecmd __P((char *)); 78254528Sandrew static void toolong __P((int)); 78354528Sandrew static int yylex __P((void)); 78454528Sandrew 78554528Sandrew static struct tab * 78636933Skarels lookup(p, cmd) 78766711Spendry struct tab *p; 78810276Ssam char *cmd; 78910276Ssam { 79010276Ssam 79136933Skarels for (; p->name != NULL; p++) 79210276Ssam if (strcmp(cmd, p->name) == 0) 79310276Ssam return (p); 79410276Ssam return (0); 79510276Ssam } 79610276Ssam 79713033Ssam #include <arpa/telnet.h> 79810276Ssam 79910276Ssam /* 80010276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 80110276Ssam */ 80210276Ssam char * 80310276Ssam getline(s, n, iop) 80410276Ssam char *s; 80554528Sandrew int n; 80666711Spendry FILE *iop; 80710276Ssam { 80866711Spendry int c; 80926494Sminshall register char *cs; 81010276Ssam 81110276Ssam cs = s; 81227751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 81326045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 81426045Sminshall *cs++ = tmpline[c]; 81526045Sminshall if (tmpline[c] == '\n') { 81626045Sminshall *cs++ = '\0'; 81736304Skarels if (debug) 81836304Skarels syslog(LOG_DEBUG, "command: %s", s); 81926045Sminshall tmpline[0] = '\0'; 82026045Sminshall return(s); 82126045Sminshall } 82236304Skarels if (c == 0) 82326045Sminshall tmpline[0] = '\0'; 82426045Sminshall } 82536304Skarels while ((c = getc(iop)) != EOF) { 82636304Skarels c &= 0377; 82736304Skarels if (c == IAC) { 82836304Skarels if ((c = getc(iop)) != EOF) { 82936304Skarels c &= 0377; 83036304Skarels switch (c) { 83127751Sminshall case WILL: 83227751Sminshall case WONT: 83336277Sbostic c = getc(iop); 83436304Skarels printf("%c%c%c", IAC, DONT, 0377&c); 83527751Sminshall (void) fflush(stdout); 83636304Skarels continue; 83727751Sminshall case DO: 83827751Sminshall case DONT: 83936277Sbostic c = getc(iop); 84036304Skarels printf("%c%c%c", IAC, WONT, 0377&c); 84127751Sminshall (void) fflush(stdout); 84236304Skarels continue; 84336304Skarels case IAC: 84427751Sminshall break; 84527751Sminshall default: 84636304Skarels continue; /* ignore command */ 84727751Sminshall } 84836304Skarels } 84910276Ssam } 85036304Skarels *cs++ = c; 85136304Skarels if (--n <= 0 || c == '\n') 85210276Ssam break; 85310276Ssam } 85427751Sminshall if (c == EOF && cs == s) 85518303Sralph return (NULL); 85610276Ssam *cs++ = '\0'; 85752999Sbostic if (debug) { 85852999Sbostic if (!guest && strncasecmp("pass ", s, 5) == 0) { 85952999Sbostic /* Don't syslog passwords */ 86052999Sbostic syslog(LOG_DEBUG, "command: %.5s ???", s); 86152999Sbostic } else { 86252999Sbostic register char *cp; 86352999Sbostic register int len; 86452999Sbostic 86552999Sbostic /* Don't syslog trailing CR-LF */ 86652999Sbostic len = strlen(s); 86752999Sbostic cp = s + len - 1; 86852999Sbostic while (cp >= s && (*cp == '\n' || *cp == '\r')) { 86952999Sbostic --cp; 87052999Sbostic --len; 87152999Sbostic } 87252999Sbostic syslog(LOG_DEBUG, "command: %.*s", len, s); 87352999Sbostic } 87452999Sbostic } 87510276Ssam return (s); 87610276Ssam } 87710276Ssam 87846669Sbostic static void 87954528Sandrew toolong(signo) 88054528Sandrew int signo; 88111652Ssam { 88211652Ssam 88311652Ssam reply(421, 88452999Sbostic "Timeout (%d seconds): closing control connection.", timeout); 88552999Sbostic if (logging) 88652999Sbostic syslog(LOG_INFO, "User %s timed out after %d seconds", 88752999Sbostic (pw ? pw -> pw_name : "unknown"), timeout); 88813246Ssam dologout(1); 88911652Ssam } 89011652Ssam 89154528Sandrew static int 89210276Ssam yylex() 89310276Ssam { 89410276Ssam static int cpos, state; 89566711Spendry char *cp, *cp2; 89666711Spendry struct tab *p; 89710276Ssam int n; 89854528Sandrew char c; 89910276Ssam 90010276Ssam for (;;) { 90110276Ssam switch (state) { 90210276Ssam 90310276Ssam case CMD: 90426494Sminshall (void) signal(SIGALRM, toolong); 90526494Sminshall (void) alarm((unsigned) timeout); 90610276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 90710276Ssam reply(221, "You could at least say goodbye."); 90813246Ssam dologout(0); 90910276Ssam } 91026494Sminshall (void) alarm(0); 91136620Srick #ifdef SETPROCTITLE 91236933Skarels if (strncasecmp(cbuf, "PASS", 4) != NULL) 91336933Skarels setproctitle("%s: %s", proctitle, cbuf); 91436620Srick #endif /* SETPROCTITLE */ 91560087Sbostic if ((cp = strchr(cbuf, '\r'))) { 91636324Sbostic *cp++ = '\n'; 91736324Sbostic *cp = '\0'; 91836324Sbostic } 91936277Sbostic if ((cp = strpbrk(cbuf, " \n"))) 92036277Sbostic cpos = cp - cbuf; 92136304Skarels if (cpos == 0) 92210276Ssam cpos = 4; 92310276Ssam c = cbuf[cpos]; 92410276Ssam cbuf[cpos] = '\0'; 92510276Ssam upper(cbuf); 92636933Skarels p = lookup(cmdtab, cbuf); 92710276Ssam cbuf[cpos] = c; 92810276Ssam if (p != 0) { 92910276Ssam if (p->implemented == 0) { 93010276Ssam nack(p->name); 93126494Sminshall longjmp(errcatch,0); 93210276Ssam /* NOTREACHED */ 93310276Ssam } 93410276Ssam state = p->state; 93566711Spendry yylval.s = p->name; 93610276Ssam return (p->token); 93710276Ssam } 93810276Ssam break; 93910276Ssam 94036933Skarels case SITECMD: 94136933Skarels if (cbuf[cpos] == ' ') { 94236933Skarels cpos++; 94336933Skarels return (SP); 94436933Skarels } 94536933Skarels cp = &cbuf[cpos]; 94636933Skarels if ((cp2 = strpbrk(cp, " \n"))) 94736933Skarels cpos = cp2 - cbuf; 94836933Skarels c = cbuf[cpos]; 94936933Skarels cbuf[cpos] = '\0'; 95036933Skarels upper(cp); 95136933Skarels p = lookup(sitetab, cp); 95236933Skarels cbuf[cpos] = c; 95336933Skarels if (p != 0) { 95436933Skarels if (p->implemented == 0) { 95536933Skarels state = CMD; 95636933Skarels nack(p->name); 95736933Skarels longjmp(errcatch,0); 95836933Skarels /* NOTREACHED */ 95936933Skarels } 96036933Skarels state = p->state; 96166711Spendry yylval.s = p->name; 96236933Skarels return (p->token); 96336933Skarels } 96436933Skarels state = CMD; 96536933Skarels break; 96636933Skarels 96710276Ssam case OSTR: 96810276Ssam if (cbuf[cpos] == '\n') { 96910276Ssam state = CMD; 97010276Ssam return (CRLF); 97110276Ssam } 97236317Sbostic /* FALLTHROUGH */ 97310276Ssam 97410276Ssam case STR1: 97536304Skarels case ZSTR1: 97636933Skarels dostr1: 97710276Ssam if (cbuf[cpos] == ' ') { 97810276Ssam cpos++; 97936317Sbostic state = state == OSTR ? STR2 : ++state; 98010276Ssam return (SP); 98110276Ssam } 98210276Ssam break; 98310276Ssam 98436304Skarels case ZSTR2: 98536304Skarels if (cbuf[cpos] == '\n') { 98636304Skarels state = CMD; 98736304Skarels return (CRLF); 98836304Skarels } 98936933Skarels /* FALLTHROUGH */ 99036304Skarels 99110276Ssam case STR2: 99210276Ssam cp = &cbuf[cpos]; 99310276Ssam n = strlen(cp); 99410276Ssam cpos += n - 1; 99510276Ssam /* 99610276Ssam * Make sure the string is nonempty and \n terminated. 99710276Ssam */ 99810276Ssam if (n > 1 && cbuf[cpos] == '\n') { 99910276Ssam cbuf[cpos] = '\0'; 100066711Spendry yylval.s = copy(cp); 100110276Ssam cbuf[cpos] = '\n'; 100210276Ssam state = ARGS; 100310276Ssam return (STRING); 100410276Ssam } 100510276Ssam break; 100610276Ssam 100736933Skarels case NSTR: 100836933Skarels if (cbuf[cpos] == ' ') { 100936933Skarels cpos++; 101036933Skarels return (SP); 101136933Skarels } 101236933Skarels if (isdigit(cbuf[cpos])) { 101336933Skarels cp = &cbuf[cpos]; 101436933Skarels while (isdigit(cbuf[++cpos])) 101536933Skarels ; 101636933Skarels c = cbuf[cpos]; 101736933Skarels cbuf[cpos] = '\0'; 101866711Spendry yylval.i = atoi(cp); 101936933Skarels cbuf[cpos] = c; 102036933Skarels state = STR1; 102136933Skarels return (NUMBER); 102236933Skarels } 102336933Skarels state = STR1; 102436933Skarels goto dostr1; 102536933Skarels 102610276Ssam case ARGS: 102710276Ssam if (isdigit(cbuf[cpos])) { 102810276Ssam cp = &cbuf[cpos]; 102910276Ssam while (isdigit(cbuf[++cpos])) 103010276Ssam ; 103110276Ssam c = cbuf[cpos]; 103210276Ssam cbuf[cpos] = '\0'; 103366711Spendry yylval.i = atoi(cp); 103410276Ssam cbuf[cpos] = c; 103510276Ssam return (NUMBER); 103610276Ssam } 103710276Ssam switch (cbuf[cpos++]) { 103810276Ssam 103910276Ssam case '\n': 104010276Ssam state = CMD; 104110276Ssam return (CRLF); 104210276Ssam 104310276Ssam case ' ': 104410276Ssam return (SP); 104510276Ssam 104610276Ssam case ',': 104710276Ssam return (COMMA); 104810276Ssam 104910276Ssam case 'A': 105010276Ssam case 'a': 105110276Ssam return (A); 105210276Ssam 105310276Ssam case 'B': 105410276Ssam case 'b': 105510276Ssam return (B); 105610276Ssam 105710276Ssam case 'C': 105810276Ssam case 'c': 105910276Ssam return (C); 106010276Ssam 106110276Ssam case 'E': 106210276Ssam case 'e': 106310276Ssam return (E); 106410276Ssam 106510276Ssam case 'F': 106610276Ssam case 'f': 106710276Ssam return (F); 106810276Ssam 106910276Ssam case 'I': 107010276Ssam case 'i': 107110276Ssam return (I); 107210276Ssam 107310276Ssam case 'L': 107410276Ssam case 'l': 107510276Ssam return (L); 107610276Ssam 107710276Ssam case 'N': 107810276Ssam case 'n': 107910276Ssam return (N); 108010276Ssam 108110276Ssam case 'P': 108210276Ssam case 'p': 108310276Ssam return (P); 108410276Ssam 108510276Ssam case 'R': 108610276Ssam case 'r': 108710276Ssam return (R); 108810276Ssam 108910276Ssam case 'S': 109010276Ssam case 's': 109110276Ssam return (S); 109210276Ssam 109310276Ssam case 'T': 109410276Ssam case 't': 109510276Ssam return (T); 109610276Ssam 109710276Ssam } 109810276Ssam break; 109910276Ssam 110010276Ssam default: 110110276Ssam fatal("Unknown state in scanner."); 110210276Ssam } 110326494Sminshall yyerror((char *) 0); 110410276Ssam state = CMD; 110526494Sminshall longjmp(errcatch,0); 110610276Ssam } 110710276Ssam } 110810276Ssam 110954528Sandrew void 111010276Ssam upper(s) 111166711Spendry char *s; 111210276Ssam { 111310276Ssam while (*s != '\0') { 111410276Ssam if (islower(*s)) 111510276Ssam *s = toupper(*s); 111610276Ssam s++; 111710276Ssam } 111810276Ssam } 111910276Ssam 112054528Sandrew static char * 112110276Ssam copy(s) 112210276Ssam char *s; 112310276Ssam { 112410276Ssam char *p; 112510276Ssam 112626494Sminshall p = malloc((unsigned) strlen(s) + 1); 112710276Ssam if (p == NULL) 112810276Ssam fatal("Ran out of memory."); 112926494Sminshall (void) strcpy(p, s); 113036933Skarels return (p); 113110276Ssam } 113210276Ssam 113354528Sandrew static void 113436933Skarels help(ctab, s) 113536933Skarels struct tab *ctab; 113610276Ssam char *s; 113710276Ssam { 113866711Spendry struct tab *c; 113966711Spendry int width, NCMDS; 114036933Skarels char *type; 114110276Ssam 114236933Skarels if (ctab == sitetab) 114336933Skarels type = "SITE "; 114436933Skarels else 114536933Skarels type = ""; 114610276Ssam width = 0, NCMDS = 0; 114736933Skarels for (c = ctab; c->name != NULL; c++) { 114836933Skarels int len = strlen(c->name); 114910276Ssam 115010276Ssam if (len > width) 115110276Ssam width = len; 115210276Ssam NCMDS++; 115310276Ssam } 115410276Ssam width = (width + 8) &~ 7; 115510276Ssam if (s == 0) { 115666711Spendry int i, j, w; 115710276Ssam int columns, lines; 115810276Ssam 115936933Skarels lreply(214, "The following %scommands are recognized %s.", 116036933Skarels type, "(* =>'s unimplemented)"); 116110276Ssam columns = 76 / width; 116210276Ssam if (columns == 0) 116310276Ssam columns = 1; 116410276Ssam lines = (NCMDS + columns - 1) / columns; 116510276Ssam for (i = 0; i < lines; i++) { 116627107Smckusick printf(" "); 116710276Ssam for (j = 0; j < columns; j++) { 116836933Skarels c = ctab + j * lines + i; 116910276Ssam printf("%s%c", c->name, 117010276Ssam c->implemented ? ' ' : '*'); 117136933Skarels if (c + lines >= &ctab[NCMDS]) 117210276Ssam break; 117331132Smckusick w = strlen(c->name) + 1; 117410276Ssam while (w < width) { 117510276Ssam putchar(' '); 117610276Ssam w++; 117710276Ssam } 117810276Ssam } 117910276Ssam printf("\r\n"); 118010276Ssam } 118126494Sminshall (void) fflush(stdout); 118210276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 118310276Ssam return; 118410276Ssam } 118510276Ssam upper(s); 118636933Skarels c = lookup(ctab, s); 118710276Ssam if (c == (struct tab *)0) { 118827107Smckusick reply(502, "Unknown command %s.", s); 118910276Ssam return; 119010276Ssam } 119110276Ssam if (c->implemented) 119236933Skarels reply(214, "Syntax: %s%s %s", type, c->name, c->help); 119310276Ssam else 119436933Skarels reply(214, "%s%-*s\t%s; unimplemented.", type, width, 119536933Skarels c->name, c->help); 119610276Ssam } 119736933Skarels 119854528Sandrew static void 119936933Skarels sizecmd(filename) 120054528Sandrew char *filename; 120136933Skarels { 120236933Skarels switch (type) { 120336933Skarels case TYPE_L: 120436933Skarels case TYPE_I: { 120536933Skarels struct stat stbuf; 120666711Spendry if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 120736933Skarels reply(550, "%s: not a plain file.", filename); 120836933Skarels else 120966711Spendry reply(213, "%qu", stbuf.st_size); 121066711Spendry break; } 121136933Skarels case TYPE_A: { 121236933Skarels FILE *fin; 121366711Spendry int c; 121466711Spendry off_t count; 121536933Skarels struct stat stbuf; 121636933Skarels fin = fopen(filename, "r"); 121736933Skarels if (fin == NULL) { 121836933Skarels perror_reply(550, filename); 121936933Skarels return; 122036933Skarels } 122166711Spendry if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 122236933Skarels reply(550, "%s: not a plain file.", filename); 122336933Skarels (void) fclose(fin); 122436933Skarels return; 122536933Skarels } 122636933Skarels 122736933Skarels count = 0; 122836933Skarels while((c=getc(fin)) != EOF) { 122936933Skarels if (c == '\n') /* will get expanded to \r\n */ 123036933Skarels count++; 123136933Skarels count++; 123236933Skarels } 123336933Skarels (void) fclose(fin); 123436933Skarels 123566711Spendry reply(213, "%qd", count); 123666711Spendry break; } 123736933Skarels default: 123836933Skarels reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 123936933Skarels } 124036933Skarels } 1241