btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / bin / network / ftp / cmds.c
blob6fad058246c845197ad5b02955908c7e775e8176
1 /* $NetBSD: cmds.c,v 1.112 2005/04/11 01:49:31 lukem Exp $ */
3 /*-
4 * Copyright (c) 1996-2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
12 * NASA Ames Research Center.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the NetBSD
25 * Foundation, Inc. and its contributors.
26 * 4. Neither the name of The NetBSD Foundation nor the names of its
27 * contributors may be used to endorse or promote products derived
28 * from this software without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
31 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
32 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
34 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 * POSSIBILITY OF SUCH DAMAGE.
44 * Copyright (c) 1985, 1989, 1993, 1994
45 * The Regents of the University of California. All rights reserved.
47 * Redistribution and use in source and binary forms, with or without
48 * modification, are permitted provided that the following conditions
49 * are met:
50 * 1. Redistributions of source code must retain the above copyright
51 * notice, this list of conditions and the following disclaimer.
52 * 2. Redistributions in binary form must reproduce the above copyright
53 * notice, this list of conditions and the following disclaimer in the
54 * documentation and/or other materials provided with the distribution.
55 * 3. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
73 * Copyright (C) 1997 and 1998 WIDE Project.
74 * All rights reserved.
76 * Redistribution and use in source and binary forms, with or without
77 * modification, are permitted provided that the following conditions
78 * are met:
79 * 1. Redistributions of source code must retain the above copyright
80 * notice, this list of conditions and the following disclaimer.
81 * 2. Redistributions in binary form must reproduce the above copyright
82 * notice, this list of conditions and the following disclaimer in the
83 * documentation and/or other materials provided with the distribution.
84 * 3. Neither the name of the project nor the names of its contributors
85 * may be used to endorse or promote products derived from this software
86 * without specific prior written permission.
88 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
89 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
90 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
91 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
92 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
93 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
94 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
95 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
96 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
97 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
98 * SUCH DAMAGE.
101 #include <sys/cdefs.h>
104 * FTP User Program -- Command Routines.
106 #include <sys/types.h>
107 #include <sys/socket.h>
108 #include <sys/stat.h>
109 #include <sys/wait.h>
110 #include <arpa/ftp.h>
112 #include <ctype.h>
113 #include <err.h>
114 #include <glob.h>
115 #include <limits.h>
116 #include <netdb.h>
117 #include <paths.h>
118 #include <stdio.h>
119 #include <stdlib.h>
120 #include <string.h>
121 #include <time.h>
122 #include <unistd.h>
123 #include <libutil.h>
125 #include "ftp_var.h"
126 #include "version.h"
128 struct types {
129 char *t_name;
130 char *t_mode;
131 int t_type;
132 char *t_arg;
133 } types[] = {
134 { "ascii", "A", TYPE_A, 0 },
135 { "binary", "I", TYPE_I, 0 },
136 { "image", "I", TYPE_I, 0 },
137 { "ebcdic", "E", TYPE_E, 0 },
138 { "tenex", "L", TYPE_L, bytename },
139 { NULL }
142 sigjmp_buf jabort;
143 const char *mname;
145 static int confirm(const char *, const char *);
147 static const char *doprocess(char *, size_t, const char *, int, int, int);
148 static const char *domap(char *, size_t, const char *);
149 static const char *docase(char *, size_t, const char *);
150 static const char *dotrans(char *, size_t, const char *);
152 static int
153 confirm(const char *cmd, const char *file)
155 char line[BUFSIZ];
157 if (!interactive || confirmrest)
158 return (1);
159 while (1) {
160 fprintf(ttyout, "%s %s [anpqy?]? ", cmd, file);
161 (void)fflush(ttyout);
162 if (fgets(line, sizeof(line), stdin) == NULL) {
163 mflag = 0;
164 fprintf(ttyout, "\nEOF received; %s aborted\n", mname);
165 clearerr(stdin);
166 return (0);
168 switch (tolower((unsigned char)*line)) {
169 case 'a':
170 confirmrest = 1;
171 fprintf(ttyout,
172 "Prompting off for duration of %s.\n", cmd);
173 break;
174 case 'p':
175 interactive = 0;
176 fputs("Interactive mode: off.\n", ttyout);
177 break;
178 case 'q':
179 mflag = 0;
180 fprintf(ttyout, "%s aborted.\n", mname);
181 /* FALLTHROUGH */
182 case 'n':
183 return (0);
184 case '?':
185 fprintf(ttyout,
186 " confirmation options:\n"
187 "\ta answer `yes' for the duration of %s\n"
188 "\tn answer `no' for this file\n"
189 "\tp turn off `prompt' mode\n"
190 "\tq stop the current %s\n"
191 "\ty answer `yes' for this file\n"
192 "\t? this help list\n",
193 cmd, cmd);
194 continue; /* back to while(1) */
196 return (1);
198 /* NOTREACHED */
202 * Set transfer type.
204 void
205 settype(int argc, char *argv[])
207 struct types *p;
208 int comret;
210 if (argc == 0 || argc > 2) {
211 char *sep;
213 fprintf(ttyout, "usage: %s [", argv[0]);
214 sep = " ";
215 for (p = types; p->t_name; p++) {
216 fprintf(ttyout, "%s%s", sep, p->t_name);
217 sep = " | ";
219 fputs(" ]\n", ttyout);
220 code = -1;
221 return;
223 if (argc < 2) {
224 fprintf(ttyout, "Using %s mode to transfer files.\n", typename);
225 code = 0;
226 return;
228 for (p = types; p->t_name; p++)
229 if (strcmp(argv[1], p->t_name) == 0)
230 break;
231 if (p->t_name == 0) {
232 fprintf(ttyout, "%s: unknown mode.\n", argv[1]);
233 code = -1;
234 return;
236 if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
237 comret = command("TYPE %s %s", p->t_mode, p->t_arg);
238 else
239 comret = command("TYPE %s", p->t_mode);
240 if (comret == COMPLETE) {
241 (void)strlcpy(typename, p->t_name, sizeof(typename));
242 curtype = type = p->t_type;
247 * Internal form of settype; changes current type in use with server
248 * without changing our notion of the type for data transfers.
249 * Used to change to and from ascii for listings.
251 void
252 changetype(int newtype, int show)
254 struct types *p;
255 int comret, oldverbose = verbose;
257 if (newtype == 0)
258 newtype = TYPE_I;
259 if (newtype == curtype)
260 return;
261 if (debug == 0 && show == 0)
262 verbose = 0;
263 for (p = types; p->t_name; p++)
264 if (newtype == p->t_type)
265 break;
266 if (p->t_name == 0) {
267 warnx("internal error: unknown type %d.", newtype);
268 return;
270 if (newtype == TYPE_L && bytename[0] != '\0')
271 comret = command("TYPE %s %s", p->t_mode, bytename);
272 else
273 comret = command("TYPE %s", p->t_mode);
274 if (comret == COMPLETE)
275 curtype = newtype;
276 verbose = oldverbose;
279 char *stype[] = {
280 "type",
286 * Set binary transfer type.
288 /*VARARGS*/
289 void
290 setbinary(int argc, char *argv[])
293 if (argc == 0) {
294 fprintf(ttyout, "usage: %s\n", argv[0]);
295 code = -1;
296 return;
298 stype[1] = "binary";
299 settype(2, stype);
303 * Set ascii transfer type.
305 /*VARARGS*/
306 void
307 setascii(int argc, char *argv[])
310 if (argc == 0) {
311 fprintf(ttyout, "usage: %s\n", argv[0]);
312 code = -1;
313 return;
315 stype[1] = "ascii";
316 settype(2, stype);
320 * Set tenex transfer type.
322 /*VARARGS*/
323 void
324 settenex(int argc, char *argv[])
327 if (argc == 0) {
328 fprintf(ttyout, "usage: %s\n", argv[0]);
329 code = -1;
330 return;
332 stype[1] = "tenex";
333 settype(2, stype);
337 * Set file transfer mode.
339 /*ARGSUSED*/
340 void
341 setftmode(int argc, char *argv[])
344 if (argc != 2) {
345 fprintf(ttyout, "usage: %s mode-name\n", argv[0]);
346 code = -1;
347 return;
349 fprintf(ttyout, "We only support %s mode, sorry.\n", modename);
350 code = -1;
354 * Set file transfer format.
356 /*ARGSUSED*/
357 void
358 setform(int argc, char *argv[])
361 if (argc != 2) {
362 fprintf(ttyout, "usage: %s format\n", argv[0]);
363 code = -1;
364 return;
366 fprintf(ttyout, "We only support %s format, sorry.\n", formname);
367 code = -1;
371 * Set file transfer structure.
373 /*ARGSUSED*/
374 void
375 setstruct(int argc, char *argv[])
378 if (argc != 2) {
379 fprintf(ttyout, "usage: %s struct-mode\n", argv[0]);
380 code = -1;
381 return;
383 fprintf(ttyout, "We only support %s structure, sorry.\n", structname);
384 code = -1;
388 * Send a single file.
390 void
391 put(int argc, char *argv[])
393 char buf[MAXPATHLEN];
394 char *cmd;
395 int loc = 0;
396 char *locfile;
397 const char *remfile;
399 if (argc == 2) {
400 argc++;
401 argv[2] = argv[1];
402 loc++;
404 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-file")))
405 goto usage;
406 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
407 usage:
408 fprintf(ttyout, "usage: %s local-file [remote-file]\n",
409 argv[0]);
410 code = -1;
411 return;
413 if ((locfile = globulize(argv[1])) == NULL) {
414 code = -1;
415 return;
417 remfile = argv[2];
418 if (loc) /* If argv[2] is a copy of the old argv[1], update it */
419 remfile = locfile;
420 cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
421 remfile = doprocess(buf, sizeof(buf), remfile,
422 0, loc && ntflag, loc && mapflag);
423 sendrequest(cmd, locfile, remfile,
424 locfile != argv[1] || remfile != argv[2]);
425 free(locfile);
428 static const char *
429 doprocess(char *dst, size_t dlen, const char *src,
430 int casef, int transf, int mapf)
432 if (casef)
433 src = docase(dst, dlen, src);
434 if (transf)
435 src = dotrans(dst, dlen, src);
436 if (mapf)
437 src = domap(dst, dlen, src);
438 return src;
442 * Send multiple files.
444 void
445 mput(int argc, char *argv[])
447 int i;
448 sigfunc oldintr;
449 int ointer;
450 const char *tp;
452 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-files"))) {
453 fprintf(ttyout, "usage: %s local-files\n", argv[0]);
454 code = -1;
455 return;
457 mname = argv[0];
458 mflag = 1;
459 oldintr = xsignal(SIGINT, mintr);
460 if (sigsetjmp(jabort, 1))
461 mabort();
462 if (proxy) {
463 char *cp;
465 while ((cp = remglob(argv, 0, NULL)) != NULL) {
466 if (*cp == '\0' || !connected) {
467 mflag = 0;
468 continue;
470 if (mflag && confirm(argv[0], cp)) {
471 char buf[MAXPATHLEN];
472 tp = doprocess(buf, sizeof(buf), cp,
473 mcase, ntflag, mapflag);
474 sendrequest((sunique) ? "STOU" : "STOR",
475 cp, tp, cp != tp || !interactive);
476 if (!mflag && fromatty) {
477 ointer = interactive;
478 interactive = 1;
479 if (confirm("Continue with", "mput")) {
480 mflag++;
482 interactive = ointer;
486 goto cleanupmput;
488 for (i = 1; i < argc && connected; i++) {
489 char **cpp;
490 glob_t gl;
491 int flags;
493 if (!doglob) {
494 if (mflag && confirm(argv[0], argv[i])) {
495 char buf[MAXPATHLEN];
496 tp = doprocess(buf, sizeof(buf), argv[i],
497 0, ntflag, mapflag);
498 sendrequest((sunique) ? "STOU" : "STOR",
499 argv[i], tp, tp != argv[i] || !interactive);
500 if (!mflag && fromatty) {
501 ointer = interactive;
502 interactive = 1;
503 if (confirm("Continue with", "mput")) {
504 mflag++;
506 interactive = ointer;
509 continue;
512 memset(&gl, 0, sizeof(gl));
513 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
514 if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
515 warnx("%s: not found", argv[i]);
516 globfree(&gl);
517 continue;
519 for (cpp = gl.gl_pathv; cpp && *cpp != NULL && connected;
520 cpp++) {
521 if (mflag && confirm(argv[0], *cpp)) {
522 char buf[MAXPATHLEN];
523 tp = *cpp;
524 tp = doprocess(buf, sizeof(buf), *cpp,
525 0, ntflag, mapflag);
526 sendrequest((sunique) ? "STOU" : "STOR",
527 *cpp, tp, *cpp != tp || !interactive);
528 if (!mflag && fromatty) {
529 ointer = interactive;
530 interactive = 1;
531 if (confirm("Continue with", "mput")) {
532 mflag++;
534 interactive = ointer;
538 globfree(&gl);
540 cleanupmput:
541 (void)xsignal(SIGINT, oldintr);
542 mflag = 0;
545 void
546 reget(int argc, char *argv[])
549 (void)getit(argc, argv, 1, "r+");
552 void
553 get(int argc, char *argv[])
556 (void)getit(argc, argv, 0, restart_point ? "r+" : "w" );
560 * Receive one file.
561 * If restartit is 1, restart the xfer always.
562 * If restartit is -1, restart the xfer only if the remote file is newer.
565 getit(int argc, char *argv[], int restartit, const char *mode)
567 int loc, rval;
568 char *remfile, *olocfile;
569 const char *locfile;
570 char buf[MAXPATHLEN];
572 loc = rval = 0;
573 if (argc == 2) {
574 argc++;
575 argv[2] = argv[1];
576 loc++;
578 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "remote-file")))
579 goto usage;
580 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
581 usage:
582 fprintf(ttyout, "usage: %s remote-file [local-file]\n",
583 argv[0]);
584 code = -1;
585 return (0);
587 remfile = argv[1];
588 if ((olocfile = globulize(argv[2])) == NULL) {
589 code = -1;
590 return (0);
592 locfile = doprocess(buf, sizeof(buf), olocfile,
593 loc && mcase, loc && ntflag, loc && mapflag);
594 if (restartit) {
595 struct stat stbuf;
596 int ret;
598 if (! features[FEAT_REST_STREAM]) {
599 fprintf(ttyout,
600 "Restart is not supported by the remote server.\n");
601 return (0);
603 ret = stat(locfile, &stbuf);
604 if (restartit == 1) {
605 if (ret < 0) {
606 warn("local: %s", locfile);
607 goto freegetit;
609 restart_point = stbuf.st_size;
610 } else {
611 if (ret == 0) {
612 time_t mtime;
614 mtime = remotemodtime(argv[1], 0);
615 if (mtime == -1)
616 goto freegetit;
617 if (stbuf.st_mtime >= mtime) {
618 rval = 1;
619 goto freegetit;
625 recvrequest("RETR", locfile, remfile, mode,
626 remfile != argv[1] || locfile != argv[2], loc);
627 restart_point = 0;
628 freegetit:
629 (void)free(olocfile);
630 return (rval);
633 /* ARGSUSED */
634 void
635 mintr(int signo)
638 alarmtimer(0);
639 if (fromatty)
640 write(fileno(ttyout), "\n", 1);
641 siglongjmp(jabort, 1);
644 void
645 mabort(void)
647 int ointer, oconf;
649 if (mflag && fromatty) {
650 ointer = interactive;
651 oconf = confirmrest;
652 interactive = 1;
653 confirmrest = 0;
654 if (confirm("Continue with", mname)) {
655 interactive = ointer;
656 confirmrest = oconf;
657 return;
659 interactive = ointer;
660 confirmrest = oconf;
662 mflag = 0;
666 * Get multiple files.
668 void
669 mget(int argc, char *argv[])
671 sigfunc oldintr;
672 int ointer;
673 char *cp;
674 const char *tp;
675 int restartit;
677 if (argc == 0 ||
678 (argc == 1 && !another(&argc, &argv, "remote-files"))) {
679 fprintf(ttyout, "usage: %s remote-files\n", argv[0]);
680 code = -1;
681 return;
683 mname = argv[0];
684 mflag = 1;
685 restart_point = 0;
686 restartit = 0;
687 if (strcmp(argv[0], "mreget") == 0) {
688 if (! features[FEAT_REST_STREAM]) {
689 fprintf(ttyout,
690 "Restart is not supported by the remote server.\n");
691 return;
693 restartit = 1;
695 oldintr = xsignal(SIGINT, mintr);
696 if (sigsetjmp(jabort, 1))
697 mabort();
698 while ((cp = remglob(argv, proxy, NULL)) != NULL) {
699 char buf[MAXPATHLEN];
700 if (*cp == '\0' || !connected) {
701 mflag = 0;
702 continue;
704 if (! mflag)
705 continue;
706 if (! fileindir(cp, localcwd)) {
707 fprintf(ttyout, "Skipping non-relative filename `%s'\n",
708 cp);
709 continue;
711 if (!confirm(argv[0], cp))
712 continue;
713 tp = doprocess(buf, sizeof(buf), cp, mcase, ntflag, mapflag);
714 if (restartit) {
715 struct stat stbuf;
717 if (stat(tp, &stbuf) == 0)
718 restart_point = stbuf.st_size;
719 else
720 warn("stat %s", tp);
722 recvrequest("RETR", tp, cp, restart_point ? "r+" : "w",
723 tp != cp || !interactive, 1);
724 restart_point = 0;
725 if (!mflag && fromatty) {
726 ointer = interactive;
727 interactive = 1;
728 if (confirm("Continue with", "mget"))
729 mflag++;
730 interactive = ointer;
733 (void)xsignal(SIGINT, oldintr);
734 mflag = 0;
738 * Read list of filenames from a local file and get those
740 void
741 fget(int argc, char *argv[])
743 char *buf, *mode;
744 FILE *fp;
746 if (argc != 2) {
747 fprintf(ttyout, "usage: %s localfile\n", argv[0]);
748 code = -1;
749 return;
752 fp = fopen(argv[1], "r");
753 if (fp == NULL) {
754 fprintf(ttyout, "Cannot open source file %s\n", argv[1]);
755 code = -1;
756 return;
759 argv[0] = "get";
760 mode = restart_point ? "r+" : "w";
762 for (;
763 (buf = fparseln(fp, NULL, NULL, "\0\0\0", 0)) != NULL;
764 free(buf)) {
765 if (buf[0] == '\0')
766 continue;
767 argv[1] = buf;
768 (void)getit(argc, argv, 0, mode);
770 fclose(fp);
773 char *
774 onoff(int value)
777 return (value != 0 ? "on" : "off");
781 * Show status.
783 /*ARGSUSED*/
784 void
785 status(int argc, char *argv[])
788 if (argc == 0) {
789 fprintf(ttyout, "usage: %s\n", argv[0]);
790 code = -1;
791 return;
793 #ifndef NO_STATUS
794 if (connected)
795 fprintf(ttyout, "Connected %sto %s.\n",
796 connected == -1 ? "and logged in" : "", hostname);
797 else
798 fputs("Not connected.\n", ttyout);
799 if (!proxy) {
800 pswitch(1);
801 if (connected) {
802 fprintf(ttyout, "Connected for proxy commands to %s.\n",
803 hostname);
805 else {
806 fputs("No proxy connection.\n", ttyout);
808 pswitch(0);
810 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
811 *gateserver ? gateserver : "(none)", gateport);
812 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
813 onoff(passivemode), onoff(activefallback));
814 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
815 modename, typename, formname, structname);
816 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
817 onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob));
818 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n",
819 onoff(sunique), onoff(runique));
820 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve));
821 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase),
822 onoff(crflag));
823 if (ntflag) {
824 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout);
826 else {
827 fputs("Ntrans: off.\n", ttyout);
829 if (mapflag) {
830 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout);
832 else {
833 fputs("Nmap: off.\n", ttyout);
835 fprintf(ttyout,
836 "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
837 onoff(hash), mark, onoff(progress));
838 fprintf(ttyout,
839 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
840 onoff(rate_get), rate_get, rate_get_incr);
841 fprintf(ttyout,
842 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
843 onoff(rate_put), rate_put, rate_put_incr);
844 fprintf(ttyout,
845 "Socket buffer sizes: send %d, receive %d.\n",
846 sndbuf_size, rcvbuf_size);
847 fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport));
848 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4),
849 epsv4bad ? " (disabled for this connection)" : "");
850 fprintf(ttyout, "Command line editing: %s.\n",
851 #ifdef NO_EDITCOMPLETE
852 "support not compiled in"
853 #else /* !def NO_EDITCOMPLETE */
854 onoff(editing)
855 #endif /* !def NO_EDITCOMPLETE */
857 if (macnum > 0) {
858 int i;
860 fputs("Macros:\n", ttyout);
861 for (i=0; i<macnum; i++) {
862 fprintf(ttyout, "\t%s\n", macros[i].mac_name);
865 #endif /* !def NO_STATUS */
866 fprintf(ttyout, "Version: %s %s\n", FTP_PRODUCT, FTP_VERSION);
867 code = 0;
871 * Toggle a variable
874 togglevar(int argc, char *argv[], int *var, const char *mesg)
876 if (argc == 1) {
877 *var = !*var;
878 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
879 *var = 1;
880 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
881 *var = 0;
882 } else {
883 fprintf(ttyout, "usage: %s [ on | off ]\n", argv[0]);
884 return (-1);
886 if (mesg)
887 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var));
888 return (*var);
892 * Set beep on cmd completed mode.
894 /*VARARGS*/
895 void
896 setbell(int argc, char *argv[])
899 code = togglevar(argc, argv, &bell, "Bell mode");
903 * Set command line editing
905 /*VARARGS*/
906 void
907 setedit(int argc, char *argv[])
910 #ifdef NO_EDITCOMPLETE
911 if (argc == 0) {
912 fprintf(ttyout, "usage: %s\n", argv[0]);
913 code = -1;
914 return;
916 if (verbose)
917 fputs("Editing support not compiled in; ignoring command.\n",
918 ttyout);
919 #else /* !def NO_EDITCOMPLETE */
920 code = togglevar(argc, argv, &editing, "Editing mode");
921 controlediting();
922 #endif /* !def NO_EDITCOMPLETE */
926 * Turn on packet tracing.
928 /*VARARGS*/
929 void
930 settrace(int argc, char *argv[])
933 code = togglevar(argc, argv, &trace, "Packet tracing");
937 * Toggle hash mark printing during transfers, or set hash mark bytecount.
939 /*VARARGS*/
940 void
941 sethash(int argc, char *argv[])
943 if (argc == 1)
944 hash = !hash;
945 else if (argc != 2) {
946 fprintf(ttyout, "usage: %s [ on | off | bytecount ]\n",
947 argv[0]);
948 code = -1;
949 return;
950 } else if (strcasecmp(argv[1], "on") == 0)
951 hash = 1;
952 else if (strcasecmp(argv[1], "off") == 0)
953 hash = 0;
954 else {
955 int nmark;
957 nmark = strsuftoi(argv[1]);
958 if (nmark < 1) {
959 fprintf(ttyout, "mark: bad bytecount value `%s'.\n",
960 argv[1]);
961 code = -1;
962 return;
964 mark = nmark;
965 hash = 1;
967 fprintf(ttyout, "Hash mark printing %s", onoff(hash));
968 if (hash)
969 fprintf(ttyout, " (%d bytes/hash mark)", mark);
970 fputs(".\n", ttyout);
971 if (hash)
972 progress = 0;
973 code = hash;
977 * Turn on printing of server echo's.
979 /*VARARGS*/
980 void
981 setverbose(int argc, char *argv[])
984 code = togglevar(argc, argv, &verbose, "Verbose mode");
988 * Toggle PORT/LPRT cmd use before each data connection.
990 /*VARARGS*/
991 void
992 setport(int argc, char *argv[])
995 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds");
999 * Toggle transfer progress bar.
1001 /*VARARGS*/
1002 void
1003 setprogress(int argc, char *argv[])
1006 code = togglevar(argc, argv, &progress, "Progress bar");
1007 if (progress)
1008 hash = 0;
1012 * Turn on interactive prompting during mget, mput, and mdelete.
1014 /*VARARGS*/
1015 void
1016 setprompt(int argc, char *argv[])
1019 code = togglevar(argc, argv, &interactive, "Interactive mode");
1023 * Toggle gate-ftp mode, or set gate-ftp server
1025 /*VARARGS*/
1026 void
1027 setgate(int argc, char *argv[])
1029 static char gsbuf[MAXHOSTNAMELEN];
1031 if (argc == 0 || argc > 3) {
1032 fprintf(ttyout,
1033 "usage: %s [ on | off | gateserver [port] ]\n", argv[0]);
1034 code = -1;
1035 return;
1036 } else if (argc < 2) {
1037 gatemode = !gatemode;
1038 } else {
1039 if (argc == 2 && strcasecmp(argv[1], "on") == 0)
1040 gatemode = 1;
1041 else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
1042 gatemode = 0;
1043 else {
1044 if (argc == 3)
1045 gateport = xstrdup(argv[2]);
1046 (void)strlcpy(gsbuf, argv[1], sizeof(gsbuf));
1047 gateserver = gsbuf;
1048 gatemode = 1;
1051 if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
1052 fprintf(ttyout,
1053 "Disabling gate-ftp mode - no gate-ftp server defined.\n");
1054 gatemode = 0;
1055 } else {
1056 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n",
1057 onoff(gatemode), *gateserver ? gateserver : "(none)",
1058 gateport);
1060 code = gatemode;
1064 * Toggle metacharacter interpretation on local file names.
1066 /*VARARGS*/
1067 void
1068 setglob(int argc, char *argv[])
1071 code = togglevar(argc, argv, &doglob, "Globbing");
1075 * Toggle preserving modification times on retrieved files.
1077 /*VARARGS*/
1078 void
1079 setpreserve(int argc, char *argv[])
1082 code = togglevar(argc, argv, &preserve, "Preserve modification times");
1086 * Set debugging mode on/off and/or set level of debugging.
1088 /*VARARGS*/
1089 void
1090 setdebug(int argc, char *argv[])
1092 if (argc == 0 || argc > 2) {
1093 fprintf(ttyout, "usage: %s [ on | off | debuglevel ]\n",
1094 argv[0]);
1095 code = -1;
1096 return;
1097 } else if (argc == 2) {
1098 if (strcasecmp(argv[1], "on") == 0)
1099 debug = 1;
1100 else if (strcasecmp(argv[1], "off") == 0)
1101 debug = 0;
1102 else {
1103 int val;
1105 val = strsuftoi(argv[1]);
1106 if (val < 0) {
1107 fprintf(ttyout, "%s: bad debugging value.\n",
1108 argv[1]);
1109 code = -1;
1110 return;
1112 debug = val;
1114 } else
1115 debug = !debug;
1116 if (debug)
1117 options |= SO_DEBUG;
1118 else
1119 options &= ~SO_DEBUG;
1120 fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug);
1121 code = debug > 0;
1125 * Set current working directory on remote machine.
1127 void
1128 cd(int argc, char *argv[])
1130 int r;
1132 if (argc == 0 || argc > 2 ||
1133 (argc == 1 && !another(&argc, &argv, "remote-directory"))) {
1134 fprintf(ttyout, "usage: %s remote-directory\n", argv[0]);
1135 code = -1;
1136 return;
1138 r = command("CWD %s", argv[1]);
1139 if (r == ERROR && code == 500) {
1140 if (verbose)
1141 fputs("CWD command not recognized, trying XCWD.\n",
1142 ttyout);
1143 r = command("XCWD %s", argv[1]);
1145 if (r == COMPLETE) {
1146 dirchange = 1;
1147 updateremotecwd();
1152 * Set current working directory on local machine.
1154 void
1155 lcd(int argc, char *argv[])
1157 char *locdir;
1159 code = -1;
1160 if (argc == 1) {
1161 argc++;
1162 argv[1] = localhome;
1164 if (argc != 2) {
1165 fprintf(ttyout, "usage: %s [local-directory]\n", argv[0]);
1166 return;
1168 if ((locdir = globulize(argv[1])) == NULL)
1169 return;
1170 if (chdir(locdir) == -1)
1171 warn("lcd %s", locdir);
1172 else {
1173 updatelocalcwd();
1174 if (localcwd[0]) {
1175 fprintf(ttyout, "Local directory now: %s\n", localcwd);
1176 code = 0;
1177 } else {
1178 fprintf(ttyout, "Unable to determine local directory\n");
1181 (void)free(locdir);
1185 * Delete a single file.
1187 void
1188 delete(int argc, char *argv[])
1191 if (argc == 0 || argc > 2 ||
1192 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
1193 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
1194 code = -1;
1195 return;
1197 if (command("DELE %s", argv[1]) == COMPLETE)
1198 dirchange = 1;
1202 * Delete multiple files.
1204 void
1205 mdelete(int argc, char *argv[])
1207 sigfunc oldintr;
1208 int ointer;
1209 char *cp;
1211 if (argc == 0 ||
1212 (argc == 1 && !another(&argc, &argv, "remote-files"))) {
1213 fprintf(ttyout, "usage: %s [remote-files]\n", argv[0]);
1214 code = -1;
1215 return;
1217 mname = argv[0];
1218 mflag = 1;
1219 oldintr = xsignal(SIGINT, mintr);
1220 if (sigsetjmp(jabort, 1))
1221 mabort();
1222 while ((cp = remglob(argv, 0, NULL)) != NULL) {
1223 if (*cp == '\0') {
1224 mflag = 0;
1225 continue;
1227 if (mflag && confirm(argv[0], cp)) {
1228 if (command("DELE %s", cp) == COMPLETE)
1229 dirchange = 1;
1230 if (!mflag && fromatty) {
1231 ointer = interactive;
1232 interactive = 1;
1233 if (confirm("Continue with", "mdelete")) {
1234 mflag++;
1236 interactive = ointer;
1240 (void)xsignal(SIGINT, oldintr);
1241 mflag = 0;
1245 * Rename a remote file.
1247 void
1248 renamefile(int argc, char *argv[])
1251 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "from-name")))
1252 goto usage;
1253 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1254 usage:
1255 fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]);
1256 code = -1;
1257 return;
1259 if (command("RNFR %s", argv[1]) == CONTINUE &&
1260 command("RNTO %s", argv[2]) == COMPLETE)
1261 dirchange = 1;
1265 * Get a directory listing of remote files.
1266 * Supports being invoked as:
1267 * cmd runs
1268 * --- ----
1269 * dir, ls LIST
1270 * mlsd MLSD
1271 * nlist NLST
1272 * pdir, pls LIST |$PAGER
1273 * mmlsd MLSD |$PAGER
1275 void
1276 ls(int argc, char *argv[])
1278 const char *cmd;
1279 char *remdir, *locfile;
1280 int freelocfile, pagecmd, mlsdcmd;
1282 remdir = NULL;
1283 locfile = "-";
1284 freelocfile = pagecmd = mlsdcmd = 0;
1286 * the only commands that start with `p' are
1287 * the `pager' versions.
1289 if (argv[0][0] == 'p')
1290 pagecmd = 1;
1291 if (strcmp(argv[0] + pagecmd , "mlsd") == 0) {
1292 if (! features[FEAT_MLST]) {
1293 fprintf(ttyout,
1294 "MLSD is not supported by the remote server.\n");
1295 return;
1297 mlsdcmd = 1;
1299 if (argc == 0)
1300 goto usage;
1302 if (mlsdcmd)
1303 cmd = "MLSD";
1304 else if (strcmp(argv[0] + pagecmd, "nlist") == 0)
1305 cmd = "NLST";
1306 else
1307 cmd = "LIST";
1309 if (argc > 1)
1310 remdir = argv[1];
1311 if (argc > 2)
1312 locfile = argv[2];
1313 if (argc > 3 || ((pagecmd | mlsdcmd) && argc > 2)) {
1314 usage:
1315 if (pagecmd || mlsdcmd)
1316 fprintf(ttyout,
1317 "usage: %s [remote-path]\n", argv[0]);
1318 else
1319 fprintf(ttyout,
1320 "usage: %s [remote-path [local-file]]\n",
1321 argv[0]);
1322 code = -1;
1323 goto freels;
1326 if (pagecmd) {
1327 char *p;
1328 int len;
1330 p = getoptionvalue("pager");
1331 if (EMPTYSTRING(p))
1332 p = DEFAULTPAGER;
1333 len = strlen(p) + 2;
1334 locfile = xmalloc(len);
1335 locfile[0] = '|';
1336 (void)strlcpy(locfile + 1, p, len - 1);
1337 freelocfile = 1;
1338 } else if ((strcmp(locfile, "-") != 0) && *locfile != '|') {
1339 mname = argv[0];
1340 if ((locfile = globulize(locfile)) == NULL ||
1341 !confirm("output to local-file:", locfile)) {
1342 code = -1;
1343 goto freels;
1345 freelocfile = 1;
1347 recvrequest(cmd, locfile, remdir, "w", 0, 0);
1348 freels:
1349 if (freelocfile && locfile)
1350 (void)free(locfile);
1354 * Get a directory listing of multiple remote files.
1356 void
1357 mls(int argc, char *argv[])
1359 sigfunc oldintr;
1360 int ointer, i;
1361 int dolist;
1362 char *mode, *dest, *odest;
1364 if (argc == 0)
1365 goto usage;
1366 if (argc < 2 && !another(&argc, &argv, "remote-files"))
1367 goto usage;
1368 if (argc < 3 && !another(&argc, &argv, "local-file")) {
1369 usage:
1370 fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]);
1371 code = -1;
1372 return;
1374 odest = dest = argv[argc - 1];
1375 argv[argc - 1] = NULL;
1376 mname = argv[0];
1377 if (strcmp(dest, "-") && *dest != '|')
1378 if (((dest = globulize(dest)) == NULL) ||
1379 !confirm("output to local-file:", dest)) {
1380 code = -1;
1381 return;
1383 dolist = strcmp(argv[0], "mls");
1384 mflag = 1;
1385 oldintr = xsignal(SIGINT, mintr);
1386 if (sigsetjmp(jabort, 1))
1387 mabort();
1388 for (i = 1; mflag && i < argc-1 && connected; i++) {
1389 mode = (i == 1) ? "w" : "a";
1390 recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode,
1391 0, 0);
1392 if (!mflag && fromatty) {
1393 ointer = interactive;
1394 interactive = 1;
1395 if (confirm("Continue with", argv[0])) {
1396 mflag++;
1398 interactive = ointer;
1401 (void)xsignal(SIGINT, oldintr);
1402 mflag = 0;
1403 if (dest != odest) /* free up after globulize() */
1404 free(dest);
1408 * Do a shell escape
1410 /*ARGSUSED*/
1411 void
1412 shell(int argc, char *argv[])
1414 pid_t pid;
1415 sigfunc oldintr;
1416 char shellnam[MAXPATHLEN], *shell, *namep;
1417 int wait_status;
1419 if (argc == 0) {
1420 fprintf(ttyout, "usage: %s [command [args]]\n", argv[0]);
1421 code = -1;
1422 return;
1424 oldintr = xsignal(SIGINT, SIG_IGN);
1425 if ((pid = fork()) == 0) {
1426 for (pid = 3; pid < 20; pid++)
1427 (void)close(pid);
1428 (void)xsignal(SIGINT, SIG_DFL);
1429 shell = getenv("SHELL");
1430 if (shell == NULL)
1431 shell = _PATH_BSHELL;
1432 namep = strrchr(shell, '/');
1433 if (namep == NULL)
1434 namep = shell;
1435 else
1436 namep++;
1437 (void)strlcpy(shellnam, namep, sizeof(shellnam));
1438 if (debug) {
1439 fputs(shell, ttyout);
1440 putc('\n', ttyout);
1442 if (argc > 1) {
1443 execl(shell, shellnam, "-c", altarg, (char *)0);
1445 else {
1446 execl(shell, shellnam, (char *)0);
1448 warn("%s", shell);
1449 code = -1;
1450 exit(1);
1452 if (pid > 0)
1453 while (wait(&wait_status) != pid)
1455 (void)xsignal(SIGINT, oldintr);
1456 if (pid == -1) {
1457 warn("Try again later");
1458 code = -1;
1459 } else
1460 code = 0;
1464 * Send new user information (re-login)
1466 void
1467 user(int argc, char *argv[])
1469 char acct[80];
1470 int n, aflag = 0;
1472 if (argc == 0)
1473 goto usage;
1474 if (argc < 2)
1475 (void)another(&argc, &argv, "username");
1476 if (argc < 2 || argc > 4) {
1477 usage:
1478 fprintf(ttyout, "usage: %s username [password [account]]\n",
1479 argv[0]);
1480 code = -1;
1481 return;
1483 n = command("USER %s", argv[1]);
1484 if (n == CONTINUE) {
1485 if (argc < 3) {
1486 argv[2] = getpass("Password: ");
1487 argc++;
1489 n = command("PASS %s", argv[2]);
1491 if (n == CONTINUE) {
1492 if (argc < 4) {
1493 (void)fputs("Account: ", ttyout);
1494 (void)fflush(ttyout);
1495 if (fgets(acct, sizeof(acct) - 1, stdin) == NULL) {
1496 fprintf(ttyout,
1497 "\nEOF received; login aborted.\n");
1498 clearerr(stdin);
1499 code = -1;
1500 return;
1502 acct[strlen(acct) - 1] = '\0';
1503 argv[3] = acct; argc++;
1505 n = command("ACCT %s", argv[3]);
1506 aflag++;
1508 if (n != COMPLETE) {
1509 fputs("Login failed.\n", ttyout);
1510 return;
1512 if (!aflag && argc == 4) {
1513 (void)command("ACCT %s", argv[3]);
1515 connected = -1;
1516 getremoteinfo();
1520 * Print working directory on remote machine.
1522 /*VARARGS*/
1523 void
1524 pwd(int argc, char *argv[])
1527 code = -1;
1528 if (argc != 1) {
1529 fprintf(ttyout, "usage: %s\n", argv[0]);
1530 return;
1532 if (! remotecwd[0])
1533 updateremotecwd();
1534 if (! remotecwd[0])
1535 fprintf(ttyout, "Unable to determine remote directory\n");
1536 else {
1537 fprintf(ttyout, "Remote directory: %s\n", remotecwd);
1538 code = 0;
1543 * Print working directory on local machine.
1545 void
1546 lpwd(int argc, char *argv[])
1549 code = -1;
1550 if (argc != 1) {
1551 fprintf(ttyout, "usage: %s\n", argv[0]);
1552 return;
1554 if (! localcwd[0])
1555 updatelocalcwd();
1556 if (! localcwd[0])
1557 fprintf(ttyout, "Unable to determine local directory\n");
1558 else {
1559 fprintf(ttyout, "Local directory: %s\n", localcwd);
1560 code = 0;
1565 * Make a directory.
1567 void
1568 makedir(int argc, char *argv[])
1570 int r;
1572 if (argc == 0 || argc > 2 ||
1573 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1574 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1575 code = -1;
1576 return;
1578 r = command("MKD %s", argv[1]);
1579 if (r == ERROR && code == 500) {
1580 if (verbose)
1581 fputs("MKD command not recognized, trying XMKD.\n",
1582 ttyout);
1583 r = command("XMKD %s", argv[1]);
1585 if (r == COMPLETE)
1586 dirchange = 1;
1590 * Remove a directory.
1592 void
1593 removedir(int argc, char *argv[])
1595 int r;
1597 if (argc == 0 || argc > 2 ||
1598 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1599 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1600 code = -1;
1601 return;
1603 r = command("RMD %s", argv[1]);
1604 if (r == ERROR && code == 500) {
1605 if (verbose)
1606 fputs("RMD command not recognized, trying XRMD.\n",
1607 ttyout);
1608 r = command("XRMD %s", argv[1]);
1610 if (r == COMPLETE)
1611 dirchange = 1;
1615 * Send a line, verbatim, to the remote machine.
1617 void
1618 quote(int argc, char *argv[])
1621 if (argc == 0 ||
1622 (argc == 1 && !another(&argc, &argv, "command line to send"))) {
1623 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1624 code = -1;
1625 return;
1627 quote1("", argc, argv);
1631 * Send a SITE command to the remote machine. The line
1632 * is sent verbatim to the remote machine, except that the
1633 * word "SITE" is added at the front.
1635 void
1636 site(int argc, char *argv[])
1639 if (argc == 0 ||
1640 (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){
1641 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1642 code = -1;
1643 return;
1645 quote1("SITE ", argc, argv);
1649 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1650 * Send the result as a one-line command and get response.
1652 void
1653 quote1(const char *initial, int argc, char *argv[])
1655 int i;
1656 char buf[BUFSIZ]; /* must be >= sizeof(line) */
1658 (void)strlcpy(buf, initial, sizeof(buf));
1659 for (i = 1; i < argc; i++) {
1660 (void)strlcat(buf, argv[i], sizeof(buf));
1661 if (i < (argc - 1))
1662 (void)strlcat(buf, " ", sizeof(buf));
1664 if (command("%s", buf) == PRELIM) {
1665 while (getreply(0) == PRELIM)
1666 continue;
1668 dirchange = 1;
1671 void
1672 do_chmod(int argc, char *argv[])
1675 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode")))
1676 goto usage;
1677 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
1678 usage:
1679 fprintf(ttyout, "usage: %s mode remote-file\n", argv[0]);
1680 code = -1;
1681 return;
1683 (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1686 #define COMMAND_1ARG(argc, argv, cmd) \
1687 if (argc == 1) \
1688 command(cmd); \
1689 else \
1690 command(cmd " %s", argv[1])
1692 void
1693 do_umask(int argc, char *argv[])
1695 int oldverbose = verbose;
1697 if (argc == 0) {
1698 fprintf(ttyout, "usage: %s [umask]\n", argv[0]);
1699 code = -1;
1700 return;
1702 verbose = 1;
1703 COMMAND_1ARG(argc, argv, "SITE UMASK");
1704 verbose = oldverbose;
1707 void
1708 idlecmd(int argc, char *argv[])
1710 int oldverbose = verbose;
1712 if (argc < 1 || argc > 2) {
1713 fprintf(ttyout, "usage: %s [seconds]\n", argv[0]);
1714 code = -1;
1715 return;
1717 verbose = 1;
1718 COMMAND_1ARG(argc, argv, "SITE IDLE");
1719 verbose = oldverbose;
1723 * Ask the other side for help.
1725 void
1726 rmthelp(int argc, char *argv[])
1728 int oldverbose = verbose;
1730 if (argc == 0) {
1731 fprintf(ttyout, "usage: %s\n", argv[0]);
1732 code = -1;
1733 return;
1735 verbose = 1;
1736 COMMAND_1ARG(argc, argv, "HELP");
1737 verbose = oldverbose;
1741 * Terminate session and exit.
1742 * May be called with 0, NULL.
1744 /*VARARGS*/
1745 void
1746 quit(int argc, char *argv[])
1749 /* this may be called with argc == 0, argv == NULL */
1750 if (argc == 0 && argv != NULL) {
1751 fprintf(ttyout, "usage: %s\n", argv[0]);
1752 code = -1;
1753 return;
1755 if (connected)
1756 disconnect(0, NULL);
1757 pswitch(1);
1758 if (connected)
1759 disconnect(0, NULL);
1760 exit(0);
1764 * Terminate session, but don't exit.
1765 * May be called with 0, NULL.
1767 void
1768 disconnect(int argc, char *argv[])
1771 /* this may be called with argc == 0, argv == NULL */
1772 if (argc == 0 && argv != NULL) {
1773 fprintf(ttyout, "usage: %s\n", argv[0]);
1774 code = -1;
1775 return;
1777 if (!connected)
1778 return;
1779 (void)command("QUIT");
1780 cleanuppeer();
1783 void
1784 account(int argc, char *argv[])
1786 char *ap;
1788 if (argc == 0 || argc > 2) {
1789 fprintf(ttyout, "usage: %s [password]\n", argv[0]);
1790 code = -1;
1791 return;
1793 else if (argc == 2)
1794 ap = argv[1];
1795 else
1796 ap = getpass("Account:");
1797 (void)command("ACCT %s", ap);
1800 sigjmp_buf abortprox;
1802 void
1803 proxabort(int notused)
1806 sigint_raised = 1;
1807 alarmtimer(0);
1808 if (!proxy) {
1809 pswitch(1);
1811 if (connected) {
1812 proxflag = 1;
1814 else {
1815 proxflag = 0;
1817 pswitch(0);
1818 siglongjmp(abortprox, 1);
1821 void
1822 doproxy(int argc, char *argv[])
1824 struct cmd *c;
1825 int cmdpos;
1826 sigfunc oldintr;
1828 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) {
1829 fprintf(ttyout, "usage: %s command\n", argv[0]);
1830 code = -1;
1831 return;
1833 c = getcmd(argv[1]);
1834 if (c == (struct cmd *) -1) {
1835 fputs("?Ambiguous command.\n", ttyout);
1836 code = -1;
1837 return;
1839 if (c == 0) {
1840 fputs("?Invalid command.\n", ttyout);
1841 code = -1;
1842 return;
1844 if (!c->c_proxy) {
1845 fputs("?Invalid proxy command.\n", ttyout);
1846 code = -1;
1847 return;
1849 if (sigsetjmp(abortprox, 1)) {
1850 code = -1;
1851 return;
1853 oldintr = xsignal(SIGINT, proxabort);
1854 pswitch(1);
1855 if (c->c_conn && !connected) {
1856 fputs("Not connected.\n", ttyout);
1857 pswitch(0);
1858 (void)xsignal(SIGINT, oldintr);
1859 code = -1;
1860 return;
1862 cmdpos = strcspn(line, " \t");
1863 if (cmdpos > 0) /* remove leading "proxy " from input buffer */
1864 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1865 argv[1] = c->c_name;
1866 (*c->c_handler)(argc-1, argv+1);
1867 if (connected) {
1868 proxflag = 1;
1870 else {
1871 proxflag = 0;
1873 pswitch(0);
1874 (void)xsignal(SIGINT, oldintr);
1877 void
1878 setcase(int argc, char *argv[])
1881 code = togglevar(argc, argv, &mcase, "Case mapping");
1885 * convert the given name to lower case if it's all upper case, into
1886 * a static buffer which is returned to the caller
1888 static const char *
1889 docase(char *dst, size_t dlen, const char *src)
1891 size_t i;
1892 int dochange = 1;
1894 for (i = 0; src[i] != '\0' && i < dlen - 1; i++) {
1895 dst[i] = src[i];
1896 if (islower((unsigned char)dst[i]))
1897 dochange = 0;
1899 dst[i] = '\0';
1901 if (dochange) {
1902 for (i = 0; dst[i] != '\0'; i++)
1903 if (isupper((unsigned char)dst[i]))
1904 dst[i] = tolower((unsigned char)dst[i]);
1906 return dst;
1909 void
1910 setcr(int argc, char *argv[])
1913 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1916 void
1917 setntrans(int argc, char *argv[])
1920 if (argc == 0 || argc > 3) {
1921 fprintf(ttyout, "usage: %s [inchars [outchars]]\n", argv[0]);
1922 code = -1;
1923 return;
1925 if (argc == 1) {
1926 ntflag = 0;
1927 fputs("Ntrans off.\n", ttyout);
1928 code = ntflag;
1929 return;
1931 ntflag++;
1932 code = ntflag;
1933 (void)strlcpy(ntin, argv[1], sizeof(ntin));
1934 if (argc == 2) {
1935 ntout[0] = '\0';
1936 return;
1938 (void)strlcpy(ntout, argv[2], sizeof(ntout));
1941 static const char *
1942 dotrans(char *dst, size_t dlen, const char *src)
1944 const char *cp1;
1945 char *cp2 = dst;
1946 size_t i, ostop;
1948 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1949 continue;
1950 for (cp1 = src; *cp1; cp1++) {
1951 int found = 0;
1952 for (i = 0; *(ntin + i) && i < 16; i++) {
1953 if (*cp1 == *(ntin + i)) {
1954 found++;
1955 if (i < ostop) {
1956 *cp2++ = *(ntout + i);
1957 if (cp2 - dst >= dlen - 1)
1958 goto out;
1960 break;
1963 if (!found) {
1964 *cp2++ = *cp1;
1967 out:
1968 *cp2 = '\0';
1969 return dst;
1972 void
1973 setnmap(int argc, char *argv[])
1975 char *cp;
1977 if (argc == 1) {
1978 mapflag = 0;
1979 fputs("Nmap off.\n", ttyout);
1980 code = mapflag;
1981 return;
1983 if (argc == 0 ||
1984 (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1985 fprintf(ttyout, "usage: %s [mapin mapout]\n", argv[0]);
1986 code = -1;
1987 return;
1989 mapflag = 1;
1990 code = 1;
1991 cp = strchr(altarg, ' ');
1992 if (proxy) {
1993 while(*++cp == ' ')
1994 continue;
1995 altarg = cp;
1996 cp = strchr(altarg, ' ');
1998 *cp = '\0';
1999 (void)strlcpy(mapin, altarg, MAXPATHLEN);
2000 while (*++cp == ' ')
2001 continue;
2002 (void)strlcpy(mapout, cp, MAXPATHLEN);
2005 static const char *
2006 domap(char *dst, size_t dlen, const char *src)
2008 const char *cp1 = src;
2009 char *cp2 = mapin;
2010 const char *tp[9], *te[9];
2011 int i, toks[9], toknum = 0, match = 1;
2013 for (i=0; i < 9; ++i) {
2014 toks[i] = 0;
2016 while (match && *cp1 && *cp2) {
2017 switch (*cp2) {
2018 case '\\':
2019 if (*++cp2 != *cp1) {
2020 match = 0;
2022 break;
2023 case '$':
2024 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
2025 if (*cp1 != *(++cp2+1)) {
2026 toks[toknum = *cp2 - '1']++;
2027 tp[toknum] = cp1;
2028 while (*++cp1 && *(cp2+1)
2029 != *cp1);
2030 te[toknum] = cp1;
2032 cp2++;
2033 break;
2035 /* FALLTHROUGH */
2036 default:
2037 if (*cp2 != *cp1) {
2038 match = 0;
2040 break;
2042 if (match && *cp1) {
2043 cp1++;
2045 if (match && *cp2) {
2046 cp2++;
2049 if (!match && *cp1) /* last token mismatch */
2051 toks[toknum] = 0;
2053 cp2 = dst;
2054 *cp2 = '\0';
2055 cp1 = mapout;
2056 while (*cp1) {
2057 match = 0;
2058 switch (*cp1) {
2059 case '\\':
2060 if (*(cp1 + 1)) {
2061 *cp2++ = *++cp1;
2063 break;
2064 case '[':
2065 LOOP:
2066 if (*++cp1 == '$' &&
2067 isdigit((unsigned char)*(cp1+1))) {
2068 if (*++cp1 == '0') {
2069 const char *cp3 = src;
2071 while (*cp3) {
2072 *cp2++ = *cp3++;
2074 match = 1;
2076 else if (toks[toknum = *cp1 - '1']) {
2077 const char *cp3 = tp[toknum];
2079 while (cp3 != te[toknum]) {
2080 *cp2++ = *cp3++;
2082 match = 1;
2085 else {
2086 while (*cp1 && *cp1 != ',' &&
2087 *cp1 != ']') {
2088 if (*cp1 == '\\') {
2089 cp1++;
2091 else if (*cp1 == '$' &&
2092 isdigit((unsigned char)*(cp1+1))) {
2093 if (*++cp1 == '0') {
2094 const char *cp3 = src;
2096 while (*cp3) {
2097 *cp2++ = *cp3++;
2100 else if (toks[toknum =
2101 *cp1 - '1']) {
2102 const char *cp3=tp[toknum];
2104 while (cp3 !=
2105 te[toknum]) {
2106 *cp2++ = *cp3++;
2110 else if (*cp1) {
2111 *cp2++ = *cp1++;
2114 if (!*cp1) {
2115 fputs(
2116 "nmap: unbalanced brackets.\n",
2117 ttyout);
2118 return (src);
2120 match = 1;
2121 cp1--;
2123 if (match) {
2124 while (*++cp1 && *cp1 != ']') {
2125 if (*cp1 == '\\' && *(cp1 + 1)) {
2126 cp1++;
2129 if (!*cp1) {
2130 fputs(
2131 "nmap: unbalanced brackets.\n",
2132 ttyout);
2133 return (src);
2135 break;
2137 switch (*++cp1) {
2138 case ',':
2139 goto LOOP;
2140 case ']':
2141 break;
2142 default:
2143 cp1--;
2144 goto LOOP;
2146 break;
2147 case '$':
2148 if (isdigit((unsigned char)*(cp1 + 1))) {
2149 if (*++cp1 == '0') {
2150 const char *cp3 = src;
2152 while (*cp3) {
2153 *cp2++ = *cp3++;
2156 else if (toks[toknum = *cp1 - '1']) {
2157 const char *cp3 = tp[toknum];
2159 while (cp3 != te[toknum]) {
2160 *cp2++ = *cp3++;
2163 break;
2165 /* intentional drop through */
2166 default:
2167 *cp2++ = *cp1;
2168 break;
2170 cp1++;
2172 *cp2 = '\0';
2173 return *dst ? dst : src;
2176 void
2177 setpassive(int argc, char *argv[])
2180 if (argc == 1) {
2181 passivemode = !passivemode;
2182 activefallback = passivemode;
2183 } else if (argc != 2) {
2184 passiveusage:
2185 fprintf(ttyout, "usage: %s [ on | off | auto ]\n", argv[0]);
2186 code = -1;
2187 return;
2188 } else if (strcasecmp(argv[1], "on") == 0) {
2189 passivemode = 1;
2190 activefallback = 0;
2191 } else if (strcasecmp(argv[1], "off") == 0) {
2192 passivemode = 0;
2193 activefallback = 0;
2194 } else if (strcasecmp(argv[1], "auto") == 0) {
2195 passivemode = 1;
2196 activefallback = 1;
2197 } else
2198 goto passiveusage;
2199 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
2200 onoff(passivemode), onoff(activefallback));
2201 code = passivemode;
2204 void
2205 setepsv4(int argc, char *argv[])
2208 code = togglevar(argc, argv, &epsv4,
2209 verbose ? "EPSV/EPRT on IPv4" : NULL);
2210 epsv4bad = 0;
2213 void
2214 setsunique(int argc, char *argv[])
2217 code = togglevar(argc, argv, &sunique, "Store unique");
2220 void
2221 setrunique(int argc, char *argv[])
2224 code = togglevar(argc, argv, &runique, "Receive unique");
2228 parserate(int argc, char *argv[], int cmdlineopt)
2230 int dir, max, incr, showonly;
2231 sigfunc oldusr1, oldusr2;
2233 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) {
2234 usage:
2235 if (cmdlineopt)
2236 fprintf(ttyout,
2237 "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n",
2238 argv[0]);
2239 else
2240 fprintf(ttyout,
2241 "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n",
2242 argv[0]);
2243 return -1;
2245 dir = max = incr = showonly = 0;
2246 #define RATE_GET 1
2247 #define RATE_PUT 2
2248 #define RATE_ALL (RATE_GET | RATE_PUT)
2250 if (strcasecmp(argv[1], "all") == 0)
2251 dir = RATE_ALL;
2252 else if (strcasecmp(argv[1], "get") == 0)
2253 dir = RATE_GET;
2254 else if (strcasecmp(argv[1], "put") == 0)
2255 dir = RATE_PUT;
2256 else
2257 goto usage;
2259 if (argc >= 3) {
2260 if ((max = strsuftoi(argv[2])) < 0)
2261 goto usage;
2262 } else
2263 showonly = 1;
2265 if (argc == 4) {
2266 if ((incr = strsuftoi(argv[3])) <= 0)
2267 goto usage;
2268 } else
2269 incr = DEFAULTINCR;
2271 oldusr1 = xsignal(SIGUSR1, SIG_IGN);
2272 oldusr2 = xsignal(SIGUSR2, SIG_IGN);
2273 if (dir & RATE_GET) {
2274 if (!showonly) {
2275 rate_get = max;
2276 rate_get_incr = incr;
2278 if (!cmdlineopt || verbose)
2279 fprintf(ttyout,
2280 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
2281 onoff(rate_get), rate_get, rate_get_incr);
2283 if (dir & RATE_PUT) {
2284 if (!showonly) {
2285 rate_put = max;
2286 rate_put_incr = incr;
2288 if (!cmdlineopt || verbose)
2289 fprintf(ttyout,
2290 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
2291 onoff(rate_put), rate_put, rate_put_incr);
2293 (void)xsignal(SIGUSR1, oldusr1);
2294 (void)xsignal(SIGUSR2, oldusr2);
2295 return 0;
2298 void
2299 setrate(int argc, char *argv[])
2302 code = parserate(argc, argv, 0);
2305 /* change directory to parent directory */
2306 void
2307 cdup(int argc, char *argv[])
2309 int r;
2311 if (argc == 0) {
2312 fprintf(ttyout, "usage: %s\n", argv[0]);
2313 code = -1;
2314 return;
2316 r = command("CDUP");
2317 if (r == ERROR && code == 500) {
2318 if (verbose)
2319 fputs("CDUP command not recognized, trying XCUP.\n",
2320 ttyout);
2321 r = command("XCUP");
2323 if (r == COMPLETE) {
2324 dirchange = 1;
2325 updateremotecwd();
2330 * Restart transfer at specific point
2332 void
2333 restart(int argc, char *argv[])
2336 if (argc == 0 || argc > 2) {
2337 fprintf(ttyout, "usage: %s [restart-point]\n", argv[0]);
2338 code = -1;
2339 return;
2341 if (! features[FEAT_REST_STREAM]) {
2342 fprintf(ttyout,
2343 "Restart is not supported by the remote server.\n");
2344 return;
2346 if (argc == 2) {
2347 off_t rp;
2348 char *ep;
2350 rp = STRTOLL(argv[1], &ep, 10);
2351 if (rp < 0 || *ep != '\0')
2352 fprintf(ttyout, "restart: Invalid offset `%s'\n",
2353 argv[1]);
2354 else
2355 restart_point = rp;
2357 if (restart_point == 0)
2358 fputs("No restart point defined.\n", ttyout);
2359 else
2360 fprintf(ttyout,
2361 "Restarting at " LLF " for next get, put or append\n",
2362 (LLT)restart_point);
2366 * Show remote system type
2368 void
2369 syst(int argc, char *argv[])
2371 int oldverbose = verbose;
2373 if (argc == 0) {
2374 fprintf(ttyout, "usage: %s\n", argv[0]);
2375 code = -1;
2376 return;
2378 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2379 (void)command("SYST");
2380 verbose = oldverbose;
2383 void
2384 macdef(int argc, char *argv[])
2386 char *tmp;
2387 int c;
2389 if (argc == 0)
2390 goto usage;
2391 if (macnum == 16) {
2392 fputs("Limit of 16 macros have already been defined.\n",
2393 ttyout);
2394 code = -1;
2395 return;
2397 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2398 usage:
2399 fprintf(ttyout, "usage: %s macro_name\n", argv[0]);
2400 code = -1;
2401 return;
2403 if (interactive)
2404 fputs(
2405 "Enter macro line by line, terminating it with a null line.\n",
2406 ttyout);
2407 (void)strlcpy(macros[macnum].mac_name, argv[1],
2408 sizeof(macros[macnum].mac_name));
2409 if (macnum == 0)
2410 macros[macnum].mac_start = macbuf;
2411 else
2412 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2413 tmp = macros[macnum].mac_start;
2414 while (tmp != macbuf+4096) {
2415 if ((c = getchar()) == EOF) {
2416 fputs("macdef: end of file encountered.\n", ttyout);
2417 code = -1;
2418 return;
2420 if ((*tmp = c) == '\n') {
2421 if (tmp == macros[macnum].mac_start) {
2422 macros[macnum++].mac_end = tmp;
2423 code = 0;
2424 return;
2426 if (*(tmp-1) == '\0') {
2427 macros[macnum++].mac_end = tmp - 1;
2428 code = 0;
2429 return;
2431 *tmp = '\0';
2433 tmp++;
2435 while (1) {
2436 while ((c = getchar()) != '\n' && c != EOF)
2437 /* LOOP */;
2438 if (c == EOF || getchar() == '\n') {
2439 fputs("Macro not defined - 4K buffer exceeded.\n",
2440 ttyout);
2441 code = -1;
2442 return;
2448 * Get size of file on remote machine
2450 void
2451 sizecmd(int argc, char *argv[])
2453 off_t size;
2455 if (argc == 0 || argc > 2 ||
2456 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2457 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2458 code = -1;
2459 return;
2461 size = remotesize(argv[1], 1);
2462 if (size != -1)
2463 fprintf(ttyout,
2464 "%s\t" LLF "\n", argv[1], (LLT)size);
2465 code = (size > 0);
2469 * Get last modification time of file on remote machine
2471 void
2472 modtime(int argc, char *argv[])
2474 time_t mtime;
2476 if (argc == 0 || argc > 2 ||
2477 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2478 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2479 code = -1;
2480 return;
2482 mtime = remotemodtime(argv[1], 1);
2483 if (mtime != -1)
2484 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
2485 code = (mtime > 0);
2489 * Show status on remote machine
2491 void
2492 rmtstatus(int argc, char *argv[])
2495 if (argc == 0) {
2496 fprintf(ttyout, "usage: %s [remote-file]\n", argv[0]);
2497 code = -1;
2498 return;
2500 COMMAND_1ARG(argc, argv, "STAT");
2504 * Get file if modtime is more recent than current file
2506 void
2507 newer(int argc, char *argv[])
2510 if (getit(argc, argv, -1, "w"))
2511 fprintf(ttyout,
2512 "Local file \"%s\" is newer than remote file \"%s\".\n",
2513 argv[2], argv[1]);
2517 * Display one local file through $PAGER.
2519 void
2520 lpage(int argc, char *argv[])
2522 int len;
2523 char *p, *pager, *locfile;
2525 if (argc == 0 || argc > 2 ||
2526 (argc == 1 && !another(&argc, &argv, "local-file"))) {
2527 fprintf(ttyout, "usage: %s local-file\n", argv[0]);
2528 code = -1;
2529 return;
2531 if ((locfile = globulize(argv[1])) == NULL) {
2532 code = -1;
2533 return;
2535 p = getoptionvalue("pager");
2536 if (EMPTYSTRING(p))
2537 p = DEFAULTPAGER;
2538 len = strlen(p) + strlen(locfile) + 2;
2539 pager = xmalloc(len);
2540 (void)strlcpy(pager, p, len);
2541 (void)strlcat(pager, " ", len);
2542 (void)strlcat(pager, locfile, len);
2543 system(pager);
2544 code = 0;
2545 (void)free(pager);
2546 (void)free(locfile);
2550 * Display one remote file through $PAGER.
2552 void
2553 page(int argc, char *argv[])
2555 int ohash, orestart_point, overbose, len;
2556 char *p, *pager;
2558 if (argc == 0 || argc > 2 ||
2559 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2560 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2561 code = -1;
2562 return;
2564 p = getoptionvalue("pager");
2565 if (EMPTYSTRING(p))
2566 p = DEFAULTPAGER;
2567 len = strlen(p) + 2;
2568 pager = xmalloc(len);
2569 pager[0] = '|';
2570 (void)strlcpy(pager + 1, p, len - 1);
2572 ohash = hash;
2573 orestart_point = restart_point;
2574 overbose = verbose;
2575 hash = restart_point = verbose = 0;
2576 recvrequest("RETR", pager, argv[1], "r+", 1, 0);
2577 hash = ohash;
2578 restart_point = orestart_point;
2579 verbose = overbose;
2580 (void)free(pager);
2584 * Set the socket send or receive buffer size.
2586 void
2587 setxferbuf(int argc, char *argv[])
2589 int size, dir;
2591 if (argc != 2) {
2592 usage:
2593 fprintf(ttyout, "usage: %s size\n", argv[0]);
2594 code = -1;
2595 return;
2597 if (strcasecmp(argv[0], "sndbuf") == 0)
2598 dir = RATE_PUT;
2599 else if (strcasecmp(argv[0], "rcvbuf") == 0)
2600 dir = RATE_GET;
2601 else if (strcasecmp(argv[0], "xferbuf") == 0)
2602 dir = RATE_ALL;
2603 else
2604 goto usage;
2606 if ((size = strsuftoi(argv[1])) == -1)
2607 goto usage;
2609 if (size == 0) {
2610 fprintf(ttyout, "%s: size must be positive.\n", argv[0]);
2611 goto usage;
2614 if (dir & RATE_PUT)
2615 sndbuf_size = size;
2616 if (dir & RATE_GET)
2617 rcvbuf_size = size;
2618 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n",
2619 sndbuf_size, rcvbuf_size);
2620 code = 0;
2624 * Set or display options (defaults are provided by various env vars)
2626 void
2627 setoption(int argc, char *argv[])
2629 struct option *o;
2631 code = -1;
2632 if (argc == 0 || (argc != 1 && argc != 3)) {
2633 fprintf(ttyout, "usage: %s [option value]\n", argv[0]);
2634 return;
2637 #define OPTIONINDENT ((int) sizeof("http_proxy"))
2638 if (argc == 1) {
2639 for (o = optiontab; o->name != NULL; o++) {
2640 fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT,
2641 o->name, o->value ? o->value : "");
2643 } else {
2644 o = getoption(argv[1]);
2645 if (o == NULL) {
2646 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2647 return;
2649 FREEPTR(o->value);
2650 o->value = xstrdup(argv[2]);
2651 if (verbose)
2652 fprintf(ttyout, "Setting `%s' to `%s'.\n",
2653 o->name, o->value);
2655 code = 0;
2659 * Unset an option
2661 void
2662 unsetoption(int argc, char *argv[])
2664 struct option *o;
2666 code = -1;
2667 if (argc == 0 || argc != 2) {
2668 fprintf(ttyout, "usage: %s option\n", argv[0]);
2669 return;
2672 o = getoption(argv[1]);
2673 if (o == NULL) {
2674 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2675 return;
2677 FREEPTR(o->value);
2678 fprintf(ttyout, "Unsetting `%s'.\n", o->name);
2679 code = 0;
2683 * Display features supported by the remote host.
2685 void
2686 feat(int argc, char *argv[])
2688 int oldverbose = verbose;
2690 if (argc == 0) {
2691 fprintf(ttyout, "usage: %s\n", argv[0]);
2692 code = -1;
2693 return;
2695 if (! features[FEAT_FEAT]) {
2696 fprintf(ttyout,
2697 "FEAT is not supported by the remote server.\n");
2698 return;
2700 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2701 (void)command("FEAT");
2702 verbose = oldverbose;
2705 void
2706 mlst(int argc, char *argv[])
2708 int oldverbose = verbose;
2710 if (argc < 1 || argc > 2) {
2711 fprintf(ttyout, "usage: %s [remote-path]\n", argv[0]);
2712 code = -1;
2713 return;
2715 if (! features[FEAT_MLST]) {
2716 fprintf(ttyout,
2717 "MLST is not supported by the remote server.\n");
2718 return;
2720 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2721 COMMAND_1ARG(argc, argv, "MLST");
2722 verbose = oldverbose;
2725 void
2726 opts(int argc, char *argv[])
2728 int oldverbose = verbose;
2730 if (argc < 2 || argc > 3) {
2731 fprintf(ttyout, "usage: %s command [options]\n", argv[0]);
2732 code = -1;
2733 return;
2735 if (! features[FEAT_FEAT]) {
2736 fprintf(ttyout,
2737 "OPTS is not supported by the remote server.\n");
2738 return;
2740 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2741 if (argc == 2)
2742 command("OPTS %s", argv[1]);
2743 else
2744 command("OPTS %s %s", argv[1], argv[2]);
2745 verbose = oldverbose;