1 /* $NetBSD: ftpcmd.y,v 1.6 1995/06/03 22:46:45 mycroft Exp $ */
4 * Copyright (c) 1985, 1988, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
39 * Grammar for FTP commands.
45 #include "ftpd_locl.h"
50 static int hasyyerrored
;
55 static int cmd_bytesz
;
63 short implemented
; /* 1 if command is implemented */
67 extern
struct tab cmdtab
[];
68 extern
struct tab sitetab
[];
70 static char *copy
(char *);
71 static void help
(struct tab
*, char *);
73 lookup
(struct tab
*, char *);
74 static void sizecmd
(char *);
75 static RETSIGTYPE toolong
(int);
76 static int yylex (void);
78 /* This is for bison */
80 #if !defined(alloca) && !defined(HAVE_ALLOCA)
81 #define alloca(x) malloc(x)
97 USER PASS ACCT REIN QUIT PORT
98 PASV TYPE STRU MODE RETR STOR
99 APPE MLFL MAIL MSND MSOM MSAM
100 MRSQ MRCP ALLO REST RNFR RNTO
101 ABOR DELE CWD LIST NLST SITE
102 sTAT HELP NOOP MKD RMD PWD
103 CDUP STOU SMNT SYST SIZE MDTM
108 AUTH ADAT PROT PBSZ CCC MIC
111 KAUTH KLIST KDESTROY KRBTKFILE AFSLOG
121 %type
<i
> check_login check_login_no_guest check_secure octal_number byte_size
122 %type
<i
> struct_code mode_code type_code form_code
123 %type
<s
> pathstring pathname password username
133 fromname
= (char *) 0;
134 restart_point
= (off_t
) 0;
140 : USER SP username CRLF check_secure
146 | PASS SP password CRLF check_secure
150 memset
($3, 0, strlen
($3));
154 | PORT SP host_port CRLF check_secure
158 (data_dest
->sa_family
!= his_addr
->sa_family ||
159 (socket_get_port
(data_dest
) < IPPORT_RESERVED
) ||
160 memcmp
(socket_get_address
(data_dest
),
161 socket_get_address
(his_addr
),
162 socket_addr_size
(his_addr
)) != 0)) {
164 reply
(500, "Illegal PORT range rejected.");
171 reply
(200, "PORT command successful.");
175 | EPRT SP STRING CRLF check_secure
181 | PASV CRLF check_login
186 | EPSV CRLF check_login
191 | EPSV SP STRING CRLF check_login
197 | TYPE SP type_code CRLF check_secure
203 if
(cmd_form
== FORM_N
) {
204 reply
(200, "Type set to A.");
208 reply
(504, "Form must be N.");
212 reply
(504, "Type E not implemented.");
216 reply
(200, "Type set to I.");
222 if
(cmd_bytesz
== 8) {
224 "Type set to L (byte size 8).");
227 reply
(504, "Byte size must be 8.");
228 #else /* NBBY == 8 */
229 UNIMPLEMENTED for NBBY
!= 8
230 #endif /* NBBY == 8 */
234 | STRU SP struct_code CRLF check_secure
240 reply
(200, "STRU F ok.");
244 reply
(504, "Unimplemented STRU type.");
248 | MODE SP mode_code CRLF check_secure
254 reply
(200, "MODE S ok.");
258 reply
(502, "Unimplemented MODE type.");
262 | ALLO SP NUMBER CRLF check_secure
265 reply
(202, "ALLO command ignored.");
268 | ALLO SP NUMBER SP R SP NUMBER CRLF check_secure
271 reply
(202, "ALLO command ignored.");
274 | RETR SP pathname CRLF check_login
278 if
($5 && name
!= NULL
)
283 | STOR SP pathname CRLF check_login
287 if
($5 && name
!= NULL
)
288 do_store
(name
, "w", 0);
292 | APPE SP pathname CRLF check_login
296 if
($5 && name
!= NULL
)
297 do_store
(name
, "a", 0);
301 | NLST CRLF check_login
306 | NLST SP STRING CRLF check_login
310 if
($5 && name
!= NULL
)
311 send_file_list
(name
);
315 | LIST CRLF check_login
320 | LIST SP pathname CRLF check_login
326 | sTAT SP pathname CRLF check_login
328 if
($5 && $3 != NULL
)
333 | sTAT CRLF check_secure
338 | DELE SP pathname CRLF check_login_no_guest
340 if
($5 && $3 != NULL
)
345 | RNTO SP pathname CRLF check_login_no_guest
349 renamecmd
(fromname
, $3);
351 fromname
= (char *) 0;
353 reply
(503, "Bad sequence of commands.");
359 | ABOR CRLF check_secure
362 reply
(225, "ABOR command successful.");
364 | CWD CRLF check_login
367 const char *path
= pw
->pw_dir
;
368 if
(dochroot || guest
)
373 | CWD SP pathname CRLF check_login
375 if
($5 && $3 != NULL
)
380 | HELP CRLF check_secure
383 help
(cmdtab
, (char *) 0);
385 | HELP SP STRING CRLF check_secure
390 if
(strncasecmp
(cp
, "SITE", 4) == 0) {
397 help
(sitetab
, (char *) 0);
402 | NOOP CRLF check_secure
405 reply
(200, "NOOP command successful.");
407 | MKD SP pathname CRLF check_login
409 if
($5 && $3 != NULL
)
414 | RMD SP pathname CRLF check_login_no_guest
416 if
($5 && $3 != NULL
)
421 | PWD CRLF check_login
426 | CDUP CRLF check_login
431 | FEAT CRLF check_secure
434 lreply
(211, "Supported features:");
436 lreply
(0, " REST STREAM");
441 | OPTS SP STRING CRLF check_secure
444 reply
(501, "Bad options");
448 | SITE SP HELP CRLF check_secure
451 help
(sitetab
, (char *) 0);
453 | SITE SP HELP SP STRING CRLF check_secure
458 | SITE SP UMASK CRLF check_login
461 int oldmask
= umask
(0);
463 reply
(200, "Current UMASK is %03o", oldmask
);
466 | SITE SP UMASK SP octal_number CRLF check_login_no_guest
469 if
(($5 == -1) ||
($5 > 0777)) {
470 reply
(501, "Bad UMASK value");
472 int oldmask
= umask
($5);
474 "UMASK set to %03o (was %03o)",
479 | SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest
481 if
($9 && $7 != NULL
) {
484 "CHMOD: Mode value must be between 0 and 0777");
485 else if
(chmod
($7, $5) < 0)
486 perror_reply
(550, $7);
488 reply
(200, "CHMOD command successful.");
493 | SITE SP IDLE CRLF check_secure
497 "Current IDLE time limit is %d seconds; max %d",
498 ftpd_timeout
, maxtimeout
);
500 | SITE SP IDLE SP NUMBER CRLF check_secure
503 if
($5 < 30 ||
$5 > maxtimeout
) {
505 "Maximum IDLE time must be between 30 and %d seconds",
509 alarm
((unsigned) ftpd_timeout
);
511 "Maximum IDLE time set to %d seconds",
517 | SITE SP KAUTH SP STRING CRLF check_login
519 reply
(500, "Command not implemented.");
521 | SITE SP KLIST CRLF check_login
526 | SITE SP KDESTROY CRLF check_login
528 reply
(500, "Command not implemented.");
530 | SITE SP KRBTKFILE SP STRING CRLF check_login
532 reply
(500, "Command not implemented.");
534 | SITE SP AFSLOG CRLF check_login
538 reply
(500, "Can't be done as guest.");
542 reply
(500, "Command not implemented.");
545 | SITE SP AFSLOG SP STRING CRLF check_login
549 reply
(500, "Can't be done as guest.");
555 reply
(500, "Command not implemented.");
558 | SITE SP LOCATE SP STRING CRLF check_login
565 | SITE SP URL CRLF check_secure
568 reply
(200, "http://www.pdc.kth.se/heimdal/");
570 | STOU SP pathname CRLF check_login
572 if
($5 && $3 != NULL
)
573 do_store
($3, "w", 1);
577 | SYST CRLF check_secure
580 #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
581 reply
(215, "UNIX Type: L%d", NBBY
);
583 reply
(215, "UNKNOWN Type: L%d", NBBY
);
589 * SIZE is not in RFC959, but Postel has blessed it and
590 * it will be in the updated RFC.
592 * Return size of file in a format suitable for
593 * using with RESTART (we just count bytes).
595 | SIZE SP pathname CRLF check_login
597 if
($5 && $3 != NULL
)
604 * MDTM is not in RFC959, but Postel has blessed it and
605 * it will be in the updated RFC.
607 * Return modification time of file as an ISO 3307
608 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
609 * where xxx is the fractional second (of any precision,
610 * not necessarily 3 digits)
612 | MDTM SP pathname CRLF check_login
614 if
($5 && $3 != NULL
) {
616 if
(stat
($3, &stbuf
) < 0)
618 $3, strerror
(errno
));
619 else if
(!S_ISREG
(stbuf.st_mode
)) {
621 "%s: not a plain file.", $3);
624 time_t mtime
= stbuf.st_mtime
;
628 "%04d%02d%02d%02d%02d%02d",
640 | QUIT CRLF check_secure
643 reply
(221, "Goodbye.");
653 : RNFR SP pathname CRLF check_login_no_guest
655 restart_point
= (off_t
) 0;
657 fromname
= renamefrom
($3);
658 if
(fromname
== (char *) 0 && $3) {
663 | REST SP byte_size CRLF check_secure
666 fromname
= (char *) 0;
667 restart_point
= $3; /* XXX $3 is only "int" */
668 reply
(350, "Restarting at %ld. %s",
670 "Send STORE or RETRIEVE to initiate transfer.");
673 | AUTH SP STRING CRLF
678 | ADAT SP STRING CRLF
683 | PBSZ SP NUMBER CRLF check_secure
688 | PROT SP STRING CRLF check_secure
693 | CCC CRLF check_secure
703 | CONF SP STRING CRLF
705 mec
($3, prot_confidential
);
710 mec
($3, prot_private
);
722 $$
= (char *)calloc
(1, sizeof
(char));
732 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
735 struct sockaddr_in
*sin4
= (struct sockaddr_in
*)data_dest
;
737 sin4
->sin_family
= AF_INET
;
738 sin4
->sin_port
= htons
($9 * 256 + $11);
739 sin4
->sin_addr.s_addr
=
740 htonl
(($1 << 24) |
($3 << 16) |
($5 << 8) |
$7);
794 /* this is for a bug in the BBN ftp */
836 * Problem: this production is used for all pathname
837 * processing, but only gives a 550 error reply.
838 * This is a valid reply in some cases but not in others.
840 if
(logged_in
&& $1 && *$1 == '~') {
843 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE
;
845 memset
(&gl
, 0, sizeof
(gl
));
846 if
(glob
($1, flags
, NULL
, &gl
) ||
848 reply
(550, "not found");
851 $$
= strdup
(gl.gl_pathv
[0]);
867 int ret
, dec
, multby
, digit
;
870 * Convert a number that was read as decimal number
871 * to what it would be if it had been read as octal.
882 ret
+= digit
* multby
;
891 check_login_no_guest
: check_login
895 reply
(550, "Permission denied");
899 check_login
: check_secure
902 if
(($$
= logged_in
) == 0)
903 reply
(530, "Please login with USER and PASS.");
909 check_secure
: /* empty */
912 if
(sec_complete
&& !ccc_passed
&& !secure_command
()) {
914 reply
(533, "Command protection level denied "
915 "for paranoid reasons.");
922 #define CMD 0 /* beginning of command */
923 #define ARGS 1 /* expect miscellaneous arguments */
924 #define STR1 2 /* expect SP followed by STRING */
925 #define STR2 3 /* expect STRING */
926 #define OSTR 4 /* optional SP then STRING */
927 #define ZSTR1 5 /* SP then optional STRING */
928 #define ZSTR2 6 /* optional STRING after SP */
929 #define SITECMD 7 /* SITE command */
930 #define NSTR 8 /* Number followed by a string */
932 struct tab cmdtab
[] = { /* In order defined in RFC 765 */
933 { "USER", USER
, STR1
, 1, "<sp> username" },
934 { "PASS", PASS
, ZSTR1
, 1, "<sp> password" },
935 { "ACCT", ACCT
, STR1
, 0, "(specify account)" },
936 { "SMNT", SMNT
, ARGS
, 0, "(structure mount)" },
937 { "REIN", REIN
, ARGS
, 0, "(reinitialize server state)" },
938 { "QUIT", QUIT
, ARGS
, 1, "(terminate service)", },
939 { "PORT", PORT
, ARGS
, 1, "<sp> b0, b1, b2, b3, b4" },
940 { "EPRT", EPRT
, STR1
, 1, "<sp> string" },
941 { "PASV", PASV
, ARGS
, 1, "(set server in passive mode)" },
942 { "EPSV", EPSV
, OSTR
, 1, "[<sp> foo]" },
943 { "TYPE", TYPE
, ARGS
, 1, "<sp> [ A | E | I | L ]" },
944 { "STRU", STRU
, ARGS
, 1, "(specify file structure)" },
945 { "MODE", MODE
, ARGS
, 1, "(specify transfer mode)" },
946 { "RETR", RETR
, STR1
, 1, "<sp> file-name" },
947 { "STOR", STOR
, STR1
, 1, "<sp> file-name" },
948 { "APPE", APPE
, STR1
, 1, "<sp> file-name" },
949 { "MLFL", MLFL
, OSTR
, 0, "(mail file)" },
950 { "MAIL", MAIL
, OSTR
, 0, "(mail to user)" },
951 { "MSND", MSND
, OSTR
, 0, "(mail send to terminal)" },
952 { "MSOM", MSOM
, OSTR
, 0, "(mail send to terminal or mailbox)" },
953 { "MSAM", MSAM
, OSTR
, 0, "(mail send to terminal and mailbox)" },
954 { "MRSQ", MRSQ
, OSTR
, 0, "(mail recipient scheme question)" },
955 { "MRCP", MRCP
, STR1
, 0, "(mail recipient)" },
956 { "ALLO", ALLO
, ARGS
, 1, "allocate storage (vacuously)" },
957 { "REST", REST
, ARGS
, 1, "<sp> offset (restart command)" },
958 { "RNFR", RNFR
, STR1
, 1, "<sp> file-name" },
959 { "RNTO", RNTO
, STR1
, 1, "<sp> file-name" },
960 { "ABOR", ABOR
, ARGS
, 1, "(abort operation)" },
961 { "DELE", DELE
, STR1
, 1, "<sp> file-name" },
962 { "CWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
963 { "XCWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
964 { "LIST", LIST
, OSTR
, 1, "[ <sp> path-name ]" },
965 { "NLST", NLST
, OSTR
, 1, "[ <sp> path-name ]" },
966 { "SITE", SITE
, SITECMD
, 1, "site-cmd [ <sp> arguments ]" },
967 { "SYST", SYST
, ARGS
, 1, "(get type of operating system)" },
968 { "STAT", sTAT
, OSTR
, 1, "[ <sp> path-name ]" },
969 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
970 { "NOOP", NOOP
, ARGS
, 1, "" },
971 { "MKD", MKD
, STR1
, 1, "<sp> path-name" },
972 { "XMKD", MKD
, STR1
, 1, "<sp> path-name" },
973 { "RMD", RMD
, STR1
, 1, "<sp> path-name" },
974 { "XRMD", RMD
, STR1
, 1, "<sp> path-name" },
975 { "PWD", PWD
, ARGS
, 1, "(return current directory)" },
976 { "XPWD", PWD
, ARGS
, 1, "(return current directory)" },
977 { "CDUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
978 { "XCUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
979 { "STOU", STOU
, STR1
, 1, "<sp> file-name" },
980 { "SIZE", SIZE
, OSTR
, 1, "<sp> path-name" },
981 { "MDTM", MDTM
, OSTR
, 1, "<sp> path-name" },
983 /* extensions from RFC2228 */
984 { "AUTH", AUTH
, STR1
, 1, "<sp> auth-type" },
985 { "ADAT", ADAT
, STR1
, 1, "<sp> auth-data" },
986 { "PBSZ", PBSZ
, ARGS
, 1, "<sp> buffer-size" },
987 { "PROT", PROT
, STR1
, 1, "<sp> prot-level" },
988 { "CCC", CCC
, ARGS
, 1, "" },
989 { "MIC", MIC
, STR1
, 1, "<sp> integrity command" },
990 { "CONF", CONF
, STR1
, 1, "<sp> confidentiality command" },
991 { "ENC", ENC
, STR1
, 1, "<sp> privacy command" },
994 { "FEAT", FEAT
, ARGS
, 1, "" },
995 { "OPTS", OPTS
, ARGS
, 1, "<sp> command [<sp> options]" },
1000 struct tab sitetab
[] = {
1001 { "UMASK", UMASK
, ARGS
, 1, "[ <sp> umask ]" },
1002 { "IDLE", IDLE
, ARGS
, 1, "[ <sp> maximum-idle-time ]" },
1003 { "CHMOD", CHMOD
, NSTR
, 1, "<sp> mode <sp> file-name" },
1004 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
1006 { "KAUTH", KAUTH
, STR1
, 1, "<sp> principal [ <sp> ticket ]" },
1007 { "KLIST", KLIST
, ARGS
, 1, "(show ticket file)" },
1008 { "KDESTROY", KDESTROY
, ARGS
, 1, "(destroy tickets)" },
1009 { "KRBTKFILE", KRBTKFILE
, STR1
, 1, "<sp> ticket-file" },
1010 { "AFSLOG", AFSLOG
, OSTR
, 1, "[<sp> cell]" },
1012 { "LOCATE", LOCATE
, STR1
, 1, "<sp> globexpr" },
1013 { "FIND", LOCATE
, STR1
, 1, "<sp> globexpr" },
1015 { "URL", URL
, ARGS
, 1, "?" },
1017 { NULL
, 0, 0, 0, 0 }
1021 lookup
(struct tab
*p
, char *cmd
)
1024 for
(; p
->name
!= NULL
; p
++)
1025 if
(strcmp
(cmd
, p
->name
) == 0)
1031 * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1034 ftpd_getline
(char *s
, int n
)
1041 /* might still be data within the security MIC/CONF/ENC */
1043 strlcpy
(s
, ftp_command
, n
);
1045 syslog
(LOG_DEBUG
, "command: %s", s
);
1048 while
((c
= getc
(stdin
)) != EOF
) {
1051 if
((c
= getc
(stdin
)) != EOF
) {
1057 printf
("%c%c%c", IAC
, DONT
, 0377&c
);
1063 printf
("%c%c%c", IAC
, WONT
, 0377&c
);
1069 continue
; /* ignore command */
1074 if
(--n
<= 0 || c
== '\n')
1077 if
(c
== EOF
&& cs
== s
)
1081 if
(!guest
&& strncasecmp
("pass ", s
, 5) == 0) {
1082 /* Don't syslog passwords */
1083 syslog
(LOG_DEBUG
, "command: %.5s ???", s
);
1088 /* Don't syslog trailing CR-LF */
1091 while
(cp
>= s
&& (*cp
== '\n' ||
*cp
== '\r')) {
1095 syslog
(LOG_DEBUG
, "command: %.*s", len
, s
);
1099 fprintf
(stderr
, "%s\n", s
);
1109 "Timeout (%d seconds): closing control connection.",
1112 syslog
(LOG_INFO
, "User %s timed out after %d seconds",
1113 (pw ? pw
-> pw_name
: "unknown"), ftpd_timeout
);
1121 static int cpos
, state
;
1133 signal
(SIGALRM
, toolong
);
1134 alarm
((unsigned) ftpd_timeout
);
1135 if
(ftpd_getline
(cbuf
, sizeof
(cbuf
)-1) == NULL
) {
1136 reply
(221, "You could at least say goodbye.");
1140 #ifdef HAVE_SETPROCTITLE
1141 if
(strncasecmp
(cbuf
, "PASS", 4) != 0)
1142 setproctitle
("%s: %s", proctitle
, cbuf
);
1143 #endif /* HAVE_SETPROCTITLE */
1144 if
((cp
= strchr
(cbuf
, '\r'))) {
1148 if
((cp
= strpbrk
(cbuf
, " \n")))
1155 p
= lookup
(cmdtab
, cbuf
);
1158 if
(p
->implemented
== 0) {
1170 if
(cbuf
[cpos
] == ' ') {
1175 if
((cp2
= strpbrk
(cp
, " \n")))
1180 p
= lookup
(sitetab
, cp
);
1183 if
(p
->implemented
== 0) {
1197 if
(cbuf
[cpos
] == '\n') {
1206 if
(cbuf
[cpos
] == ' ') {
1217 if
(cbuf
[cpos
] == '\n') {
1228 * Make sure the string is nonempty and \n terminated.
1230 if
(n
> 1 && cbuf
[cpos
] == '\n') {
1232 yylval.s
= copy
(cp
);
1240 if
(cbuf
[cpos
] == ' ') {
1244 if
(isdigit
((unsigned char)cbuf
[cpos
])) {
1246 while
(isdigit
((unsigned char)cbuf
[++cpos
]))
1250 yylval.i
= atoi
(cp
);
1259 if
(isdigit
((unsigned char)cbuf
[cpos
])) {
1261 while
(isdigit
((unsigned char)cbuf
[++cpos
]))
1265 yylval.i
= atoi
(cp
);
1269 switch
(cbuf
[cpos
++]) {
1333 fatal
("Unknown state in scanner.");
1350 if
((cp
= strchr
(cbuf
,'\n')))
1352 reply
(500, "'%s': command not understood.", cbuf
);
1363 fatal
("Ran out of memory.");
1368 help
(struct tab
*ctab
, char *s
)
1375 if
(ctab
== sitetab
)
1379 width
= 0, NCMDS
= 0;
1380 for
(c
= ctab
; c
->name
!= NULL
; c
++) {
1381 int len
= strlen
(c
->name
);
1387 width
= (width
+ 8) &~
7;
1392 lreply
(214, "The following %scommands are recognized %s.",
1393 t
, "(* =>'s unimplemented)");
1394 columns
= 76 / width
;
1397 lines
= (NCMDS
+ columns
- 1) / columns
;
1398 for
(i
= 0; i
< lines
; i
++) {
1399 strlcpy
(buf
, " ", sizeof
(buf
));
1400 for
(j
= 0; j
< columns
; j
++) {
1401 c
= ctab
+ j
* lines
+ i
;
1402 snprintf
(buf
+ strlen
(buf
),
1403 sizeof
(buf
) - strlen
(buf
),
1406 c
->implemented ?
' ' : '*');
1407 if
(c
+ lines
>= &ctab
[NCMDS
])
1409 w
= strlen
(c
->name
) + 1;
1417 lreply
(214, "%s", buf
);
1419 reply
(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1423 c
= lookup
(ctab
, s
);
1424 if
(c
== (struct tab
*)0) {
1425 reply
(502, "Unknown command %s.", s
);
1429 reply
(214, "Syntax: %s%s %s", t
, c
->name
, c
->help
);
1431 reply
(214, "%s%-*s\t%s; unimplemented.", t
, width
,
1436 sizecmd
(char *filename
)
1442 if
(stat
(filename
, &stbuf
) < 0 ||
!S_ISREG
(stbuf.st_mode
))
1443 reply
(550, "%s: not a plain file.", filename
);
1445 reply
(213, "%lu", (unsigned long)stbuf.st_size
);
1453 fin
= fopen
(filename
, "r");
1455 perror_reply
(550, filename
);
1458 if
(fstat
(fileno
(fin
), &stbuf
) < 0 ||
!S_ISREG
(stbuf.st_mode
)) {
1459 reply
(550, "%s: not a plain file.", filename
);
1465 while
((c
=getc
(fin
)) != EOF
) {
1466 if
(c
== '\n') /* will get expanded to \r\n */
1472 reply
(213, "%lu", (unsigned long)count
);
1476 reply
(504, "SIZE not implemented for Type %c.", "?AEIL"[type
]);