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"
46 __RCSID
("$Heimdal: ftpcmd.y 15677 2005-07-19 18:33:08Z lha $"
51 static int hasyyerrored
;
56 static int cmd_bytesz
;
64 short implemented
; /* 1 if command is implemented */
68 extern
struct tab cmdtab
[];
69 extern
struct tab sitetab
[];
71 static char *copy
(char *);
72 static void help
(struct tab
*, char *);
74 lookup
(struct tab
*, char *);
75 static void sizecmd
(char *);
76 static RETSIGTYPE toolong
(int);
77 static int yylex (void);
79 /* This is for bison */
81 #if !defined(alloca) && !defined(HAVE_ALLOCA)
82 #define alloca(x) malloc(x)
98 USER PASS ACCT REIN QUIT PORT
99 PASV TYPE STRU MODE RETR STOR
100 APPE MLFL MAIL MSND MSOM MSAM
101 MRSQ MRCP ALLO REST RNFR RNTO
102 ABOR DELE CWD LIST NLST SITE
103 sTAT HELP NOOP MKD RMD PWD
104 CDUP STOU SMNT SYST SIZE MDTM
109 AUTH ADAT PROT PBSZ CCC MIC
112 KAUTH KLIST KDESTROY KRBTKFILE AFSLOG
122 %type
<i
> check_login check_login_no_guest check_secure octal_number byte_size
123 %type
<i
> struct_code mode_code type_code form_code
124 %type
<s
> pathstring pathname password username
134 fromname
= (char *) 0;
135 restart_point
= (off_t
) 0;
141 : USER SP username CRLF check_secure
147 | PASS SP password CRLF check_secure
151 memset
($3, 0, strlen
($3));
154 | PORT SP host_port CRLF check_secure
162 reply
(200, "PORT command successful.");
165 | EPRT SP STRING CRLF check_secure
171 | PASV CRLF check_login
176 | EPSV CRLF check_login
181 | EPSV SP STRING CRLF check_login
187 | TYPE SP type_code CRLF check_secure
193 if
(cmd_form
== FORM_N
) {
194 reply
(200, "Type set to A.");
198 reply
(504, "Form must be N.");
202 reply
(504, "Type E not implemented.");
206 reply
(200, "Type set to I.");
212 if
(cmd_bytesz
== 8) {
214 "Type set to L (byte size 8).");
217 reply
(504, "Byte size must be 8.");
218 #else /* NBBY == 8 */
219 UNIMPLEMENTED for NBBY
!= 8
220 #endif /* NBBY == 8 */
224 | STRU SP struct_code CRLF check_secure
230 reply
(200, "STRU F ok.");
234 reply
(504, "Unimplemented STRU type.");
238 | MODE SP mode_code CRLF check_secure
244 reply
(200, "MODE S ok.");
248 reply
(502, "Unimplemented MODE type.");
252 | ALLO SP NUMBER CRLF check_secure
255 reply
(202, "ALLO command ignored.");
258 | ALLO SP NUMBER SP R SP NUMBER CRLF check_secure
261 reply
(202, "ALLO command ignored.");
264 | RETR SP pathname CRLF check_login
268 if
($5 && name
!= NULL
)
273 | STOR SP pathname CRLF check_login
277 if
($5 && name
!= NULL
)
278 do_store
(name
, "w", 0);
282 | APPE SP pathname CRLF check_login
286 if
($5 && name
!= NULL
)
287 do_store
(name
, "a", 0);
291 | NLST CRLF check_login
296 | NLST SP STRING CRLF check_login
300 if
($5 && name
!= NULL
)
301 send_file_list
(name
);
305 | LIST CRLF check_login
310 | LIST SP pathname CRLF check_login
316 | sTAT SP pathname CRLF check_login
318 if
($5 && $3 != NULL
)
323 | sTAT CRLF check_secure
328 | DELE SP pathname CRLF check_login_no_guest
330 if
($5 && $3 != NULL
)
335 | RNTO SP pathname CRLF check_login_no_guest
339 renamecmd
(fromname
, $3);
341 fromname
= (char *) 0;
343 reply
(503, "Bad sequence of commands.");
349 | ABOR CRLF check_secure
352 reply
(225, "ABOR command successful.");
354 | CWD CRLF check_login
359 | CWD SP pathname CRLF check_login
361 if
($5 && $3 != NULL
)
366 | HELP CRLF check_secure
369 help
(cmdtab
, (char *) 0);
371 | HELP SP STRING CRLF check_secure
376 if
(strncasecmp
(cp
, "SITE", 4) == 0) {
383 help
(sitetab
, (char *) 0);
388 | NOOP CRLF check_secure
391 reply
(200, "NOOP command successful.");
393 | MKD SP pathname CRLF check_login
395 if
($5 && $3 != NULL
)
400 | RMD SP pathname CRLF check_login_no_guest
402 if
($5 && $3 != NULL
)
407 | PWD CRLF check_login
412 | CDUP CRLF check_login
417 | FEAT CRLF check_secure
420 lreply
(211, "Supported features:");
422 lreply
(0, " REST STREAM");
427 | OPTS SP STRING CRLF check_secure
430 reply
(501, "Bad options");
434 | SITE SP HELP CRLF check_secure
437 help
(sitetab
, (char *) 0);
439 | SITE SP HELP SP STRING CRLF check_secure
444 | SITE SP UMASK CRLF check_login
447 int oldmask
= umask
(0);
449 reply
(200, "Current UMASK is %03o", oldmask
);
452 | SITE SP UMASK SP octal_number CRLF check_login_no_guest
455 if
(($5 == -1) ||
($5 > 0777)) {
456 reply
(501, "Bad UMASK value");
458 int oldmask
= umask
($5);
460 "UMASK set to %03o (was %03o)",
465 | SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest
467 if
($9 && $7 != NULL
) {
470 "CHMOD: Mode value must be between 0 and 0777");
471 else if
(chmod
($7, $5) < 0)
472 perror_reply
(550, $7);
474 reply
(200, "CHMOD command successful.");
479 | SITE SP IDLE CRLF check_secure
483 "Current IDLE time limit is %d seconds; max %d",
484 ftpd_timeout
, maxtimeout
);
486 | SITE SP IDLE SP NUMBER CRLF check_secure
489 if
($5 < 30 ||
$5 > maxtimeout
) {
491 "Maximum IDLE time must be between 30 and %d seconds",
495 alarm
((unsigned) ftpd_timeout
);
497 "Maximum IDLE time set to %d seconds",
503 | SITE SP KAUTH SP STRING CRLF check_login
509 reply
(500, "Can't be done as guest.");
511 if
($7 && $5 != NULL
){
512 p
= strpbrk
($5, " \t");
515 kauth
($5, p
+ strspn
(p
, " \t"));
523 reply
(500, "Command not implemented.");
526 | SITE SP KLIST CRLF check_login
531 | SITE SP KDESTROY CRLF check_login
537 reply
(500, "Command not implemented.");
540 | SITE SP KRBTKFILE SP STRING CRLF check_login
544 reply
(500, "Can't be done as guest.");
550 reply
(500, "Command not implemented.");
553 | SITE SP AFSLOG CRLF check_login
555 #if defined(KRB4) || defined(KRB5)
557 reply
(500, "Can't be done as guest.");
561 reply
(500, "Command not implemented.");
564 | SITE SP AFSLOG SP STRING CRLF check_login
566 #if defined(KRB4) || defined(KRB5)
568 reply
(500, "Can't be done as guest.");
574 reply
(500, "Command not implemented.");
577 | SITE SP LOCATE SP STRING CRLF check_login
584 | SITE SP URL CRLF check_secure
587 reply
(200, "http://www.pdc.kth.se/heimdal/");
589 | STOU SP pathname CRLF check_login
591 if
($5 && $3 != NULL
)
592 do_store
($3, "w", 1);
596 | SYST CRLF check_secure
599 #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
600 reply
(215, "UNIX Type: L%d", NBBY
);
602 reply
(215, "UNKNOWN Type: L%d", NBBY
);
608 * SIZE is not in RFC959, but Postel has blessed it and
609 * it will be in the updated RFC.
611 * Return size of file in a format suitable for
612 * using with RESTART (we just count bytes).
614 | SIZE SP pathname CRLF check_login
616 if
($5 && $3 != NULL
)
623 * MDTM is not in RFC959, but Postel has blessed it and
624 * it will be in the updated RFC.
626 * Return modification time of file as an ISO 3307
627 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
628 * where xxx is the fractional second (of any precision,
629 * not necessarily 3 digits)
631 | MDTM SP pathname CRLF check_login
633 if
($5 && $3 != NULL
) {
635 if
(stat
($3, &stbuf
) < 0)
637 $3, strerror
(errno
));
638 else if
(!S_ISREG
(stbuf.st_mode
)) {
640 "%s: not a plain file.", $3);
643 time_t mtime
= stbuf.st_mtime
;
647 "%04d%02d%02d%02d%02d%02d",
659 | QUIT CRLF check_secure
662 reply
(221, "Goodbye.");
672 : RNFR SP pathname CRLF check_login_no_guest
674 restart_point
= (off_t
) 0;
676 fromname
= renamefrom
($3);
677 if
(fromname
== (char *) 0 && $3) {
682 | REST SP byte_size CRLF check_secure
685 fromname
= (char *) 0;
686 restart_point
= $3; /* XXX $3 is only "int" */
687 reply
(350, "Restarting at %ld. %s",
689 "Send STORE or RETRIEVE to initiate transfer.");
692 | AUTH SP STRING CRLF
697 | ADAT SP STRING CRLF
702 | PBSZ SP NUMBER CRLF check_secure
707 | PROT SP STRING CRLF check_secure
712 | CCC CRLF check_secure
722 | CONF SP STRING CRLF
724 mec
($3, prot_confidential
);
729 mec
($3, prot_private
);
741 $$
= (char *)calloc
(1, sizeof
(char));
751 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
754 struct sockaddr_in
*sin4
= (struct sockaddr_in
*)data_dest
;
756 sin4
->sin_family
= AF_INET
;
757 sin4
->sin_port
= htons
($9 * 256 + $11);
758 sin4
->sin_addr.s_addr
=
759 htonl
(($1 << 24) |
($3 << 16) |
($5 << 8) |
$7);
813 /* this is for a bug in the BBN ftp */
855 * Problem: this production is used for all pathname
856 * processing, but only gives a 550 error reply.
857 * This is a valid reply in some cases but not in others.
859 if
(logged_in
&& $1 && *$1 == '~') {
862 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE
;
864 memset
(&gl
, 0, sizeof
(gl
));
865 if
(glob
($1, flags
, NULL
, &gl
) ||
867 reply
(550, "not found");
870 $$
= strdup
(gl.gl_pathv
[0]);
886 int ret
, dec
, multby
, digit
;
889 * Convert a number that was read as decimal number
890 * to what it would be if it had been read as octal.
901 ret
+= digit
* multby
;
910 check_login_no_guest
: check_login
914 reply
(550, "Permission denied");
918 check_login
: check_secure
921 if
(($$
= logged_in
) == 0)
922 reply
(530, "Please login with USER and PASS.");
928 check_secure
: /* empty */
931 if
(sec_complete
&& !ccc_passed
&& !secure_command
()) {
933 reply
(533, "Command protection level denied "
934 "for paranoid reasons.");
941 #define CMD 0 /* beginning of command */
942 #define ARGS 1 /* expect miscellaneous arguments */
943 #define STR1 2 /* expect SP followed by STRING */
944 #define STR2 3 /* expect STRING */
945 #define OSTR 4 /* optional SP then STRING */
946 #define ZSTR1 5 /* SP then optional STRING */
947 #define ZSTR2 6 /* optional STRING after SP */
948 #define SITECMD 7 /* SITE command */
949 #define NSTR 8 /* Number followed by a string */
951 struct tab cmdtab
[] = { /* In order defined in RFC 765 */
952 { "USER", USER
, STR1
, 1, "<sp> username" },
953 { "PASS", PASS
, ZSTR1
, 1, "<sp> password" },
954 { "ACCT", ACCT
, STR1
, 0, "(specify account)" },
955 { "SMNT", SMNT
, ARGS
, 0, "(structure mount)" },
956 { "REIN", REIN
, ARGS
, 0, "(reinitialize server state)" },
957 { "QUIT", QUIT
, ARGS
, 1, "(terminate service)", },
958 { "PORT", PORT
, ARGS
, 1, "<sp> b0, b1, b2, b3, b4" },
959 { "EPRT", EPRT
, STR1
, 1, "<sp> string" },
960 { "PASV", PASV
, ARGS
, 1, "(set server in passive mode)" },
961 { "EPSV", EPSV
, OSTR
, 1, "[<sp> foo]" },
962 { "TYPE", TYPE
, ARGS
, 1, "<sp> [ A | E | I | L ]" },
963 { "STRU", STRU
, ARGS
, 1, "(specify file structure)" },
964 { "MODE", MODE
, ARGS
, 1, "(specify transfer mode)" },
965 { "RETR", RETR
, STR1
, 1, "<sp> file-name" },
966 { "STOR", STOR
, STR1
, 1, "<sp> file-name" },
967 { "APPE", APPE
, STR1
, 1, "<sp> file-name" },
968 { "MLFL", MLFL
, OSTR
, 0, "(mail file)" },
969 { "MAIL", MAIL
, OSTR
, 0, "(mail to user)" },
970 { "MSND", MSND
, OSTR
, 0, "(mail send to terminal)" },
971 { "MSOM", MSOM
, OSTR
, 0, "(mail send to terminal or mailbox)" },
972 { "MSAM", MSAM
, OSTR
, 0, "(mail send to terminal and mailbox)" },
973 { "MRSQ", MRSQ
, OSTR
, 0, "(mail recipient scheme question)" },
974 { "MRCP", MRCP
, STR1
, 0, "(mail recipient)" },
975 { "ALLO", ALLO
, ARGS
, 1, "allocate storage (vacuously)" },
976 { "REST", REST
, ARGS
, 1, "<sp> offset (restart command)" },
977 { "RNFR", RNFR
, STR1
, 1, "<sp> file-name" },
978 { "RNTO", RNTO
, STR1
, 1, "<sp> file-name" },
979 { "ABOR", ABOR
, ARGS
, 1, "(abort operation)" },
980 { "DELE", DELE
, STR1
, 1, "<sp> file-name" },
981 { "CWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
982 { "XCWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
983 { "LIST", LIST
, OSTR
, 1, "[ <sp> path-name ]" },
984 { "NLST", NLST
, OSTR
, 1, "[ <sp> path-name ]" },
985 { "SITE", SITE
, SITECMD
, 1, "site-cmd [ <sp> arguments ]" },
986 { "SYST", SYST
, ARGS
, 1, "(get type of operating system)" },
987 { "STAT", sTAT
, OSTR
, 1, "[ <sp> path-name ]" },
988 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
989 { "NOOP", NOOP
, ARGS
, 1, "" },
990 { "MKD", MKD
, STR1
, 1, "<sp> path-name" },
991 { "XMKD", MKD
, STR1
, 1, "<sp> path-name" },
992 { "RMD", RMD
, STR1
, 1, "<sp> path-name" },
993 { "XRMD", RMD
, STR1
, 1, "<sp> path-name" },
994 { "PWD", PWD
, ARGS
, 1, "(return current directory)" },
995 { "XPWD", PWD
, ARGS
, 1, "(return current directory)" },
996 { "CDUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
997 { "XCUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
998 { "STOU", STOU
, STR1
, 1, "<sp> file-name" },
999 { "SIZE", SIZE
, OSTR
, 1, "<sp> path-name" },
1000 { "MDTM", MDTM
, OSTR
, 1, "<sp> path-name" },
1002 /* extensions from RFC2228 */
1003 { "AUTH", AUTH
, STR1
, 1, "<sp> auth-type" },
1004 { "ADAT", ADAT
, STR1
, 1, "<sp> auth-data" },
1005 { "PBSZ", PBSZ
, ARGS
, 1, "<sp> buffer-size" },
1006 { "PROT", PROT
, STR1
, 1, "<sp> prot-level" },
1007 { "CCC", CCC
, ARGS
, 1, "" },
1008 { "MIC", MIC
, STR1
, 1, "<sp> integrity command" },
1009 { "CONF", CONF
, STR1
, 1, "<sp> confidentiality command" },
1010 { "ENC", ENC
, STR1
, 1, "<sp> privacy command" },
1013 { "FEAT", FEAT
, ARGS
, 1, "" },
1014 { "OPTS", OPTS
, ARGS
, 1, "<sp> command [<sp> options]" },
1016 { NULL
, 0, 0, 0, 0 }
1019 struct tab sitetab
[] = {
1020 { "UMASK", UMASK
, ARGS
, 1, "[ <sp> umask ]" },
1021 { "IDLE", IDLE
, ARGS
, 1, "[ <sp> maximum-idle-time ]" },
1022 { "CHMOD", CHMOD
, NSTR
, 1, "<sp> mode <sp> file-name" },
1023 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
1025 { "KAUTH", KAUTH
, STR1
, 1, "<sp> principal [ <sp> ticket ]" },
1026 { "KLIST", KLIST
, ARGS
, 1, "(show ticket file)" },
1027 { "KDESTROY", KDESTROY
, ARGS
, 1, "(destroy tickets)" },
1028 { "KRBTKFILE", KRBTKFILE
, STR1
, 1, "<sp> ticket-file" },
1029 { "AFSLOG", AFSLOG
, OSTR
, 1, "[<sp> cell]" },
1031 { "LOCATE", LOCATE
, STR1
, 1, "<sp> globexpr" },
1032 { "FIND", LOCATE
, STR1
, 1, "<sp> globexpr" },
1034 { "URL", URL
, ARGS
, 1, "?" },
1036 { NULL
, 0, 0, 0, 0 }
1040 lookup
(struct tab
*p
, char *cmd
)
1043 for
(; p
->name
!= NULL
; p
++)
1044 if
(strcmp
(cmd
, p
->name
) == 0)
1050 * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1053 ftpd_getline
(char *s
, int n
)
1060 /* might still be data within the security MIC/CONF/ENC */
1062 strlcpy
(s
, ftp_command
, n
);
1064 syslog
(LOG_DEBUG
, "command: %s", s
);
1067 while
((c
= getc
(stdin
)) != EOF
) {
1070 if
((c
= getc
(stdin
)) != EOF
) {
1076 printf
("%c%c%c", IAC
, DONT
, 0377&c
);
1082 printf
("%c%c%c", IAC
, WONT
, 0377&c
);
1088 continue
; /* ignore command */
1093 if
(--n
<= 0 || c
== '\n')
1096 if
(c
== EOF
&& cs
== s
)
1100 if
(!guest
&& strncasecmp
("pass ", s
, 5) == 0) {
1101 /* Don't syslog passwords */
1102 syslog
(LOG_DEBUG
, "command: %.5s ???", s
);
1107 /* Don't syslog trailing CR-LF */
1110 while
(cp
>= s
&& (*cp
== '\n' ||
*cp
== '\r')) {
1114 syslog
(LOG_DEBUG
, "command: %.*s", len
, s
);
1118 fprintf
(stderr
, "%s\n", s
);
1128 "Timeout (%d seconds): closing control connection.",
1131 syslog
(LOG_INFO
, "User %s timed out after %d seconds",
1132 (pw ? pw
-> pw_name
: "unknown"), ftpd_timeout
);
1140 static int cpos
, state
;
1152 signal
(SIGALRM
, toolong
);
1153 alarm
((unsigned) ftpd_timeout
);
1154 if
(ftpd_getline
(cbuf
, sizeof
(cbuf
)-1) == NULL
) {
1155 reply
(221, "You could at least say goodbye.");
1159 #ifdef HAVE_SETPROCTITLE
1160 if
(strncasecmp
(cbuf
, "PASS", 4) != 0)
1161 setproctitle
("%s: %s", proctitle
, cbuf
);
1162 #endif /* HAVE_SETPROCTITLE */
1163 if
((cp
= strchr
(cbuf
, '\r'))) {
1167 if
((cp
= strpbrk
(cbuf
, " \n")))
1174 p
= lookup
(cmdtab
, cbuf
);
1177 if
(p
->implemented
== 0) {
1189 if
(cbuf
[cpos
] == ' ') {
1194 if
((cp2
= strpbrk
(cp
, " \n")))
1199 p
= lookup
(sitetab
, cp
);
1202 if
(p
->implemented
== 0) {
1216 if
(cbuf
[cpos
] == '\n') {
1225 if
(cbuf
[cpos
] == ' ') {
1236 if
(cbuf
[cpos
] == '\n') {
1247 * Make sure the string is nonempty and \n terminated.
1249 if
(n
> 1 && cbuf
[cpos
] == '\n') {
1251 yylval.s
= copy
(cp
);
1259 if
(cbuf
[cpos
] == ' ') {
1263 if
(isdigit
((unsigned char)cbuf
[cpos
])) {
1265 while
(isdigit
((unsigned char)cbuf
[++cpos
]))
1269 yylval.i
= atoi
(cp
);
1278 if
(isdigit
((unsigned char)cbuf
[cpos
])) {
1280 while
(isdigit
((unsigned char)cbuf
[++cpos
]))
1284 yylval.i
= atoi
(cp
);
1288 switch
(cbuf
[cpos
++]) {
1352 fatal
("Unknown state in scanner.");
1369 if
((cp
= strchr
(cbuf
,'\n')))
1371 reply
(500, "'%s': command not understood.", cbuf
);
1382 fatal
("Ran out of memory.");
1387 help
(struct tab
*ctab
, char *s
)
1394 if
(ctab
== sitetab
)
1398 width
= 0, NCMDS
= 0;
1399 for
(c
= ctab
; c
->name
!= NULL
; c
++) {
1400 int len
= strlen
(c
->name
);
1406 width
= (width
+ 8) &~
7;
1411 lreply
(214, "The following %scommands are recognized %s.",
1412 t
, "(* =>'s unimplemented)");
1413 columns
= 76 / width
;
1416 lines
= (NCMDS
+ columns
- 1) / columns
;
1417 for
(i
= 0; i
< lines
; i
++) {
1418 strlcpy
(buf
, " ", sizeof
(buf
));
1419 for
(j
= 0; j
< columns
; j
++) {
1420 c
= ctab
+ j
* lines
+ i
;
1421 snprintf
(buf
+ strlen
(buf
),
1422 sizeof
(buf
) - strlen
(buf
),
1425 c
->implemented ?
' ' : '*');
1426 if
(c
+ lines
>= &ctab
[NCMDS
])
1428 w
= strlen
(c
->name
) + 1;
1436 lreply
(214, "%s", buf
);
1438 reply
(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1442 c
= lookup
(ctab
, s
);
1443 if
(c
== (struct tab
*)0) {
1444 reply
(502, "Unknown command %s.", s
);
1448 reply
(214, "Syntax: %s%s %s", t
, c
->name
, c
->help
);
1450 reply
(214, "%s%-*s\t%s; unimplemented.", t
, width
,
1455 sizecmd
(char *filename
)
1461 if
(stat
(filename
, &stbuf
) < 0 ||
!S_ISREG
(stbuf.st_mode
))
1462 reply
(550, "%s: not a plain file.", filename
);
1464 reply
(213, "%lu", (unsigned long)stbuf.st_size
);
1472 fin
= fopen
(filename
, "r");
1474 perror_reply
(550, filename
);
1477 if
(fstat
(fileno
(fin
), &stbuf
) < 0 ||
!S_ISREG
(stbuf.st_mode
)) {
1478 reply
(550, "%s: not a plain file.", filename
);
1484 while
((c
=getc
(fin
)) != EOF
) {
1485 if
(c
== '\n') /* will get expanded to \r\n */
1491 reply
(213, "%lu", (unsigned long)count
);
1495 reply
(504, "SIZE not implemented for Type %c.", "?AEIL"[type
]);