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.");
545 reply
(500, "Command not implemented.");
548 | SITE SP AFSLOG SP STRING CRLF check_login
552 reply
(500, "Can't be done as guest.");
561 reply
(500, "Command not implemented.");
564 | SITE SP LOCATE SP STRING CRLF check_login
571 | SITE SP URL CRLF check_secure
574 reply
(200, "http://www.pdc.kth.se/heimdal/");
576 | STOU SP pathname CRLF check_login
578 if
($5 && $3 != NULL
)
579 do_store
($3, "w", 1);
583 | SYST CRLF check_secure
586 #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
587 reply
(215, "UNIX Type: L%d", NBBY
);
589 reply
(215, "UNKNOWN Type: L%d", NBBY
);
595 * SIZE is not in RFC959, but Postel has blessed it and
596 * it will be in the updated RFC.
598 * Return size of file in a format suitable for
599 * using with RESTART (we just count bytes).
601 | SIZE SP pathname CRLF check_login
603 if
($5 && $3 != NULL
)
610 * MDTM is not in RFC959, but Postel has blessed it and
611 * it will be in the updated RFC.
613 * Return modification time of file as an ISO 3307
614 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
615 * where xxx is the fractional second (of any precision,
616 * not necessarily 3 digits)
618 | MDTM SP pathname CRLF check_login
620 if
($5 && $3 != NULL
) {
622 if
(stat
($3, &stbuf
) < 0)
624 $3, strerror
(errno
));
625 else if
(!S_ISREG
(stbuf.st_mode
)) {
627 "%s: not a plain file.", $3);
630 time_t mtime
= stbuf.st_mtime
;
634 "%04d%02d%02d%02d%02d%02d",
646 | QUIT CRLF check_secure
649 reply
(221, "Goodbye.");
659 : RNFR SP pathname CRLF check_login_no_guest
661 restart_point
= (off_t
) 0;
663 fromname
= renamefrom
($3);
664 if
(fromname
== (char *) 0 && $3) {
669 | REST SP byte_size CRLF check_secure
672 fromname
= (char *) 0;
673 restart_point
= $3; /* XXX $3 is only "int" */
674 reply
(350, "Restarting at %ld. %s",
676 "Send STORE or RETRIEVE to initiate transfer.");
679 | AUTH SP STRING CRLF
684 | ADAT SP STRING CRLF
689 | PBSZ SP NUMBER CRLF check_secure
694 | PROT SP STRING CRLF check_secure
699 | CCC CRLF check_secure
709 | CONF SP STRING CRLF
711 mec
($3, prot_confidential
);
716 mec
($3, prot_private
);
728 $$
= (char *)calloc
(1, sizeof
(char));
738 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
741 struct sockaddr_in
*sin4
= (struct sockaddr_in
*)data_dest
;
743 sin4
->sin_family
= AF_INET
;
744 sin4
->sin_port
= htons
($9 * 256 + $11);
745 sin4
->sin_addr.s_addr
=
746 htonl
(($1 << 24) |
($3 << 16) |
($5 << 8) |
$7);
800 /* this is for a bug in the BBN ftp */
842 * Problem: this production is used for all pathname
843 * processing, but only gives a 550 error reply.
844 * This is a valid reply in some cases but not in others.
846 if
(logged_in
&& $1 && *$1 == '~') {
849 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE
;
851 memset
(&gl
, 0, sizeof
(gl
));
852 if
(glob
($1, flags
, NULL
, &gl
) ||
854 reply
(550, "not found");
857 $$
= strdup
(gl.gl_pathv
[0]);
873 int ret
, dec
, multby
, digit
;
876 * Convert a number that was read as decimal number
877 * to what it would be if it had been read as octal.
888 ret
+= digit
* multby
;
897 check_login_no_guest
: check_login
901 reply
(550, "Permission denied");
905 check_login
: check_secure
908 if
(($$
= logged_in
) == 0)
909 reply
(530, "Please login with USER and PASS.");
915 check_secure
: /* empty */
918 if
(sec_complete
&& !ccc_passed
&& !secure_command
()) {
920 reply
(533, "Command protection level denied "
921 "for paranoid reasons.");
928 #define CMD 0 /* beginning of command */
929 #define ARGS 1 /* expect miscellaneous arguments */
930 #define STR1 2 /* expect SP followed by STRING */
931 #define STR2 3 /* expect STRING */
932 #define OSTR 4 /* optional SP then STRING */
933 #define ZSTR1 5 /* SP then optional STRING */
934 #define ZSTR2 6 /* optional STRING after SP */
935 #define SITECMD 7 /* SITE command */
936 #define NSTR 8 /* Number followed by a string */
938 struct tab cmdtab
[] = { /* In order defined in RFC 765 */
939 { "USER", USER
, STR1
, 1, "<sp> username" },
940 { "PASS", PASS
, ZSTR1
, 1, "<sp> password" },
941 { "ACCT", ACCT
, STR1
, 0, "(specify account)" },
942 { "SMNT", SMNT
, ARGS
, 0, "(structure mount)" },
943 { "REIN", REIN
, ARGS
, 0, "(reinitialize server state)" },
944 { "QUIT", QUIT
, ARGS
, 1, "(terminate service)", },
945 { "PORT", PORT
, ARGS
, 1, "<sp> b0, b1, b2, b3, b4" },
946 { "EPRT", EPRT
, STR1
, 1, "<sp> string" },
947 { "PASV", PASV
, ARGS
, 1, "(set server in passive mode)" },
948 { "EPSV", EPSV
, OSTR
, 1, "[<sp> foo]" },
949 { "TYPE", TYPE
, ARGS
, 1, "<sp> [ A | E | I | L ]" },
950 { "STRU", STRU
, ARGS
, 1, "(specify file structure)" },
951 { "MODE", MODE
, ARGS
, 1, "(specify transfer mode)" },
952 { "RETR", RETR
, STR1
, 1, "<sp> file-name" },
953 { "STOR", STOR
, STR1
, 1, "<sp> file-name" },
954 { "APPE", APPE
, STR1
, 1, "<sp> file-name" },
955 { "MLFL", MLFL
, OSTR
, 0, "(mail file)" },
956 { "MAIL", MAIL
, OSTR
, 0, "(mail to user)" },
957 { "MSND", MSND
, OSTR
, 0, "(mail send to terminal)" },
958 { "MSOM", MSOM
, OSTR
, 0, "(mail send to terminal or mailbox)" },
959 { "MSAM", MSAM
, OSTR
, 0, "(mail send to terminal and mailbox)" },
960 { "MRSQ", MRSQ
, OSTR
, 0, "(mail recipient scheme question)" },
961 { "MRCP", MRCP
, STR1
, 0, "(mail recipient)" },
962 { "ALLO", ALLO
, ARGS
, 1, "allocate storage (vacuously)" },
963 { "REST", REST
, ARGS
, 1, "<sp> offset (restart command)" },
964 { "RNFR", RNFR
, STR1
, 1, "<sp> file-name" },
965 { "RNTO", RNTO
, STR1
, 1, "<sp> file-name" },
966 { "ABOR", ABOR
, ARGS
, 1, "(abort operation)" },
967 { "DELE", DELE
, STR1
, 1, "<sp> file-name" },
968 { "CWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
969 { "XCWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
970 { "LIST", LIST
, OSTR
, 1, "[ <sp> path-name ]" },
971 { "NLST", NLST
, OSTR
, 1, "[ <sp> path-name ]" },
972 { "SITE", SITE
, SITECMD
, 1, "site-cmd [ <sp> arguments ]" },
973 { "SYST", SYST
, ARGS
, 1, "(get type of operating system)" },
974 { "STAT", sTAT
, OSTR
, 1, "[ <sp> path-name ]" },
975 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
976 { "NOOP", NOOP
, ARGS
, 1, "" },
977 { "MKD", MKD
, STR1
, 1, "<sp> path-name" },
978 { "XMKD", MKD
, STR1
, 1, "<sp> path-name" },
979 { "RMD", RMD
, STR1
, 1, "<sp> path-name" },
980 { "XRMD", RMD
, STR1
, 1, "<sp> path-name" },
981 { "PWD", PWD
, ARGS
, 1, "(return current directory)" },
982 { "XPWD", PWD
, ARGS
, 1, "(return current directory)" },
983 { "CDUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
984 { "XCUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
985 { "STOU", STOU
, STR1
, 1, "<sp> file-name" },
986 { "SIZE", SIZE
, OSTR
, 1, "<sp> path-name" },
987 { "MDTM", MDTM
, OSTR
, 1, "<sp> path-name" },
989 /* extensions from RFC2228 */
990 { "AUTH", AUTH
, STR1
, 1, "<sp> auth-type" },
991 { "ADAT", ADAT
, STR1
, 1, "<sp> auth-data" },
992 { "PBSZ", PBSZ
, ARGS
, 1, "<sp> buffer-size" },
993 { "PROT", PROT
, STR1
, 1, "<sp> prot-level" },
994 { "CCC", CCC
, ARGS
, 1, "" },
995 { "MIC", MIC
, STR1
, 1, "<sp> integrity command" },
996 { "CONF", CONF
, STR1
, 1, "<sp> confidentiality command" },
997 { "ENC", ENC
, STR1
, 1, "<sp> privacy command" },
1000 { "FEAT", FEAT
, ARGS
, 1, "" },
1001 { "OPTS", OPTS
, ARGS
, 1, "<sp> command [<sp> options]" },
1003 { NULL
, 0, 0, 0, 0 }
1006 struct tab sitetab
[] = {
1007 { "UMASK", UMASK
, ARGS
, 1, "[ <sp> umask ]" },
1008 { "IDLE", IDLE
, ARGS
, 1, "[ <sp> maximum-idle-time ]" },
1009 { "CHMOD", CHMOD
, NSTR
, 1, "<sp> mode <sp> file-name" },
1010 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
1012 { "KAUTH", KAUTH
, STR1
, 1, "<sp> principal [ <sp> ticket ]" },
1013 { "KLIST", KLIST
, ARGS
, 1, "(show ticket file)" },
1014 { "KDESTROY", KDESTROY
, ARGS
, 1, "(destroy tickets)" },
1015 { "KRBTKFILE", KRBTKFILE
, STR1
, 1, "<sp> ticket-file" },
1016 { "AFSLOG", AFSLOG
, OSTR
, 1, "[<sp> cell]" },
1018 { "LOCATE", LOCATE
, STR1
, 1, "<sp> globexpr" },
1019 { "FIND", LOCATE
, STR1
, 1, "<sp> globexpr" },
1021 { "URL", URL
, ARGS
, 1, "?" },
1023 { NULL
, 0, 0, 0, 0 }
1027 lookup
(struct tab
*p
, char *cmd
)
1030 for
(; p
->name
!= NULL
; p
++)
1031 if
(strcmp
(cmd
, p
->name
) == 0)
1037 * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1040 ftpd_getline
(char *s
, int n
)
1047 /* might still be data within the security MIC/CONF/ENC */
1049 strlcpy
(s
, ftp_command
, n
);
1051 syslog
(LOG_DEBUG
, "command: %s", s
);
1054 while
((c
= getc
(stdin
)) != EOF
) {
1057 if
((c
= getc
(stdin
)) != EOF
) {
1063 printf
("%c%c%c", IAC
, DONT
, 0377&c
);
1069 printf
("%c%c%c", IAC
, WONT
, 0377&c
);
1075 continue
; /* ignore command */
1080 if
(--n
<= 0 || c
== '\n')
1083 if
(c
== EOF
&& cs
== s
)
1087 if
(!guest
&& strncasecmp
("pass ", s
, 5) == 0) {
1088 /* Don't syslog passwords */
1089 syslog
(LOG_DEBUG
, "command: %.5s ???", s
);
1094 /* Don't syslog trailing CR-LF */
1097 while
(cp
>= s
&& (*cp
== '\n' ||
*cp
== '\r')) {
1101 syslog
(LOG_DEBUG
, "command: %.*s", len
, s
);
1105 fprintf
(stderr
, "%s\n", s
);
1115 "Timeout (%d seconds): closing control connection.",
1118 syslog
(LOG_INFO
, "User %s timed out after %d seconds",
1119 (pw ? pw
-> pw_name
: "unknown"), ftpd_timeout
);
1127 static int cpos
, state
;
1139 signal
(SIGALRM
, toolong
);
1140 alarm
((unsigned) ftpd_timeout
);
1141 if
(ftpd_getline
(cbuf
, sizeof
(cbuf
)-1) == NULL
) {
1142 reply
(221, "You could at least say goodbye.");
1146 #ifdef HAVE_SETPROCTITLE
1147 if
(strncasecmp
(cbuf
, "PASS", 4) != 0)
1148 setproctitle
("%s: %s", proctitle
, cbuf
);
1149 #endif /* HAVE_SETPROCTITLE */
1150 if
((cp
= strchr
(cbuf
, '\r'))) {
1154 if
((cp
= strpbrk
(cbuf
, " \n")))
1161 p
= lookup
(cmdtab
, cbuf
);
1164 if
(p
->implemented
== 0) {
1176 if
(cbuf
[cpos
] == ' ') {
1181 if
((cp2
= strpbrk
(cp
, " \n")))
1186 p
= lookup
(sitetab
, cp
);
1189 if
(p
->implemented
== 0) {
1203 if
(cbuf
[cpos
] == '\n') {
1212 if
(cbuf
[cpos
] == ' ') {
1223 if
(cbuf
[cpos
] == '\n') {
1234 * Make sure the string is nonempty and \n terminated.
1236 if
(n
> 1 && cbuf
[cpos
] == '\n') {
1238 yylval.s
= copy
(cp
);
1246 if
(cbuf
[cpos
] == ' ') {
1250 if
(isdigit
((unsigned char)cbuf
[cpos
])) {
1252 while
(isdigit
((unsigned char)cbuf
[++cpos
]))
1256 yylval.i
= atoi
(cp
);
1265 if
(isdigit
((unsigned char)cbuf
[cpos
])) {
1267 while
(isdigit
((unsigned char)cbuf
[++cpos
]))
1271 yylval.i
= atoi
(cp
);
1275 switch
(cbuf
[cpos
++]) {
1339 fatal
("Unknown state in scanner.");
1356 if
((cp
= strchr
(cbuf
,'\n')))
1358 reply
(500, "'%s': command not understood.", cbuf
);
1369 fatal
("Ran out of memory.");
1374 help
(struct tab
*ctab
, char *s
)
1381 if
(ctab
== sitetab
)
1385 width
= 0, NCMDS
= 0;
1386 for
(c
= ctab
; c
->name
!= NULL
; c
++) {
1387 int len
= strlen
(c
->name
);
1393 width
= (width
+ 8) &~
7;
1398 lreply
(214, "The following %scommands are recognized %s.",
1399 t
, "(* =>'s unimplemented)");
1400 columns
= 76 / width
;
1403 lines
= (NCMDS
+ columns
- 1) / columns
;
1404 for
(i
= 0; i
< lines
; i
++) {
1405 strlcpy
(buf
, " ", sizeof
(buf
));
1406 for
(j
= 0; j
< columns
; j
++) {
1407 c
= ctab
+ j
* lines
+ i
;
1408 snprintf
(buf
+ strlen
(buf
),
1409 sizeof
(buf
) - strlen
(buf
),
1412 c
->implemented ?
' ' : '*');
1413 if
(c
+ lines
>= &ctab
[NCMDS
])
1415 w
= strlen
(c
->name
) + 1;
1423 lreply
(214, "%s", buf
);
1425 reply
(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1429 c
= lookup
(ctab
, s
);
1430 if
(c
== (struct tab
*)0) {
1431 reply
(502, "Unknown command %s.", s
);
1435 reply
(214, "Syntax: %s%s %s", t
, c
->name
, c
->help
);
1437 reply
(214, "%s%-*s\t%s; unimplemented.", t
, width
,
1442 sizecmd
(char *filename
)
1448 if
(stat
(filename
, &stbuf
) < 0 ||
!S_ISREG
(stbuf.st_mode
))
1449 reply
(550, "%s: not a plain file.", filename
);
1451 reply
(213, "%lu", (unsigned long)stbuf.st_size
);
1459 fin
= fopen
(filename
, "r");
1461 perror_reply
(550, filename
);
1464 if
(fstat
(fileno
(fin
), &stbuf
) < 0 ||
!S_ISREG
(stbuf.st_mode
)) {
1465 reply
(550, "%s: not a plain file.", filename
);
1471 while
((c
=getc
(fin
)) != EOF
) {
1472 if
(c
== '\n') /* will get expanded to \r\n */
1478 reply
(213, "%lu", (unsigned long)count
);
1482 reply
(504, "SIZE not implemented for Type %c.", "?AEIL"[type
]);