1 /* ftpcmd.y: yacc parser for the FTP daemon.
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
12 Modified by cmetz for OPIE 2.4. Use DOTITLE rather than SETPROCTITLE.
13 Modified by cmetz for OPIE 2.3. Moved LS_COMMAND here.
14 Modified by cmetz for OPIE 2.2. Fixed a *lot* of warnings.
15 Use FUNCTION declaration et al. Removed useless strings.
16 Changed some char []s to char *s. Deleted comment address.
17 Changed tmpline references to be more pure-pointer
18 references. Changed tmpline declaration back to char [].
19 Modified at NRL for OPIE 2.1. Minor changes for autoconf.
20 Modified at NRL for OPIE 2.01. Added forward declaration for sitetab[]
21 -- fixes problems experienced by bison users. Merged in new
22 PORT attack fixes from Hobbit.
23 Modified at NRL for OPIE 2.0.
26 $FreeBSD: src/contrib/opie/ftpcmd.y,v 1.2.6.4 2002/07/15 14:48:43 des Exp $
27 $DragonFly: src/contrib/opie/ftpcmd.y,v 1.2 2003/06/17 04:24:04 dillon Exp $
30 * Copyright (c) 1985, 1988 Regents of the University of California.
31 * All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. All advertising materials mentioning features or use of this software
42 * must display the following acknowledgement:
43 * This product includes software developed by the University of
44 * California, Berkeley and its contributors.
45 * 4. Neither the name of the University nor the names of its contributors
46 * may be used to endorse or promote products derived from this software
47 * without specific prior written permission.
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91
65 * Grammar for FTP commands.
72 #include <sys/param.h>
73 #include <sys/types.h>
74 #include <sys/socket.h>
76 #include <netinet/in.h>
83 #else /* TM_IN_SYS_TIME */
85 #endif /* TM_IN_SYS_TIME */
96 #define LS_COMMAND "/bin/ls -lgA"
97 #else /* HAVE_LS_G_FLAG */
98 #define LS_COMMAND "/bin/ls -lA"
99 #endif /* HAVE_LS_G_FLAG */
101 extern
struct sockaddr_in data_dest
;
102 extern
struct sockaddr_in his_addr
;
103 extern
int logged_in
;
104 extern
struct passwd
*pw
;
110 extern
int maxtimeout
;
112 extern
char *remotehost
;
113 extern
char *proctitle
;
114 extern
char *globerr
;
115 extern
int usedefault
;
116 extern
int transflag
;
117 extern
char tmpline
[];
120 VOIDRET dologout __P
((int));
121 VOIDRET upper __P
((char *));
122 VOIDRET nack __P
((char *));
123 VOIDRET opiefatal __P
((char *));
125 VOIDRET pass __P
((char *));
126 int user __P
((char *));
127 VOIDRET passive __P
((void));
128 VOIDRET retrieve __P
((char *, char *));
129 VOIDRET store __P
((char *, char *, int));
130 VOIDRET send_file_list __P
((char *));
131 VOIDRET statfilecmd __P
((char *));
132 VOIDRET statcmd __P
((void));
133 VOIDRET delete __P
((char *));
134 VOIDRET renamecmd __P
((char *, char *));
135 VOIDRET cwd __P
((char *));
136 VOIDRET makedir __P
((char *));
137 VOIDRET removedir __P
((char *));
138 VOIDRET pwd __P
((void));
140 VOIDRET sizecmd __P
((char *));
146 static int cmd_bytesz
;
147 static unsigned short cliport
= 0;
155 short implemented
; /* 1 if command is implemented */
159 VOIDRET help __P
((struct tab
*, char *));
161 struct tab cmdtab
[], sitetab
[];
169 SP CRLF COMMA STRING NUMBER
171 USER PASS ACCT REIN QUIT PORT
172 PASV TYPE STRU MODE RETR STOR
173 APPE MLFL MAIL MSND MSOM MSAM
174 MRSQ MRCP ALLO REST RNFR RNTO
175 ABOR DELE CWD LIST NLST SITE
176 STAT HELP NOOP MKD RMD PWD
177 CDUP STOU SMNT SYST SIZE MDTM
187 cmd_list: /* empty */
190 fromname
= (char *) 0;
191 restart_point
= (off_t
) 0;
196 cmd: USER SP username CRLF
201 | PASS SP password CRLF
206 | PORT check_login SP host_port CRLF
213 /* H* port fix, part B: admonish the twit.
214 Also require login before PORT works */
216 if
((cliport
> 1023) && (data_dest.sin_addr.s_addr
> 0)) {
217 reply
(200, "PORT command successful.");
219 syslog
(LOG_WARNING
, "refused %s from %s",
221 reply
(500, "You've GOT to be joking.");
229 | PASV check_login CRLF
231 /* Require login for PASV, too. This actually fixes a bug -- telnet to an
232 unfixed wu-ftpd and type PASV first off, and it crashes! */
237 | TYPE SP type_code CRLF
242 if
(cmd_form
== FORM_N
) {
243 reply
(200, "Type set to A.");
247 reply
(504, "Form must be N.");
251 reply
(504, "Type E not implemented.");
255 reply
(200, "Type set to I.");
261 if
(cmd_bytesz
== 8) {
263 "Type set to L (byte size 8).");
266 reply
(504, "Byte size must be 8.");
267 #else /* NBBY == 8 */
268 UNIMPLEMENTED for NBBY
!= 8
269 #endif /* NBBY == 8 */
272 | STRU SP struct_code CRLF
277 reply
(200, "STRU F ok.");
281 reply
(504, "Unimplemented STRU type.");
284 | MODE SP mode_code CRLF
289 reply
(200, "MODE S ok.");
293 reply
(502, "Unimplemented MODE type.");
296 | ALLO SP NUMBER CRLF
298 reply
(202, "ALLO command ignored.");
300 | ALLO SP NUMBER SP R SP NUMBER CRLF
302 reply
(202, "ALLO command ignored.");
304 | RETR check_login SP pathname CRLF
307 retrieve
((char *) 0, (char *) $4);
311 | STOR check_login SP pathname CRLF
314 store
((char *) $4, "w", 0);
318 | APPE check_login SP pathname CRLF
321 store
((char *) $4, "a", 0);
325 | NLST check_login CRLF
330 | NLST check_login SP STRING CRLF
333 send_file_list
((char *) $4);
337 | LIST check_login CRLF
340 retrieve
(LS_COMMAND
, "");
342 | LIST check_login SP pathname CRLF
346 char buffer
[sizeof
(LS_COMMAND
)+3];
347 strcpy
(buffer
, LS_COMMAND
);
348 strcat
(buffer
, " %s");
349 retrieve
(buffer
, (char *) $4);
354 | STAT check_login SP pathname CRLF
357 statfilecmd
((char *) $4);
365 | DELE check_login SP pathname CRLF
372 | RNTO SP pathname CRLF
375 renamecmd
(fromname
, (char *) $3);
377 fromname
= (char *) 0;
379 reply
(503, "Bad sequence of commands.");
385 reply
(225, "ABOR command successful.");
387 | CWD check_login CRLF
392 | CWD check_login SP pathname CRLF
401 help
(cmdtab
, (char *) 0);
403 | HELP SP STRING CRLF
405 register
char *cp
= (char *)$3;
407 if
(strncasecmp
(cp
, "SITE", 4) == 0) {
414 help
(sitetab
, (char *) 0);
416 help
(cmdtab
, (char *) $3);
420 reply
(200, "NOOP command successful.");
422 | MKD check_login SP pathname CRLF
425 makedir
((char *) $4);
429 | RMD check_login SP pathname CRLF
432 removedir
((char *) $4);
436 | PWD check_login CRLF
441 | CDUP check_login CRLF
448 help
(sitetab
, (char *) 0);
450 | SITE SP HELP SP STRING CRLF
452 help
(sitetab
, (char *) $5);
454 | SITE SP UMASK check_login CRLF
460 (void) umask
(oldmask
);
461 reply
(200, "Current UMASK is %03o", oldmask
);
464 | SITE SP UMASK check_login SP octal_number CRLF
469 if
(($6 == -1) ||
($6 > 0777)) {
470 reply
(501, "Bad UMASK value");
474 "UMASK set to %03o (was %03o)",
479 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
484 "CHMOD: Mode value must be between 0 and 0777");
485 else if
(chmod
((char *) $8, $6) < 0)
486 perror_reply
(550, (char *) $8);
488 reply
(200, "CHMOD command successful.");
496 "Current IDLE time limit is %d seconds; max %d",
497 timeout
, maxtimeout
);
499 | SITE SP IDLE SP NUMBER CRLF
501 if
($5 < 30 ||
$5 > maxtimeout
) {
503 "Maximum IDLE time must be between 30 and %d seconds",
507 (void) alarm
((unsigned) timeout
);
509 "Maximum IDLE time set to %d seconds",
513 | STOU check_login SP pathname CRLF
516 store
((char *) $4, "w", 1);
524 reply
(215, "UNIX Type: L%d Version: BSD-%d",
527 reply
(215, "UNIX Type: L%d", NBBY
);
530 reply
(215, "UNKNOWN Type: L%d", NBBY
);
535 * SIZE is not in RFC959, but Postel has blessed it and
536 * it will be in the updated RFC.
538 * Return size of file in a format suitable for
539 * using with RESTART (we just count bytes).
541 | SIZE check_login SP pathname CRLF
544 sizecmd
((char *) $4);
550 * MDTM is not in RFC959, but Postel has blessed it and
551 * it will be in the updated RFC.
553 * Return modification time of file as an ISO 3307
554 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
555 * where xxx is the fractional second (of any precision,
556 * not necessarily 3 digits)
558 | MDTM check_login SP pathname CRLF
562 if
(stat
((char *) $4, &stbuf
) < 0)
563 perror_reply
(550, (char *) $4);
564 else if
((stbuf.st_mode
&S_IFMT
) != S_IFREG
) {
565 reply
(550, "%s: not a plain file.",
568 register
struct tm
*t
;
570 t
= gmtime
(&stbuf.st_mtime
);
572 "%d%02d%02d%02d%02d%02d",
573 t
->tm_year
+1900, t
->tm_mon
+1, t
->tm_mday
,
574 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
582 reply
(221, "Goodbye.");
590 rcmd: RNFR check_login SP pathname CRLF
594 restart_point
= (off_t
) 0;
596 fromname
= renamefrom
((char *) $4);
597 if
(fromname
== (char *) 0 && $4) {
602 | REST SP byte_size CRLF
606 fromname
= (char *) 0;
608 reply
(350, "Restarting at %ld. %s", restart_point
,
609 "Send STORE or RETRIEVE to initiate transfer.");
616 password: /* empty */
618 *(char **)&($$
) = (char *)calloc
(1, sizeof
(char));
626 host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
629 register
char *a
, *p
;
631 a
= (char *)&data_dest.sin_addr
;
632 a
[0] = $1; a
[1] = $3; a
[2] = $5; a
[3] = $7;
634 /* H* port fix, part A-1: Check the args against the client addr */
635 p
= (char *)&his_addr.sin_addr
;
636 if
(memcmp
(a
, p
, sizeof
(data_dest.sin_addr
)))
637 memset
(a
, 0, sizeof
(data_dest.sin_addr
)); /* XXX */
639 p
= (char *)&data_dest.sin_port
;
641 /* H* port fix, part A-2: only allow client ports in "user space" */
643 cliport
= ($9 << 8) + $11;
644 if
(cliport
> 1023) {
645 p
[0] = $9; p
[1] = $11;
648 p
[0] = $9; p
[1] = $11;
649 data_dest.sin_family
= AF_INET
;
701 /* this is for a bug in the BBN ftp */
740 * Problem: this production is used for all pathname
741 * processing, but only gives a 550 error reply.
742 * This is a valid reply in some cases but not in others.
744 if
(logged_in
&& $1 && strncmp
((char *) $1, "~", 1) == 0) {
745 *(char **)&($$
) = *ftpglob
((char *) $1);
746 if
(globerr
!= NULL
) {
762 register
int ret
, dec
, multby
, digit
;
765 * Convert a number that was read as decimal number
766 * to what it would be if it had been read as octal.
777 ret
+= digit
* multby
;
785 check_login: /* empty */
790 reply
(530, "Please login with USER and PASS.");
798 extern
jmp_buf errcatch
;
800 #define CMD 0 /* beginning of command */
801 #define ARGS 1 /* expect miscellaneous arguments */
802 #define STR1 2 /* expect SP followed by STRING */
803 #define STR2 3 /* expect STRING */
804 #define OSTR 4 /* optional SP then STRING */
805 #define ZSTR1 5 /* SP then optional STRING */
806 #define ZSTR2 6 /* optional STRING after SP */
807 #define SITECMD 7 /* SITE command */
808 #define NSTR 8 /* Number followed by a string */
810 struct tab cmdtab
[] = { /* In order defined in RFC 765 */
811 { "USER", USER
, STR1
, 1, "<sp> username" },
812 { "PASS", PASS
, ZSTR1
, 1, "<sp> password" },
813 { "ACCT", ACCT
, STR1
, 0, "(specify account)" },
814 { "SMNT", SMNT
, ARGS
, 0, "(structure mount)" },
815 { "REIN", REIN
, ARGS
, 0, "(reinitialize server state)" },
816 { "QUIT", QUIT
, ARGS
, 1, "(terminate service)", },
817 { "PORT", PORT
, ARGS
, 1, "<sp> b0, b1, b2, b3, b4" },
818 { "PASV", PASV
, ARGS
, 1, "(set server in passive mode)" },
819 { "TYPE", TYPE
, ARGS
, 1, "<sp> [ A | E | I | L ]" },
820 { "STRU", STRU
, ARGS
, 1, "(specify file structure)" },
821 { "MODE", MODE
, ARGS
, 1, "(specify transfer mode)" },
822 { "RETR", RETR
, STR1
, 1, "<sp> file-name" },
823 { "STOR", STOR
, STR1
, 1, "<sp> file-name" },
824 { "APPE", APPE
, STR1
, 1, "<sp> file-name" },
825 { "MLFL", MLFL
, OSTR
, 0, "(mail file)" },
826 { "MAIL", MAIL
, OSTR
, 0, "(mail to user)" },
827 { "MSND", MSND
, OSTR
, 0, "(mail send to terminal)" },
828 { "MSOM", MSOM
, OSTR
, 0, "(mail send to terminal or mailbox)" },
829 { "MSAM", MSAM
, OSTR
, 0, "(mail send to terminal and mailbox)" },
830 { "MRSQ", MRSQ
, OSTR
, 0, "(mail recipient scheme question)" },
831 { "MRCP", MRCP
, STR1
, 0, "(mail recipient)" },
832 { "ALLO", ALLO
, ARGS
, 1, "allocate storage (vacuously)" },
833 { "REST", REST
, ARGS
, 1, "(restart command)" },
834 { "RNFR", RNFR
, STR1
, 1, "<sp> file-name" },
835 { "RNTO", RNTO
, STR1
, 1, "<sp> file-name" },
836 { "ABOR", ABOR
, ARGS
, 1, "(abort operation)" },
837 { "DELE", DELE
, STR1
, 1, "<sp> file-name" },
838 { "CWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
839 { "XCWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
840 { "LIST", LIST
, OSTR
, 1, "[ <sp> path-name ]" },
841 { "NLST", NLST
, OSTR
, 1, "[ <sp> path-name ]" },
842 { "SITE", SITE
, SITECMD
, 1, "site-cmd [ <sp> arguments ]" },
843 { "SYST", SYST
, ARGS
, 1, "(get type of operating system)" },
844 { "STAT", STAT
, OSTR
, 1, "[ <sp> path-name ]" },
845 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
846 { "NOOP", NOOP
, ARGS
, 1, "" },
847 { "MKD", MKD
, STR1
, 1, "<sp> path-name" },
848 { "XMKD", MKD
, STR1
, 1, "<sp> path-name" },
849 { "RMD", RMD
, STR1
, 1, "<sp> path-name" },
850 { "XRMD", RMD
, STR1
, 1, "<sp> path-name" },
851 { "PWD", PWD
, ARGS
, 1, "(return current directory)" },
852 { "XPWD", PWD
, ARGS
, 1, "(return current directory)" },
853 { "CDUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
854 { "XCUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
855 { "STOU", STOU
, STR1
, 1, "<sp> file-name" },
856 { "SIZE", SIZE
, OSTR
, 1, "<sp> path-name" },
857 { "MDTM", MDTM
, OSTR
, 1, "<sp> path-name" },
861 struct tab sitetab
[] = {
862 { "UMASK", UMASK
, ARGS
, 1, "[ <sp> umask ]" },
863 { "IDLE", IDLE
, ARGS
, 1, "[ <sp> maximum-idle-time ]" },
864 { "CHMOD", CHMOD
, NSTR
, 1, "<sp> mode <sp> file-name" },
865 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
869 struct tab
*lookup FUNCTION
((p
, cmd
), register
struct tab
*p AND
char *cmd
)
872 for
(; p
->name
!= NULL
; p
++)
873 if
(strcmp
(cmd
, p
->name
) == 0)
878 #include <arpa/telnet.h>
881 * getline - a hacked up version of fgets to ignore TELNET escape codes.
883 char *getline FUNCTION
((s
, n
, iop
), char *s AND
int n AND
FILE *iop
)
889 /* tmpline may contain saved command from urgent mode interruption */
890 for
(c
= 0; *(tmpline
+ c
) && --n
> 0; ++c
) {
891 *cs
++ = *(tmpline
+ c
);
892 if
(*(tmpline
+ c
) == '\n') {
895 syslog
(LOG_DEBUG
, "command: %s", s
);
902 while
((c
= getc
(iop
)) != EOF
) {
905 if
((c
= getc
(iop
)) != EOF
) {
911 printf
("%c%c%c", IAC
, DONT
, 0377&c
);
912 (void) fflush
(stdout
);
917 printf
("%c%c%c", IAC
, WONT
, 0377&c
);
918 (void) fflush
(stdout
);
923 continue
; /* ignore command */
928 if
(--n
<= 0 || c
== '\n')
931 if
(c
== EOF
&& cs
== s
)
935 syslog
(LOG_DEBUG
, "command: %s", s
);
939 static VOIDRET toolong FUNCTION
((input
), int input
)
943 reply
(421, "Timeout (%d seconds): closing control connection.", timeout
);
945 syslog
(LOG_INFO
, "User %s timed out after %d seconds at %s",
946 (pw ? pw
-> pw_name
: "unknown"), timeout
, ctime
(&now
));
950 int yylex FUNCTION_NOARGS
952 static int cpos
, state
;
953 register
char *cp
, *cp2
;
954 register
struct tab
*p
;
962 (void) signal
(SIGALRM
, toolong
);
963 (void) alarm
((unsigned) timeout
);
964 if
(getline
(cbuf
, sizeof
(cbuf
)-1, stdin
) == NULL
) {
965 reply
(221, "You could at least say goodbye.");
970 if
(strncasecmp
(cbuf
, "PASS", 4) != NULL
)
971 setproctitle
("%s: %s", proctitle
, cbuf
);
973 if
((cp
= strchr
(cbuf
, '\r'))) {
977 if
((cp
= strpbrk
(cbuf
, " \n")))
984 p
= lookup
(cmdtab
, cbuf
);
987 if
(p
->implemented
== 0) {
993 *(char **)&yylval = p
->name
;
999 if
(cbuf
[cpos
] == ' ') {
1004 if
((cp2
= strpbrk
(cp
, " \n")))
1009 p
= lookup
(sitetab
, cp
);
1012 if
(p
->implemented
== 0) {
1015 longjmp
(errcatch
,0);
1019 *(char **)&yylval = p
->name
;
1026 if
(cbuf
[cpos
] == '\n') {
1035 if
(cbuf
[cpos
] == ' ') {
1037 state
= state
== OSTR ? STR2
: ++state
;
1043 if
(cbuf
[cpos
] == '\n') {
1054 * Make sure the string is nonempty and \n terminated.
1056 if
(n
> 1 && cbuf
[cpos
] == '\n') {
1058 *(char **)&yylval = copy
(cp
);
1066 if
(cbuf
[cpos
] == ' ') {
1070 if
(isdigit
(cbuf
[cpos
])) {
1072 while
(isdigit
(cbuf
[++cpos
]))
1085 if
(isdigit
(cbuf
[cpos
])) {
1087 while
(isdigit
(cbuf
[++cpos
]))
1095 switch
(cbuf
[cpos
++]) {
1159 opiefatal
("Unknown state in scanner.");
1161 yyerror((char *) 0);
1163 longjmp
(errcatch
,0);
1167 VOIDRET upper FUNCTION
((s
), char *s
)
1169 while
(*s
!= '\0') {
1176 char *copy FUNCTION
((s
), char *s
)
1180 p
= malloc
((unsigned) strlen
(s
) + 1);
1182 opiefatal
("Ran out of memory.");
1183 (void) strcpy
(p
, s
);
1187 VOIDRET help FUNCTION
((ctab
, s
), struct tab
*ctab AND
char *s
)
1189 register
struct tab
*c
;
1190 register
int width
, NCMDS
;
1193 if
(ctab
== sitetab
)
1197 width
= 0, NCMDS
= 0;
1198 for
(c
= ctab
; c
->name
!= NULL
; c
++) {
1199 int len
= strlen
(c
->name
);
1205 width
= (width
+ 8) &~
7;
1207 register
int i
, j
, w
;
1210 lreply
(214, "The following %scommands are recognized %s.",
1211 type
, "(* =>'s unimplemented)");
1212 columns
= 76 / width
;
1215 lines
= (NCMDS
+ columns
- 1) / columns
;
1216 for
(i
= 0; i
< lines
; i
++) {
1218 for
(j
= 0; j
< columns
; j
++) {
1219 c
= ctab
+ j
* lines
+ i
;
1220 printf
("%s%c", c
->name
,
1221 c
->implemented ?
' ' : '*');
1222 if
(c
+ lines
>= &ctab
[NCMDS
])
1224 w
= strlen
(c
->name
) + 1;
1232 (void) fflush
(stdout
);
1237 c
= lookup
(ctab
, s
);
1238 if
(c
== (struct tab
*)0) {
1239 reply
(502, "Unknown command %s.", s
);
1243 reply
(214, "Syntax: %s%s %s", type
, c
->name
, c
->help
);
1245 reply
(214, "%s%-*s\t%s; unimplemented.", type
, width
,
1249 VOIDRET sizecmd FUNCTION
((filename
), char *filename
)
1255 if
(stat
(filename
, &stbuf
) < 0 ||
1256 (stbuf.st_mode
&S_IFMT
) != S_IFREG
)
1257 reply
(550, "%s: not a plain file.", filename
);
1259 reply
(213, "%lu", stbuf.st_size
);
1264 register
long count
;
1266 fin
= fopen
(filename
, "r");
1268 perror_reply
(550, filename
);
1271 if
(fstat
(fileno
(fin
), &stbuf
) < 0 ||
1272 (stbuf.st_mode
&S_IFMT
) != S_IFREG
) {
1273 reply
(550, "%s: not a plain file.", filename
);
1279 while
((c
=getc
(fin
)) != EOF
) {
1280 if
(c
== '\n') /* will get expanded to \r\n */
1286 reply
(213, "%ld", count
);
1289 reply
(504, "SIZE not implemented for Type %c.", "?AEIL"[type
]);