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: head/contrib/opie/ftpcmd.y 92914 2002-03-21 23:42:52Z markm $
29 * Copyright (c) 1985, 1988 Regents of the University of California.
30 * All rights reserved.
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 * must display the following acknowledgement:
42 * This product includes software developed by the University of
43 * California, Berkeley and its contributors.
44 * 4. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91
64 * Grammar for FTP commands.
71 #include <sys/param.h>
72 #include <sys/types.h>
73 #include <sys/socket.h>
75 #include <netinet/in.h>
82 #else /* TM_IN_SYS_TIME */
84 #endif /* TM_IN_SYS_TIME */
95 #define LS_COMMAND "/bin/ls -lgA"
96 #else /* HAVE_LS_G_FLAG */
97 #define LS_COMMAND "/bin/ls -lA"
98 #endif /* HAVE_LS_G_FLAG */
100 extern
struct sockaddr_in data_dest
;
101 extern
struct sockaddr_in his_addr
;
102 extern
int logged_in
;
103 extern
struct passwd
*pw
;
109 extern
int maxtimeout
;
111 extern
char *remotehost
;
112 extern
char *proctitle
;
113 extern
char *globerr
;
114 extern
int usedefault
;
115 extern
int transflag
;
116 extern
char tmpline
[];
119 VOIDRET dologout __P
((int));
120 VOIDRET upper __P
((char *));
121 VOIDRET nack __P
((char *));
122 VOIDRET opiefatal __P
((char *));
124 VOIDRET pass __P
((char *));
125 int user __P
((char *));
126 VOIDRET passive __P
((void));
127 VOIDRET retrieve __P
((char *, char *));
128 VOIDRET store __P
((char *, char *, int));
129 VOIDRET send_file_list __P
((char *));
130 VOIDRET statfilecmd __P
((char *));
131 VOIDRET statcmd __P
((void));
132 VOIDRET delete __P
((char *));
133 VOIDRET renamecmd __P
((char *, char *));
134 VOIDRET cwd __P
((char *));
135 VOIDRET makedir __P
((char *));
136 VOIDRET removedir __P
((char *));
137 VOIDRET pwd __P
((void));
139 VOIDRET sizecmd __P
((char *));
145 static int cmd_bytesz
;
146 static unsigned short cliport
= 0;
154 short implemented
; /* 1 if command is implemented */
158 VOIDRET help __P
((struct tab
*, char *));
160 struct tab cmdtab
[], sitetab
[];
168 SP CRLF COMMA STRING NUMBER
170 USER PASS ACCT REIN QUIT PORT
171 PASV TYPE STRU MODE RETR STOR
172 APPE MLFL MAIL MSND MSOM MSAM
173 MRSQ MRCP ALLO REST RNFR RNTO
174 ABOR DELE CWD LIST NLST SITE
175 STAT HELP NOOP MKD RMD PWD
176 CDUP STOU SMNT SYST SIZE MDTM
186 cmd_list: /* empty */
189 fromname
= (char *) 0;
190 restart_point
= (off_t
) 0;
195 cmd: USER SP username CRLF
200 | PASS SP password CRLF
205 | PORT check_login SP host_port CRLF
212 /* H* port fix, part B: admonish the twit.
213 Also require login before PORT works */
215 if
((cliport
> 1023) && (data_dest.sin_addr.s_addr
> 0)) {
216 reply
(200, "PORT command successful.");
218 syslog
(LOG_WARNING
, "refused %s from %s",
220 reply
(500, "You've GOT to be joking.");
228 | PASV check_login CRLF
230 /* Require login for PASV, too. This actually fixes a bug -- telnet to an
231 unfixed wu-ftpd and type PASV first off, and it crashes! */
236 | TYPE SP type_code CRLF
241 if
(cmd_form
== FORM_N
) {
242 reply
(200, "Type set to A.");
246 reply
(504, "Form must be N.");
250 reply
(504, "Type E not implemented.");
254 reply
(200, "Type set to I.");
260 if
(cmd_bytesz
== 8) {
262 "Type set to L (byte size 8).");
265 reply
(504, "Byte size must be 8.");
266 #else /* NBBY == 8 */
267 UNIMPLEMENTED for NBBY
!= 8
268 #endif /* NBBY == 8 */
271 | STRU SP struct_code CRLF
276 reply
(200, "STRU F ok.");
280 reply
(504, "Unimplemented STRU type.");
283 | MODE SP mode_code CRLF
288 reply
(200, "MODE S ok.");
292 reply
(502, "Unimplemented MODE type.");
295 | ALLO SP NUMBER CRLF
297 reply
(202, "ALLO command ignored.");
299 | ALLO SP NUMBER SP R SP NUMBER CRLF
301 reply
(202, "ALLO command ignored.");
303 | RETR check_login SP pathname CRLF
306 retrieve
((char *) 0, (char *) $4);
310 | STOR check_login SP pathname CRLF
313 store
((char *) $4, "w", 0);
317 | APPE check_login SP pathname CRLF
320 store
((char *) $4, "a", 0);
324 | NLST check_login CRLF
329 | NLST check_login SP STRING CRLF
332 send_file_list
((char *) $4);
336 | LIST check_login CRLF
339 retrieve
(LS_COMMAND
, "");
341 | LIST check_login SP pathname CRLF
345 char buffer
[sizeof
(LS_COMMAND
)+3];
346 strcpy
(buffer
, LS_COMMAND
);
347 strcat
(buffer
, " %s");
348 retrieve
(buffer
, (char *) $4);
353 | STAT check_login SP pathname CRLF
356 statfilecmd
((char *) $4);
364 | DELE check_login SP pathname CRLF
371 | RNTO SP pathname CRLF
374 renamecmd
(fromname
, (char *) $3);
376 fromname
= (char *) 0;
378 reply
(503, "Bad sequence of commands.");
384 reply
(225, "ABOR command successful.");
386 | CWD check_login CRLF
391 | CWD check_login SP pathname CRLF
400 help
(cmdtab
, (char *) 0);
402 | HELP SP STRING CRLF
404 register
char *cp
= (char *)$3;
406 if
(strncasecmp
(cp
, "SITE", 4) == 0) {
413 help
(sitetab
, (char *) 0);
415 help
(cmdtab
, (char *) $3);
419 reply
(200, "NOOP command successful.");
421 | MKD check_login SP pathname CRLF
424 makedir
((char *) $4);
428 | RMD check_login SP pathname CRLF
431 removedir
((char *) $4);
435 | PWD check_login CRLF
440 | CDUP check_login CRLF
447 help
(sitetab
, (char *) 0);
449 | SITE SP HELP SP STRING CRLF
451 help
(sitetab
, (char *) $5);
453 | SITE SP UMASK check_login CRLF
459 (void) umask
(oldmask
);
460 reply
(200, "Current UMASK is %03o", oldmask
);
463 | SITE SP UMASK check_login SP octal_number CRLF
468 if
(($6 == -1) ||
($6 > 0777)) {
469 reply
(501, "Bad UMASK value");
473 "UMASK set to %03o (was %03o)",
478 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
483 "CHMOD: Mode value must be between 0 and 0777");
484 else if
(chmod
((char *) $8, $6) < 0)
485 perror_reply
(550, (char *) $8);
487 reply
(200, "CHMOD command successful.");
495 "Current IDLE time limit is %d seconds; max %d",
496 timeout
, maxtimeout
);
498 | SITE SP IDLE SP NUMBER CRLF
500 if
($5 < 30 ||
$5 > maxtimeout
) {
502 "Maximum IDLE time must be between 30 and %d seconds",
506 (void) alarm
((unsigned) timeout
);
508 "Maximum IDLE time set to %d seconds",
512 | STOU check_login SP pathname CRLF
515 store
((char *) $4, "w", 1);
523 reply
(215, "UNIX Type: L%d Version: BSD-%d",
526 reply
(215, "UNIX Type: L%d", NBBY
);
529 reply
(215, "UNKNOWN Type: L%d", NBBY
);
534 * SIZE is not in RFC959, but Postel has blessed it and
535 * it will be in the updated RFC.
537 * Return size of file in a format suitable for
538 * using with RESTART (we just count bytes).
540 | SIZE check_login SP pathname CRLF
543 sizecmd
((char *) $4);
549 * MDTM is not in RFC959, but Postel has blessed it and
550 * it will be in the updated RFC.
552 * Return modification time of file as an ISO 3307
553 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
554 * where xxx is the fractional second (of any precision,
555 * not necessarily 3 digits)
557 | MDTM check_login SP pathname CRLF
561 if
(stat
((char *) $4, &stbuf
) < 0)
562 perror_reply
(550, (char *) $4);
563 else if
((stbuf.st_mode
&S_IFMT
) != S_IFREG
) {
564 reply
(550, "%s: not a plain file.",
567 register
struct tm
*t
;
569 t
= gmtime
(&stbuf.st_mtime
);
571 "%d%02d%02d%02d%02d%02d",
572 t
->tm_year
+1900, t
->tm_mon
+1, t
->tm_mday
,
573 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
581 reply
(221, "Goodbye.");
589 rcmd: RNFR check_login SP pathname CRLF
593 restart_point
= (off_t
) 0;
595 fromname
= renamefrom
((char *) $4);
596 if
(fromname
== (char *) 0 && $4) {
601 | REST SP byte_size CRLF
605 fromname
= (char *) 0;
607 reply
(350, "Restarting at %ld. %s", restart_point
,
608 "Send STORE or RETRIEVE to initiate transfer.");
615 password: /* empty */
617 *(char **)&($$
) = (char *)calloc
(1, sizeof
(char));
625 host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
628 register
char *a
, *p
;
630 a
= (char *)&data_dest.sin_addr
;
631 a
[0] = $1; a
[1] = $3; a
[2] = $5; a
[3] = $7;
633 /* H* port fix, part A-1: Check the args against the client addr */
634 p
= (char *)&his_addr.sin_addr
;
635 if
(memcmp
(a
, p
, sizeof
(data_dest.sin_addr
)))
636 memset
(a
, 0, sizeof
(data_dest.sin_addr
)); /* XXX */
638 p
= (char *)&data_dest.sin_port
;
640 /* H* port fix, part A-2: only allow client ports in "user space" */
642 cliport
= ($9 << 8) + $11;
643 if
(cliport
> 1023) {
644 p
[0] = $9; p
[1] = $11;
647 p
[0] = $9; p
[1] = $11;
648 data_dest.sin_family
= AF_INET
;
700 /* this is for a bug in the BBN ftp */
739 * Problem: this production is used for all pathname
740 * processing, but only gives a 550 error reply.
741 * This is a valid reply in some cases but not in others.
743 if
(logged_in
&& $1 && strncmp
((char *) $1, "~", 1) == 0) {
744 *(char **)&($$
) = *ftpglob
((char *) $1);
745 if
(globerr
!= NULL
) {
761 register
int ret
, dec
, multby
, digit
;
764 * Convert a number that was read as decimal number
765 * to what it would be if it had been read as octal.
776 ret
+= digit
* multby
;
784 check_login: /* empty */
789 reply
(530, "Please login with USER and PASS.");
797 extern
jmp_buf errcatch
;
799 #define CMD 0 /* beginning of command */
800 #define ARGS 1 /* expect miscellaneous arguments */
801 #define STR1 2 /* expect SP followed by STRING */
802 #define STR2 3 /* expect STRING */
803 #define OSTR 4 /* optional SP then STRING */
804 #define ZSTR1 5 /* SP then optional STRING */
805 #define ZSTR2 6 /* optional STRING after SP */
806 #define SITECMD 7 /* SITE command */
807 #define NSTR 8 /* Number followed by a string */
809 struct tab cmdtab
[] = { /* In order defined in RFC 765 */
810 { "USER", USER
, STR1
, 1, "<sp> username" },
811 { "PASS", PASS
, ZSTR1
, 1, "<sp> password" },
812 { "ACCT", ACCT
, STR1
, 0, "(specify account)" },
813 { "SMNT", SMNT
, ARGS
, 0, "(structure mount)" },
814 { "REIN", REIN
, ARGS
, 0, "(reinitialize server state)" },
815 { "QUIT", QUIT
, ARGS
, 1, "(terminate service)", },
816 { "PORT", PORT
, ARGS
, 1, "<sp> b0, b1, b2, b3, b4" },
817 { "PASV", PASV
, ARGS
, 1, "(set server in passive mode)" },
818 { "TYPE", TYPE
, ARGS
, 1, "<sp> [ A | E | I | L ]" },
819 { "STRU", STRU
, ARGS
, 1, "(specify file structure)" },
820 { "MODE", MODE
, ARGS
, 1, "(specify transfer mode)" },
821 { "RETR", RETR
, STR1
, 1, "<sp> file-name" },
822 { "STOR", STOR
, STR1
, 1, "<sp> file-name" },
823 { "APPE", APPE
, STR1
, 1, "<sp> file-name" },
824 { "MLFL", MLFL
, OSTR
, 0, "(mail file)" },
825 { "MAIL", MAIL
, OSTR
, 0, "(mail to user)" },
826 { "MSND", MSND
, OSTR
, 0, "(mail send to terminal)" },
827 { "MSOM", MSOM
, OSTR
, 0, "(mail send to terminal or mailbox)" },
828 { "MSAM", MSAM
, OSTR
, 0, "(mail send to terminal and mailbox)" },
829 { "MRSQ", MRSQ
, OSTR
, 0, "(mail recipient scheme question)" },
830 { "MRCP", MRCP
, STR1
, 0, "(mail recipient)" },
831 { "ALLO", ALLO
, ARGS
, 1, "allocate storage (vacuously)" },
832 { "REST", REST
, ARGS
, 1, "(restart command)" },
833 { "RNFR", RNFR
, STR1
, 1, "<sp> file-name" },
834 { "RNTO", RNTO
, STR1
, 1, "<sp> file-name" },
835 { "ABOR", ABOR
, ARGS
, 1, "(abort operation)" },
836 { "DELE", DELE
, STR1
, 1, "<sp> file-name" },
837 { "CWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
838 { "XCWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
839 { "LIST", LIST
, OSTR
, 1, "[ <sp> path-name ]" },
840 { "NLST", NLST
, OSTR
, 1, "[ <sp> path-name ]" },
841 { "SITE", SITE
, SITECMD
, 1, "site-cmd [ <sp> arguments ]" },
842 { "SYST", SYST
, ARGS
, 1, "(get type of operating system)" },
843 { "STAT", STAT
, OSTR
, 1, "[ <sp> path-name ]" },
844 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
845 { "NOOP", NOOP
, ARGS
, 1, "" },
846 { "MKD", MKD
, STR1
, 1, "<sp> path-name" },
847 { "XMKD", MKD
, STR1
, 1, "<sp> path-name" },
848 { "RMD", RMD
, STR1
, 1, "<sp> path-name" },
849 { "XRMD", RMD
, STR1
, 1, "<sp> path-name" },
850 { "PWD", PWD
, ARGS
, 1, "(return current directory)" },
851 { "XPWD", PWD
, ARGS
, 1, "(return current directory)" },
852 { "CDUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
853 { "XCUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
854 { "STOU", STOU
, STR1
, 1, "<sp> file-name" },
855 { "SIZE", SIZE
, OSTR
, 1, "<sp> path-name" },
856 { "MDTM", MDTM
, OSTR
, 1, "<sp> path-name" },
860 struct tab sitetab
[] = {
861 { "UMASK", UMASK
, ARGS
, 1, "[ <sp> umask ]" },
862 { "IDLE", IDLE
, ARGS
, 1, "[ <sp> maximum-idle-time ]" },
863 { "CHMOD", CHMOD
, NSTR
, 1, "<sp> mode <sp> file-name" },
864 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
868 struct tab
*lookup FUNCTION
((p
, cmd
), register
struct tab
*p AND
char *cmd
)
871 for
(; p
->name
!= NULL
; p
++)
872 if
(strcmp
(cmd
, p
->name
) == 0)
877 #include <arpa/telnet.h>
880 * getline - a hacked up version of fgets to ignore TELNET escape codes.
882 char *getline FUNCTION
((s
, n
, iop
), char *s AND
int n AND
FILE *iop
)
888 /* tmpline may contain saved command from urgent mode interruption */
889 for
(c
= 0; *(tmpline
+ c
) && --n
> 0; ++c
) {
890 *cs
++ = *(tmpline
+ c
);
891 if
(*(tmpline
+ c
) == '\n') {
894 syslog
(LOG_DEBUG
, "command: %s", s
);
901 while
((c
= getc
(iop
)) != EOF
) {
904 if
((c
= getc
(iop
)) != EOF
) {
910 printf
("%c%c%c", IAC
, DONT
, 0377&c
);
911 (void) fflush
(stdout
);
916 printf
("%c%c%c", IAC
, WONT
, 0377&c
);
917 (void) fflush
(stdout
);
922 continue
; /* ignore command */
927 if
(--n
<= 0 || c
== '\n')
930 if
(c
== EOF
&& cs
== s
)
934 syslog
(LOG_DEBUG
, "command: %s", s
);
938 static VOIDRET toolong FUNCTION
((input
), int input
)
942 reply
(421, "Timeout (%d seconds): closing control connection.", timeout
);
944 syslog
(LOG_INFO
, "User %s timed out after %d seconds at %s",
945 (pw ? pw
-> pw_name
: "unknown"), timeout
, ctime
(&now
));
949 int yylex FUNCTION_NOARGS
951 static int cpos
, state
;
952 register
char *cp
, *cp2
;
953 register
struct tab
*p
;
961 (void) signal
(SIGALRM
, toolong
);
962 (void) alarm
((unsigned) timeout
);
963 if
(getline
(cbuf
, sizeof
(cbuf
)-1, stdin
) == NULL
) {
964 reply
(221, "You could at least say goodbye.");
969 if
(strncasecmp
(cbuf
, "PASS", 4) != NULL
)
970 setproctitle
("%s: %s", proctitle
, cbuf
);
972 if
((cp
= strchr
(cbuf
, '\r'))) {
976 if
((cp
= strpbrk
(cbuf
, " \n")))
983 p
= lookup
(cmdtab
, cbuf
);
986 if
(p
->implemented
== 0) {
992 *(char **)&yylval = p
->name
;
998 if
(cbuf
[cpos
] == ' ') {
1003 if
((cp2
= strpbrk
(cp
, " \n")))
1008 p
= lookup
(sitetab
, cp
);
1011 if
(p
->implemented
== 0) {
1014 longjmp
(errcatch
,0);
1018 *(char **)&yylval = p
->name
;
1025 if
(cbuf
[cpos
] == '\n') {
1034 if
(cbuf
[cpos
] == ' ') {
1036 state
= state
== OSTR ? STR2
: ++state
;
1042 if
(cbuf
[cpos
] == '\n') {
1053 * Make sure the string is nonempty and \n terminated.
1055 if
(n
> 1 && cbuf
[cpos
] == '\n') {
1057 *(char **)&yylval = copy
(cp
);
1065 if
(cbuf
[cpos
] == ' ') {
1069 if
(isdigit
(cbuf
[cpos
])) {
1071 while
(isdigit
(cbuf
[++cpos
]))
1084 if
(isdigit
(cbuf
[cpos
])) {
1086 while
(isdigit
(cbuf
[++cpos
]))
1094 switch
(cbuf
[cpos
++]) {
1158 opiefatal
("Unknown state in scanner.");
1160 yyerror((char *) 0);
1162 longjmp
(errcatch
,0);
1166 VOIDRET upper FUNCTION
((s
), char *s
)
1168 while
(*s
!= '\0') {
1175 char *copy FUNCTION
((s
), char *s
)
1179 p
= malloc
((unsigned) strlen
(s
) + 1);
1181 opiefatal
("Ran out of memory.");
1182 (void) strcpy
(p
, s
);
1186 VOIDRET help FUNCTION
((ctab
, s
), struct tab
*ctab AND
char *s
)
1188 register
struct tab
*c
;
1189 register
int width
, NCMDS
;
1192 if
(ctab
== sitetab
)
1196 width
= 0, NCMDS
= 0;
1197 for
(c
= ctab
; c
->name
!= NULL
; c
++) {
1198 int len
= strlen
(c
->name
);
1204 width
= (width
+ 8) &~
7;
1206 register
int i
, j
, w
;
1209 lreply
(214, "The following %scommands are recognized %s.",
1210 type
, "(* =>'s unimplemented)");
1211 columns
= 76 / width
;
1214 lines
= (NCMDS
+ columns
- 1) / columns
;
1215 for
(i
= 0; i
< lines
; i
++) {
1217 for
(j
= 0; j
< columns
; j
++) {
1218 c
= ctab
+ j
* lines
+ i
;
1219 printf
("%s%c", c
->name
,
1220 c
->implemented ?
' ' : '*');
1221 if
(c
+ lines
>= &ctab
[NCMDS
])
1223 w
= strlen
(c
->name
) + 1;
1231 (void) fflush
(stdout
);
1236 c
= lookup
(ctab
, s
);
1237 if
(c
== (struct tab
*)0) {
1238 reply
(502, "Unknown command %s.", s
);
1242 reply
(214, "Syntax: %s%s %s", type
, c
->name
, c
->help
);
1244 reply
(214, "%s%-*s\t%s; unimplemented.", type
, width
,
1248 VOIDRET sizecmd FUNCTION
((filename
), char *filename
)
1254 if
(stat
(filename
, &stbuf
) < 0 ||
1255 (stbuf.st_mode
&S_IFMT
) != S_IFREG
)
1256 reply
(550, "%s: not a plain file.", filename
);
1258 reply
(213, "%lu", stbuf.st_size
);
1263 register
long count
;
1265 fin
= fopen
(filename
, "r");
1267 perror_reply
(550, filename
);
1270 if
(fstat
(fileno
(fin
), &stbuf
) < 0 ||
1271 (stbuf.st_mode
&S_IFMT
) != S_IFREG
) {
1272 reply
(550, "%s: not a plain file.", filename
);
1278 while
((c
=getc
(fin
)) != EOF
) {
1279 if
(c
== '\n') /* will get expanded to \r\n */
1285 reply
(213, "%ld", count
);
1288 reply
(504, "SIZE not implemented for Type %c.", "?AEIL"[type
]);