cmds.c revision 1.59 1 /* $NetBSD: cmds.c,v 1.59 1999/09/24 00:48:24 lukem Exp $ */
2
3 /*
4 * Copyright (C) 1997 and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*-
33 * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to The NetBSD Foundation
37 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
38 * NASA Ames Research Center.
39 *
40 * This code is derived from software contributed to The NetBSD Foundation
41 * by Luke Mewburn.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by the NetBSD
54 * Foundation, Inc. and its contributors.
55 * 4. Neither the name of The NetBSD Foundation nor the names of its
56 * contributors may be used to endorse or promote products derived
57 * from this software without specific prior written permission.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
60 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
61 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
62 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
63 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
64 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
65 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
66 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
67 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
68 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
69 * POSSIBILITY OF SUCH DAMAGE.
70 */
71
72 /*
73 * Copyright (c) 1985, 1989, 1993, 1994
74 * The Regents of the University of California. All rights reserved.
75 *
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. All advertising materials mentioning features or use of this software
85 * must display the following acknowledgement:
86 * This product includes software developed by the University of
87 * California, Berkeley and its contributors.
88 * 4. Neither the name of the University nor the names of its contributors
89 * may be used to endorse or promote products derived from this software
90 * without specific prior written permission.
91 *
92 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
93 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
94 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
95 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
96 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
97 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
98 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
99 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
100 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
101 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
102 * SUCH DAMAGE.
103 */
104
105 #include <sys/cdefs.h>
106 #ifndef lint
107 #if 0
108 static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94";
109 #else
110 __RCSID("$NetBSD: cmds.c,v 1.59 1999/09/24 00:48:24 lukem Exp $");
111 #endif
112 #endif /* not lint */
113
114 /*
115 * FTP User Program -- Command Routines.
116 */
117 #include <sys/types.h>
118 #include <sys/socket.h>
119 #include <sys/stat.h>
120 #include <sys/wait.h>
121 #include <arpa/ftp.h>
122
123 #include <ctype.h>
124 #include <signal.h>
125 #include <err.h>
126 #include <glob.h>
127 #include <limits.h>
128 #include <netdb.h>
129 #include <stdio.h>
130 #include <stdlib.h>
131 #include <string.h>
132 #include <time.h>
133 #include <unistd.h>
134
135 #include "ftp_var.h"
136 #include "pathnames.h"
137
138 jmp_buf jabort;
139 char *mname;
140 char *home = "/";
141
142 struct types {
143 char *t_name;
144 char *t_mode;
145 int t_type;
146 char *t_arg;
147 } types[] = {
148 { "ascii", "A", TYPE_A, 0 },
149 { "binary", "I", TYPE_I, 0 },
150 { "image", "I", TYPE_I, 0 },
151 { "ebcdic", "E", TYPE_E, 0 },
152 { "tenex", "L", TYPE_L, bytename },
153 { NULL }
154 };
155
156 /*
157 * Set transfer type.
158 */
159 void
160 settype(argc, argv)
161 int argc;
162 char *argv[];
163 {
164 struct types *p;
165 int comret;
166
167 if (argc > 2) {
168 char *sep;
169
170 fprintf(ttyout, "usage: %s [", argv[0]);
171 sep = " ";
172 for (p = types; p->t_name; p++) {
173 fprintf(ttyout, "%s%s", sep, p->t_name);
174 sep = " | ";
175 }
176 fputs(" ]\n", ttyout);
177 code = -1;
178 return;
179 }
180 if (argc < 2) {
181 fprintf(ttyout, "Using %s mode to transfer files.\n", typename);
182 code = 0;
183 return;
184 }
185 for (p = types; p->t_name; p++)
186 if (strcmp(argv[1], p->t_name) == 0)
187 break;
188 if (p->t_name == 0) {
189 fprintf(ttyout, "%s: unknown mode.\n", argv[1]);
190 code = -1;
191 return;
192 }
193 if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
194 comret = command("TYPE %s %s", p->t_mode, p->t_arg);
195 else
196 comret = command("TYPE %s", p->t_mode);
197 if (comret == COMPLETE) {
198 (void)strcpy(typename, p->t_name);
199 curtype = type = p->t_type;
200 }
201 }
202
203 /*
204 * Internal form of settype; changes current type in use with server
205 * without changing our notion of the type for data transfers.
206 * Used to change to and from ascii for listings.
207 */
208 void
209 changetype(newtype, show)
210 int newtype, show;
211 {
212 struct types *p;
213 int comret, oldverbose = verbose;
214
215 if (newtype == 0)
216 newtype = TYPE_I;
217 if (newtype == curtype)
218 return;
219 if (debug == 0 && show == 0)
220 verbose = 0;
221 for (p = types; p->t_name; p++)
222 if (newtype == p->t_type)
223 break;
224 if (p->t_name == 0) {
225 warnx("internal error: unknown type %d.", newtype);
226 return;
227 }
228 if (newtype == TYPE_L && bytename[0] != '\0')
229 comret = command("TYPE %s %s", p->t_mode, bytename);
230 else
231 comret = command("TYPE %s", p->t_mode);
232 if (comret == COMPLETE)
233 curtype = newtype;
234 verbose = oldverbose;
235 }
236
237 char *stype[] = {
238 "type",
239 "",
240 0
241 };
242
243 /*
244 * Set binary transfer type.
245 */
246 /*VARARGS*/
247 void
248 setbinary(argc, argv)
249 int argc;
250 char *argv[];
251 {
252
253 stype[1] = "binary";
254 settype(2, stype);
255 }
256
257 /*
258 * Set ascii transfer type.
259 */
260 /*VARARGS*/
261 void
262 setascii(argc, argv)
263 int argc;
264 char *argv[];
265 {
266
267 stype[1] = "ascii";
268 settype(2, stype);
269 }
270
271 /*
272 * Set tenex transfer type.
273 */
274 /*VARARGS*/
275 void
276 settenex(argc, argv)
277 int argc;
278 char *argv[];
279 {
280
281 stype[1] = "tenex";
282 settype(2, stype);
283 }
284
285 /*
286 * Set file transfer mode.
287 */
288 /*ARGSUSED*/
289 void
290 setftmode(argc, argv)
291 int argc;
292 char *argv[];
293 {
294
295 fprintf(ttyout, "We only support %s mode, sorry.\n", modename);
296 code = -1;
297 }
298
299 /*
300 * Set file transfer format.
301 */
302 /*ARGSUSED*/
303 void
304 setform(argc, argv)
305 int argc;
306 char *argv[];
307 {
308
309 fprintf(ttyout, "We only support %s format, sorry.\n", formname);
310 code = -1;
311 }
312
313 /*
314 * Set file transfer structure.
315 */
316 /*ARGSUSED*/
317 void
318 setstruct(argc, argv)
319 int argc;
320 char *argv[];
321 {
322
323 fprintf(ttyout, "We only support %s structure, sorry.\n", structname);
324 code = -1;
325 }
326
327 /*
328 * Send a single file.
329 */
330 void
331 put(argc, argv)
332 int argc;
333 char *argv[];
334 {
335 char *cmd;
336 int loc = 0;
337 char *oldargv1, *oldargv2;
338
339 if (argc == 2) {
340 argc++;
341 argv[2] = argv[1];
342 loc++;
343 }
344 if (argc < 2 && !another(&argc, &argv, "local-file"))
345 goto usage;
346 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
347 usage:
348 fprintf(ttyout, "usage: %s local-file [ remote-file ]\n",
349 argv[0]);
350 code = -1;
351 return;
352 }
353 oldargv1 = argv[1];
354 oldargv2 = argv[2];
355 if (!globulize(&argv[1])) {
356 code = -1;
357 return;
358 }
359 /*
360 * If "globulize" modifies argv[1], and argv[2] is a copy of
361 * the old argv[1], make it a copy of the new argv[1].
362 */
363 if (argv[1] != oldargv1 && argv[2] == oldargv1) {
364 argv[2] = argv[1];
365 }
366 cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
367 if (loc && ntflag) {
368 argv[2] = dotrans(argv[2]);
369 }
370 if (loc && mapflag) {
371 argv[2] = domap(argv[2]);
372 }
373 sendrequest(cmd, argv[1], argv[2],
374 argv[1] != oldargv1 || argv[2] != oldargv2);
375 if (oldargv1 != argv[1]) /* free up after globulize() */
376 free(argv[1]);
377 }
378
379 /*
380 * Send multiple files.
381 */
382 void
383 mput(argc, argv)
384 int argc;
385 char *argv[];
386 {
387 int i;
388 sig_t oldintr;
389 int ointer;
390 char *tp;
391
392 if (argc < 2 && !another(&argc, &argv, "local-files")) {
393 fprintf(ttyout, "usage: %s local-files\n", argv[0]);
394 code = -1;
395 return;
396 }
397 mname = argv[0];
398 mflag = 1;
399 oldintr = signal(SIGINT, mabort);
400 (void)setjmp(jabort);
401 if (proxy) {
402 char *cp, *tp2, tmpbuf[MAXPATHLEN];
403
404 while ((cp = remglob(argv, 0, NULL)) != NULL) {
405 if (*cp == '\0') {
406 mflag = 0;
407 continue;
408 }
409 if (mflag && confirm(argv[0], cp)) {
410 tp = cp;
411 if (mcase) {
412 while (*tp &&
413 !islower((unsigned char)*tp)) {
414 tp++;
415 }
416 if (!*tp) {
417 tp = cp;
418 tp2 = tmpbuf;
419 while ((*tp2 = *tp) != '\0') {
420 if (isupper((unsigned char)*tp2)) {
421 *tp2 =
422 tolower(*tp2);
423 }
424 tp++;
425 tp2++;
426 }
427 }
428 tp = tmpbuf;
429 }
430 if (ntflag) {
431 tp = dotrans(tp);
432 }
433 if (mapflag) {
434 tp = domap(tp);
435 }
436 sendrequest((sunique) ? "STOU" : "STOR",
437 cp, tp, cp != tp || !interactive);
438 if (!mflag && fromatty) {
439 ointer = interactive;
440 interactive = 1;
441 if (confirm("Continue with", "mput")) {
442 mflag++;
443 }
444 interactive = ointer;
445 }
446 }
447 }
448 (void)signal(SIGINT, oldintr);
449 mflag = 0;
450 return;
451 }
452 for (i = 1; i < argc; i++) {
453 char **cpp;
454 glob_t gl;
455 int flags;
456
457 if (!doglob) {
458 if (mflag && confirm(argv[0], argv[i])) {
459 tp = (ntflag) ? dotrans(argv[i]) : argv[i];
460 tp = (mapflag) ? domap(tp) : tp;
461 sendrequest((sunique) ? "STOU" : "STOR",
462 argv[i], tp, tp != argv[i] || !interactive);
463 if (!mflag && fromatty) {
464 ointer = interactive;
465 interactive = 1;
466 if (confirm("Continue with", "mput")) {
467 mflag++;
468 }
469 interactive = ointer;
470 }
471 }
472 continue;
473 }
474
475 memset(&gl, 0, sizeof(gl));
476 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
477 if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
478 warnx("%s: not found", argv[i]);
479 globfree(&gl);
480 continue;
481 }
482 for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) {
483 if (mflag && confirm(argv[0], *cpp)) {
484 tp = (ntflag) ? dotrans(*cpp) : *cpp;
485 tp = (mapflag) ? domap(tp) : tp;
486 sendrequest((sunique) ? "STOU" : "STOR",
487 *cpp, tp, *cpp != tp || !interactive);
488 if (!mflag && fromatty) {
489 ointer = interactive;
490 interactive = 1;
491 if (confirm("Continue with", "mput")) {
492 mflag++;
493 }
494 interactive = ointer;
495 }
496 }
497 }
498 globfree(&gl);
499 }
500 (void)signal(SIGINT, oldintr);
501 mflag = 0;
502 }
503
504 void
505 reget(argc, argv)
506 int argc;
507 char *argv[];
508 {
509
510 (void)getit(argc, argv, 1, "r+w");
511 }
512
513 void
514 get(argc, argv)
515 int argc;
516 char *argv[];
517 {
518
519 (void)getit(argc, argv, 0, restart_point ? "r+w" : "w" );
520 }
521
522 /*
523 * Receive one file.
524 */
525 int
526 getit(argc, argv, restartit, mode)
527 int argc;
528 char *argv[];
529 int restartit;
530 const char *mode;
531 {
532 int loc = 0;
533 int rval = 0;
534 char *oldargv1, *oldargv2, *globargv2;
535
536 if (argc == 2) {
537 argc++;
538 argv[2] = argv[1];
539 loc++;
540 }
541 if (argc < 2 && !another(&argc, &argv, "remote-file"))
542 goto usage;
543 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
544 usage:
545 fprintf(ttyout, "usage: %s remote-file [ local-file ]\n",
546 argv[0]);
547 code = -1;
548 return (0);
549 }
550 oldargv1 = argv[1];
551 oldargv2 = argv[2];
552 if (!globulize(&argv[2])) {
553 code = -1;
554 return (0);
555 }
556 globargv2 = argv[2];
557 if (loc && mcase) {
558 char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
559
560 while (*tp && !islower((unsigned char)*tp)) {
561 tp++;
562 }
563 if (!*tp) {
564 tp = argv[2];
565 tp2 = tmpbuf;
566 while ((*tp2 = *tp) != '\0') {
567 if (isupper((unsigned char)*tp2)) {
568 *tp2 = tolower(*tp2);
569 }
570 tp++;
571 tp2++;
572 }
573 argv[2] = tmpbuf;
574 }
575 }
576 if (loc && ntflag)
577 argv[2] = dotrans(argv[2]);
578 if (loc && mapflag)
579 argv[2] = domap(argv[2]);
580 if (restartit) {
581 struct stat stbuf;
582 int ret;
583
584 ret = stat(argv[2], &stbuf);
585 if (restartit == 1) {
586 if (ret < 0) {
587 warn("local: %s", argv[2]);
588 goto freegetit;
589 }
590 restart_point = stbuf.st_size;
591 } else {
592 if (ret == 0) {
593 time_t mtime;
594
595 mtime = remotemodtime(argv[1], 0);
596 if (mtime == -1)
597 goto freegetit;
598 if (stbuf.st_mtime >= mtime) {
599 rval = 1;
600 goto freegetit;
601 }
602 }
603 }
604 }
605
606 recvrequest("RETR", argv[2], argv[1], mode,
607 argv[1] != oldargv1 || argv[2] != oldargv2, loc);
608 restart_point = 0;
609 freegetit:
610 if (oldargv2 != globargv2) /* free up after globulize() */
611 free(globargv2);
612 return (rval);
613 }
614
615 /* ARGSUSED */
616 void
617 mabort(signo)
618 int signo;
619 {
620 int ointer, oconf;
621
622 alarmtimer(0);
623 putc('\n', ttyout);
624 if (mflag && fromatty) {
625 ointer = interactive;
626 oconf = confirmrest;
627 interactive = 1;
628 confirmrest = 0;
629 if (confirm("Continue with", mname)) {
630 interactive = ointer;
631 confirmrest = oconf;
632 longjmp(jabort, 0);
633 }
634 interactive = ointer;
635 confirmrest = oconf;
636 }
637 mflag = 0;
638 longjmp(jabort, 0);
639 }
640
641 /*
642 * Get multiple files.
643 */
644 void
645 mget(argc, argv)
646 int argc;
647 char *argv[];
648 {
649 sig_t oldintr;
650 int ch, ointer;
651 char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
652
653 if (argc < 2 && !another(&argc, &argv, "remote-files")) {
654 fprintf(ttyout, "usage: %s remote-files\n", argv[0]);
655 code = -1;
656 return;
657 }
658 mname = argv[0];
659 mflag = 1;
660 oldintr = signal(SIGINT, mabort);
661 (void)setjmp(jabort);
662 while ((cp = remglob(argv, proxy, NULL)) != NULL) {
663 if (*cp == '\0') {
664 mflag = 0;
665 continue;
666 }
667 if (mflag && confirm(argv[0], cp)) {
668 tp = cp;
669 if (mcase) {
670 for (tp2 = tmpbuf; (ch = *tp++) != 0; )
671 *tp2++ = isupper(ch) ? tolower(ch) : ch;
672 *tp2 = '\0';
673 tp = tmpbuf;
674 }
675 if (ntflag) {
676 tp = dotrans(tp);
677 }
678 if (mapflag) {
679 tp = domap(tp);
680 }
681 recvrequest("RETR", tp, cp, "w",
682 tp != cp || !interactive, 1);
683 if (!mflag && fromatty) {
684 ointer = interactive;
685 interactive = 1;
686 if (confirm("Continue with", "mget")) {
687 mflag++;
688 }
689 interactive = ointer;
690 }
691 }
692 }
693 (void)signal(SIGINT, oldintr);
694 mflag = 0;
695 }
696
697 char *
698 onoff(bool)
699 int bool;
700 {
701
702 return (bool ? "on" : "off");
703 }
704
705 /*
706 * Show status.
707 */
708 /*ARGSUSED*/
709 void
710 status(argc, argv)
711 int argc;
712 char *argv[];
713 {
714 int i;
715
716 if (connected)
717 fprintf(ttyout, "Connected %sto %s.\n",
718 connected == -1 ? "and logged in" : "", hostname);
719 else
720 fputs("Not connected.\n", ttyout);
721 if (!proxy) {
722 pswitch(1);
723 if (connected) {
724 fprintf(ttyout, "Connected for proxy commands to %s.\n",
725 hostname);
726 }
727 else {
728 fputs("No proxy connection.\n", ttyout);
729 }
730 pswitch(0);
731 }
732 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
733 *gateserver ? gateserver : "(none)", gateport);
734 fprintf(ttyout, "Passive mode: %s.\n", onoff(passivemode));
735 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
736 modename, typename, formname, structname);
737 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
738 onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob));
739 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n",
740 onoff(sunique), onoff(runique));
741 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve));
742 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase),
743 onoff(crflag));
744 if (ntflag) {
745 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout);
746 }
747 else {
748 fputs("Ntrans: off.\n", ttyout);
749 }
750 if (mapflag) {
751 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout);
752 }
753 else {
754 fputs("Nmap: off.\n", ttyout);
755 }
756 fprintf(ttyout,
757 "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
758 onoff(hash), mark, onoff(progress));
759 fprintf(ttyout,
760 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
761 onoff(rate_get), rate_get, rate_get_incr);
762 fprintf(ttyout,
763 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
764 onoff(rate_put), rate_put, rate_put_incr);
765 fprintf(ttyout,
766 "Socket buffer sizes: send %d, receive %d.\n",
767 sndbuf_size, rcvbuf_size);
768 fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport));
769 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s.\n", onoff(epsv4));
770 #ifndef NO_EDITCOMPLETE
771 fprintf(ttyout, "Command line editing: %s.\n", onoff(editing));
772 #endif /* !NO_EDITCOMPLETE */
773 if (macnum > 0) {
774 fputs("Macros:\n", ttyout);
775 for (i=0; i<macnum; i++) {
776 fprintf(ttyout, "\t%s\n", macros[i].mac_name);
777 }
778 }
779 code = 0;
780 }
781
782 /*
783 * Toggle a variable
784 */
785 int
786 togglevar(argc, argv, var, mesg)
787 int argc;
788 char *argv[];
789 int *var;
790 const char *mesg;
791 {
792 if (argc < 2) {
793 *var = !*var;
794 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
795 *var = 1;
796 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
797 *var = 0;
798 } else {
799 fprintf(ttyout, "usage: %s [ on | off ]\n", argv[0]);
800 return (-1);
801 }
802 if (mesg)
803 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var));
804 return (*var);
805 }
806
807 /*
808 * Set beep on cmd completed mode.
809 */
810 /*VARARGS*/
811 void
812 setbell(argc, argv)
813 int argc;
814 char *argv[];
815 {
816
817 code = togglevar(argc, argv, &bell, "Bell mode");
818 }
819
820 #ifndef NO_EDITCOMPLETE
821 /*
822 * Set command line editing
823 */
824 /*VARARGS*/
825 void
826 setedit(argc, argv)
827 int argc;
828 char *argv[];
829 {
830
831 code = togglevar(argc, argv, &editing, "Editing mode");
832 controlediting();
833 }
834 #endif /* !NO_EDITCOMPLETE */
835
836 /*
837 * Turn on packet tracing.
838 */
839 /*VARARGS*/
840 void
841 settrace(argc, argv)
842 int argc;
843 char *argv[];
844 {
845
846 code = togglevar(argc, argv, &trace, "Packet tracing");
847 }
848
849 /*
850 * Toggle hash mark printing during transfers, or set hash mark bytecount.
851 */
852 /*VARARGS*/
853 void
854 sethash(argc, argv)
855 int argc;
856 char *argv[];
857 {
858 if (argc == 1)
859 hash = !hash;
860 else if (argc != 2) {
861 fprintf(ttyout, "usage: %s [ on | off | bytecount ]\n",
862 argv[0]);
863 code = -1;
864 return;
865 } else if (strcasecmp(argv[1], "on") == 0)
866 hash = 1;
867 else if (strcasecmp(argv[1], "off") == 0)
868 hash = 0;
869 else {
870 int nmark;
871
872 nmark = strsuftoi(argv[1]);
873 if (nmark < 1) {
874 fprintf(ttyout, "mark: bad bytecount value `%s'.\n",
875 argv[1]);
876 code = -1;
877 return;
878 }
879 mark = nmark;
880 hash = 1;
881 }
882 fprintf(ttyout, "Hash mark printing %s", onoff(hash));
883 if (hash)
884 fprintf(ttyout, " (%d bytes/hash mark)", mark);
885 fputs(".\n", ttyout);
886 if (hash)
887 progress = 0;
888 code = hash;
889 }
890
891 /*
892 * Turn on printing of server echo's.
893 */
894 /*VARARGS*/
895 void
896 setverbose(argc, argv)
897 int argc;
898 char *argv[];
899 {
900
901 code = togglevar(argc, argv, &verbose, "Verbose mode");
902 }
903
904 /*
905 * Toggle PORT/LPRT cmd use before each data connection.
906 */
907 /*VARARGS*/
908 void
909 setport(argc, argv)
910 int argc;
911 char *argv[];
912 {
913
914 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds");
915 }
916
917 /*
918 * Toggle transfer progress bar.
919 */
920 /*VARARGS*/
921 void
922 setprogress(argc, argv)
923 int argc;
924 char *argv[];
925 {
926
927 code = togglevar(argc, argv, &progress, "Progress bar");
928 if (progress)
929 hash = 0;
930 }
931
932 /*
933 * Turn on interactive prompting during mget, mput, and mdelete.
934 */
935 /*VARARGS*/
936 void
937 setprompt(argc, argv)
938 int argc;
939 char *argv[];
940 {
941
942 code = togglevar(argc, argv, &interactive, "Interactive mode");
943 }
944
945 /*
946 * Toggle gate-ftp mode, or set gate-ftp server
947 */
948 /*VARARGS*/
949 void
950 setgate(argc, argv)
951 int argc;
952 char *argv[];
953 {
954 static char gsbuf[MAXHOSTNAMELEN];
955
956 if (argc > 3) {
957 fprintf(ttyout,
958 "usage: %s [ on | off | gateserver [ port ] ]\n", argv[0]);
959 code = -1;
960 return;
961 } else if (argc < 2) {
962 gatemode = !gatemode;
963 } else {
964 if (argc == 2 && strcasecmp(argv[1], "on") == 0)
965 gatemode = 1;
966 else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
967 gatemode = 0;
968 else {
969 if (argc == 3)
970 gateport = strdup(argv[2]);
971 strncpy(gsbuf, argv[1], sizeof(gsbuf) - 1);
972 gsbuf[sizeof(gsbuf) - 1] = '\0';
973 gateserver = gsbuf;
974 gatemode = 1;
975 }
976 }
977 if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
978 fprintf(ttyout,
979 "Disabling gate-ftp mode - no gate-ftp server defined.\n");
980 gatemode = 0;
981 } else {
982 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n",
983 onoff(gatemode), *gateserver ? gateserver : "(none)",
984 gateport);
985 }
986 code = gatemode;
987 }
988
989 /*
990 * Toggle metacharacter interpretation on local file names.
991 */
992 /*VARARGS*/
993 void
994 setglob(argc, argv)
995 int argc;
996 char *argv[];
997 {
998
999 code = togglevar(argc, argv, &doglob, "Globbing");
1000 }
1001
1002 /*
1003 * Toggle preserving modification times on retrieved files.
1004 */
1005 /*VARARGS*/
1006 void
1007 setpreserve(argc, argv)
1008 int argc;
1009 char *argv[];
1010 {
1011
1012 code = togglevar(argc, argv, &preserve, "Preserve modification times");
1013 }
1014
1015 /*
1016 * Set debugging mode on/off and/or set level of debugging.
1017 */
1018 /*VARARGS*/
1019 void
1020 setdebug(argc, argv)
1021 int argc;
1022 char *argv[];
1023 {
1024 if (argc > 2) {
1025 fprintf(ttyout, "usage: %s [ on | off | debuglevel ]\n",
1026 argv[0]);
1027 code = -1;
1028 return;
1029 } else if (argc == 2) {
1030 if (strcasecmp(argv[1], "on") == 0)
1031 debug = 1;
1032 else if (strcasecmp(argv[1], "off") == 0)
1033 debug = 0;
1034 else {
1035 int val;
1036
1037 val = strsuftoi(argv[1]);
1038 if (val < 0) {
1039 fprintf(ttyout, "%s: bad debugging value.\n",
1040 argv[1]);
1041 code = -1;
1042 return;
1043 }
1044 debug = val;
1045 }
1046 } else
1047 debug = !debug;
1048 if (debug)
1049 options |= SO_DEBUG;
1050 else
1051 options &= ~SO_DEBUG;
1052 fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug);
1053 code = debug > 0;
1054 }
1055
1056 /*
1057 * Set current working directory on remote machine.
1058 */
1059 void
1060 cd(argc, argv)
1061 int argc;
1062 char *argv[];
1063 {
1064 int r;
1065
1066 if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
1067 argc > 2) {
1068 fprintf(ttyout, "usage: %s remote-directory\n", argv[0]);
1069 code = -1;
1070 return;
1071 }
1072 r = command("CWD %s", argv[1]);
1073 if (r == ERROR && code == 500) {
1074 if (verbose)
1075 fputs("CWD command not recognized, trying XCWD.\n",
1076 ttyout);
1077 r = command("XCWD %s", argv[1]);
1078 }
1079 if (r == COMPLETE)
1080 dirchange = 1;
1081 }
1082
1083 /*
1084 * Set current working directory on local machine.
1085 */
1086 void
1087 lcd(argc, argv)
1088 int argc;
1089 char *argv[];
1090 {
1091 char buf[MAXPATHLEN];
1092 char *oldargv1;
1093
1094 if (argc < 2)
1095 argc++, argv[1] = home;
1096 if (argc != 2) {
1097 fprintf(ttyout, "usage: %s local-directory\n", argv[0]);
1098 code = -1;
1099 return;
1100 }
1101 oldargv1 = argv[1];
1102 if (!globulize(&argv[1])) {
1103 code = -1;
1104 return;
1105 }
1106 if (chdir(argv[1]) < 0) {
1107 warn("local: %s", argv[1]);
1108 code = -1;
1109 } else {
1110 if (getcwd(buf, sizeof(buf)) != NULL)
1111 fprintf(ttyout, "Local directory now %s\n", buf);
1112 else
1113 warn("getcwd: %s", argv[1]);
1114 code = 0;
1115 }
1116 if (oldargv1 != argv[1]) /* free up after globulize() */
1117 free(argv[1]);
1118 }
1119
1120 /*
1121 * Delete a single file.
1122 */
1123 void
1124 delete(argc, argv)
1125 int argc;
1126 char *argv[];
1127 {
1128
1129 if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) {
1130 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
1131 code = -1;
1132 return;
1133 }
1134 (void)command("DELE %s", argv[1]);
1135 }
1136
1137 /*
1138 * Delete multiple files.
1139 */
1140 void
1141 mdelete(argc, argv)
1142 int argc;
1143 char *argv[];
1144 {
1145 sig_t oldintr;
1146 int ointer;
1147 char *cp;
1148
1149 if (argc < 2 && !another(&argc, &argv, "remote-files")) {
1150 fprintf(ttyout, "usage: %s remote-files\n", argv[0]);
1151 code = -1;
1152 return;
1153 }
1154 mname = argv[0];
1155 mflag = 1;
1156 oldintr = signal(SIGINT, mabort);
1157 (void)setjmp(jabort);
1158 while ((cp = remglob(argv, 0, NULL)) != NULL) {
1159 if (*cp == '\0') {
1160 mflag = 0;
1161 continue;
1162 }
1163 if (mflag && confirm(argv[0], cp)) {
1164 (void)command("DELE %s", cp);
1165 if (!mflag && fromatty) {
1166 ointer = interactive;
1167 interactive = 1;
1168 if (confirm("Continue with", "mdelete")) {
1169 mflag++;
1170 }
1171 interactive = ointer;
1172 }
1173 }
1174 }
1175 (void)signal(SIGINT, oldintr);
1176 mflag = 0;
1177 }
1178
1179 /*
1180 * Rename a remote file.
1181 */
1182 void
1183 renamefile(argc, argv)
1184 int argc;
1185 char *argv[];
1186 {
1187
1188 if (argc < 2 && !another(&argc, &argv, "from-name"))
1189 goto usage;
1190 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1191 usage:
1192 fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]);
1193 code = -1;
1194 return;
1195 }
1196 if (command("RNFR %s", argv[1]) == CONTINUE)
1197 (void)command("RNTO %s", argv[2]);
1198 }
1199
1200 /*
1201 * Get a directory listing of remote files.
1202 */
1203 void
1204 ls(argc, argv)
1205 int argc;
1206 char *argv[];
1207 {
1208 const char *cmd;
1209 char *oldargv2, *globargv2;
1210
1211 if (argc < 2)
1212 argc++, argv[1] = NULL;
1213 if (argc < 3)
1214 argc++, argv[2] = "-";
1215 if (argc > 3) {
1216 fprintf(ttyout, "usage: %s remote-directory local-file\n",
1217 argv[0]);
1218 code = -1;
1219 return;
1220 }
1221 cmd = strcmp(argv[0], "dir") == 0 ? "LIST" : "NLST";
1222 oldargv2 = argv[2];
1223 if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
1224 code = -1;
1225 return;
1226 }
1227 globargv2 = argv[2];
1228 if (strcmp(argv[2], "-") && *argv[2] != '|')
1229 if (!globulize(&argv[2]) || !confirm("output to local-file:",
1230 argv[2])) {
1231 code = -1;
1232 goto freels;
1233 }
1234 recvrequest(cmd, argv[2], argv[1], "w", 0, 0);
1235 freels:
1236 if (argv[2] != globargv2) /* free up after globulize() */
1237 free(argv[2]);
1238 if (globargv2 != oldargv2)
1239 free(globargv2);
1240 }
1241
1242 /*
1243 * Get a directory listing of multiple remote files.
1244 */
1245 void
1246 mls(argc, argv)
1247 int argc;
1248 char *argv[];
1249 {
1250 sig_t oldintr;
1251 int ointer, i;
1252 int dolist;
1253 char mode[1], *dest, *odest;
1254
1255 if (argc < 2 && !another(&argc, &argv, "remote-files"))
1256 goto usage;
1257 if (argc < 3 && !another(&argc, &argv, "local-file")) {
1258 usage:
1259 fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]);
1260 code = -1;
1261 return;
1262 }
1263 odest = dest = argv[argc - 1];
1264 argv[argc - 1] = NULL;
1265 if (strcmp(dest, "-") && *dest != '|')
1266 if (!globulize(&dest) ||
1267 !confirm("output to local-file:", dest)) {
1268 code = -1;
1269 return;
1270 }
1271 dolist = strcmp(argv[0], "mls");
1272 mname = argv[0];
1273 mflag = 1;
1274 oldintr = signal(SIGINT, mabort);
1275 (void)setjmp(jabort);
1276 for (i = 1; mflag && i < argc-1; ++i) {
1277 *mode = (i == 1) ? 'w' : 'a';
1278 recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode,
1279 0, 0);
1280 if (!mflag && fromatty) {
1281 ointer = interactive;
1282 interactive = 1;
1283 if (confirm("Continue with", argv[0])) {
1284 mflag ++;
1285 }
1286 interactive = ointer;
1287 }
1288 }
1289 (void)signal(SIGINT, oldintr);
1290 mflag = 0;
1291 if (dest != odest) /* free up after globulize() */
1292 free(dest);
1293 }
1294
1295 /*
1296 * Do a shell escape
1297 */
1298 /*ARGSUSED*/
1299 void
1300 shell(argc, argv)
1301 int argc;
1302 char *argv[];
1303 {
1304 pid_t pid;
1305 sig_t old1, old2;
1306 char shellnam[MAXPATHLEN], *shell, *namep;
1307 int wait_status;
1308
1309 old1 = signal (SIGINT, SIG_IGN);
1310 old2 = signal (SIGQUIT, SIG_IGN);
1311 if ((pid = fork()) == 0) {
1312 for (pid = 3; pid < 20; pid++)
1313 (void)close(pid);
1314 (void)signal(SIGINT, SIG_DFL);
1315 (void)signal(SIGQUIT, SIG_DFL);
1316 shell = getenv("SHELL");
1317 if (shell == NULL)
1318 shell = _PATH_BSHELL;
1319 namep = strrchr(shell, '/');
1320 if (namep == NULL)
1321 namep = shell;
1322 shellnam[0] = '-';
1323 (void)strncpy(shellnam + 1, ++namep, sizeof(shellnam) - 2);
1324 shellnam[sizeof(shellnam) - 1] = '\0';
1325 if (strcmp(namep, "sh") != 0)
1326 shellnam[0] = '+';
1327 if (debug) {
1328 fputs(shell, ttyout);
1329 putc('\n', ttyout);
1330 }
1331 if (argc > 1) {
1332 execl(shell, shellnam, "-c", altarg, (char *)0);
1333 }
1334 else {
1335 execl(shell, shellnam, (char *)0);
1336 }
1337 warn("%s", shell);
1338 code = -1;
1339 exit(1);
1340 }
1341 if (pid > 0)
1342 while (wait(&wait_status) != pid)
1343 ;
1344 (void)signal(SIGINT, old1);
1345 (void)signal(SIGQUIT, old2);
1346 if (pid == -1) {
1347 warn("Try again later");
1348 code = -1;
1349 }
1350 else {
1351 code = 0;
1352 }
1353 }
1354
1355 /*
1356 * Send new user information (re-login)
1357 */
1358 void
1359 user(argc, argv)
1360 int argc;
1361 char *argv[];
1362 {
1363 char acct[80];
1364 int n, aflag = 0;
1365
1366 if (argc < 2)
1367 (void)another(&argc, &argv, "username");
1368 if (argc < 2 || argc > 4) {
1369 fprintf(ttyout, "usage: %s username [password] [account]\n",
1370 argv[0]);
1371 code = -1;
1372 return;
1373 }
1374 n = command("USER %s", argv[1]);
1375 if (n == CONTINUE) {
1376 if (argc < 3) {
1377 argv[2] = getpass("Password: ");
1378 argc++;
1379 }
1380 n = command("PASS %s", argv[2]);
1381 }
1382 if (n == CONTINUE) {
1383 if (argc < 4) {
1384 (void)fputs("Account: ", ttyout);
1385 (void)fflush(ttyout);
1386 if (fgets(acct, sizeof(acct) - 1, stdin) == NULL) {
1387 fprintf(ttyout,
1388 "\nEOF received; login aborted.\n");
1389 code = -1;
1390 return;
1391 }
1392 acct[strlen(acct) - 1] = '\0';
1393 argv[3] = acct; argc++;
1394 }
1395 n = command("ACCT %s", argv[3]);
1396 aflag++;
1397 }
1398 if (n != COMPLETE) {
1399 fputs("Login failed.\n", ttyout);
1400 return;
1401 }
1402 if (!aflag && argc == 4) {
1403 (void)command("ACCT %s", argv[3]);
1404 }
1405 connected = -1;
1406 }
1407
1408 /*
1409 * Print working directory on remote machine.
1410 */
1411 /*VARARGS*/
1412 void
1413 pwd(argc, argv)
1414 int argc;
1415 char *argv[];
1416 {
1417 int oldverbose = verbose;
1418
1419 /*
1420 * If we aren't verbose, this doesn't do anything!
1421 */
1422 verbose = 1;
1423 if (command("PWD") == ERROR && code == 500) {
1424 fputs("PWD command not recognized, trying XPWD.\n", ttyout);
1425 (void)command("XPWD");
1426 }
1427 verbose = oldverbose;
1428 }
1429
1430 /*
1431 * Print working directory on local machine.
1432 */
1433 void
1434 lpwd(argc, argv)
1435 int argc;
1436 char *argv[];
1437 {
1438 char buf[MAXPATHLEN];
1439
1440 if (getcwd(buf, sizeof(buf)) != NULL)
1441 fprintf(ttyout, "Local directory %s\n", buf);
1442 else
1443 warn("getcwd");
1444 code = 0;
1445 }
1446
1447 /*
1448 * Make a directory.
1449 */
1450 void
1451 makedir(argc, argv)
1452 int argc;
1453 char *argv[];
1454 {
1455
1456 if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1457 argc > 2) {
1458 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1459 code = -1;
1460 return;
1461 }
1462 if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1463 if (verbose)
1464 fputs("MKD command not recognized, trying XMKD.\n",
1465 ttyout);
1466 (void)command("XMKD %s", argv[1]);
1467 }
1468 }
1469
1470 /*
1471 * Remove a directory.
1472 */
1473 void
1474 removedir(argc, argv)
1475 int argc;
1476 char *argv[];
1477 {
1478
1479 if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1480 argc > 2) {
1481 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1482 code = -1;
1483 return;
1484 }
1485 if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1486 if (verbose)
1487 fputs("RMD command not recognized, trying XRMD.\n",
1488 ttyout);
1489 (void)command("XRMD %s", argv[1]);
1490 }
1491 }
1492
1493 /*
1494 * Send a line, verbatim, to the remote machine.
1495 */
1496 void
1497 quote(argc, argv)
1498 int argc;
1499 char *argv[];
1500 {
1501
1502 if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1503 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1504 code = -1;
1505 return;
1506 }
1507 quote1("", argc, argv);
1508 }
1509
1510 /*
1511 * Send a SITE command to the remote machine. The line
1512 * is sent verbatim to the remote machine, except that the
1513 * word "SITE" is added at the front.
1514 */
1515 void
1516 site(argc, argv)
1517 int argc;
1518 char *argv[];
1519 {
1520
1521 if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1522 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1523 code = -1;
1524 return;
1525 }
1526 quote1("SITE ", argc, argv);
1527 }
1528
1529 /*
1530 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1531 * Send the result as a one-line command and get response.
1532 */
1533 void
1534 quote1(initial, argc, argv)
1535 const char *initial;
1536 int argc;
1537 char *argv[];
1538 {
1539 int i, len;
1540 char buf[BUFSIZ]; /* must be >= sizeof(line) */
1541
1542 (void)strncpy(buf, initial, sizeof(buf) - 1);
1543 buf[sizeof(buf) - 1] = '\0';
1544 if (argc > 1) {
1545 len = strlen(buf);
1546 len += strlen(strncpy(&buf[len], argv[1],
1547 sizeof(buf) - len - 1));
1548 for (i = 2; i < argc && len < sizeof(buf); i++) {
1549 buf[len++] = ' ';
1550 len += strlen(strncpy(&buf[len], argv[i],
1551 sizeof(buf) - len) - 1);
1552 }
1553 }
1554 /* Ensure buf is NUL terminated */
1555 buf[sizeof(buf) - 1] = '\0';
1556 if (command("%s", buf) == PRELIM) {
1557 while (getreply(0) == PRELIM)
1558 continue;
1559 }
1560 }
1561
1562 void
1563 do_chmod(argc, argv)
1564 int argc;
1565 char *argv[];
1566 {
1567
1568 if (argc < 2 && !another(&argc, &argv, "mode"))
1569 goto usage;
1570 if ((argc < 3 && !another(&argc, &argv, "file-name")) || argc > 3) {
1571 usage:
1572 fprintf(ttyout, "usage: %s mode file-name\n", argv[0]);
1573 code = -1;
1574 return;
1575 }
1576 (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1577 }
1578
1579 void
1580 do_umask(argc, argv)
1581 int argc;
1582 char *argv[];
1583 {
1584 int oldverbose = verbose;
1585
1586 verbose = 1;
1587 (void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1588 verbose = oldverbose;
1589 }
1590
1591 void
1592 idle(argc, argv)
1593 int argc;
1594 char *argv[];
1595 {
1596 int oldverbose = verbose;
1597
1598 verbose = 1;
1599 (void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1600 verbose = oldverbose;
1601 }
1602
1603 /*
1604 * Ask the other side for help.
1605 */
1606 void
1607 rmthelp(argc, argv)
1608 int argc;
1609 char *argv[];
1610 {
1611 int oldverbose = verbose;
1612
1613 verbose = 1;
1614 (void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1615 verbose = oldverbose;
1616 }
1617
1618 /*
1619 * Terminate session and exit.
1620 */
1621 /*VARARGS*/
1622 void
1623 quit(argc, argv)
1624 int argc;
1625 char *argv[];
1626 {
1627
1628 if (connected)
1629 disconnect(0, 0);
1630 pswitch(1);
1631 if (connected) {
1632 disconnect(0, 0);
1633 }
1634 exit(0);
1635 }
1636
1637 /*
1638 * Terminate session, but don't exit.
1639 */
1640 void
1641 disconnect(argc, argv)
1642 int argc;
1643 char *argv[];
1644 {
1645
1646 if (!connected)
1647 return;
1648 (void)command("QUIT");
1649 if (cout) {
1650 (void)fclose(cout);
1651 }
1652 cout = NULL;
1653 connected = 0;
1654 data = -1;
1655 if (!proxy) {
1656 macnum = 0;
1657 }
1658 }
1659
1660 void
1661 account(argc, argv)
1662 int argc;
1663 char *argv[];
1664 {
1665 char *ap;
1666
1667 if (argc > 2) {
1668 fprintf(ttyout, "usage: %s [password]\n", argv[0]);
1669 code = -1;
1670 return;
1671 }
1672 else if (argc == 2)
1673 ap = argv[1];
1674 else
1675 ap = getpass("Account:");
1676 (void)command("ACCT %s", ap);
1677 }
1678
1679 jmp_buf abortprox;
1680
1681 void
1682 proxabort(notused)
1683 int notused;
1684 {
1685
1686 alarmtimer(0);
1687 if (!proxy) {
1688 pswitch(1);
1689 }
1690 if (connected) {
1691 proxflag = 1;
1692 }
1693 else {
1694 proxflag = 0;
1695 }
1696 pswitch(0);
1697 longjmp(abortprox, 1);
1698 }
1699
1700 void
1701 doproxy(argc, argv)
1702 int argc;
1703 char *argv[];
1704 {
1705 struct cmd *c;
1706 int cmdpos;
1707 sig_t oldintr;
1708
1709 if (argc < 2 && !another(&argc, &argv, "command")) {
1710 fprintf(ttyout, "usage: %s command\n", argv[0]);
1711 code = -1;
1712 return;
1713 }
1714 c = getcmd(argv[1]);
1715 if (c == (struct cmd *) -1) {
1716 fputs("?Ambiguous command.\n", ttyout);
1717 code = -1;
1718 return;
1719 }
1720 if (c == 0) {
1721 fputs("?Invalid command.\n", ttyout);
1722 code = -1;
1723 return;
1724 }
1725 if (!c->c_proxy) {
1726 fputs("?Invalid proxy command.\n", ttyout);
1727 code = -1;
1728 return;
1729 }
1730 if (setjmp(abortprox)) {
1731 code = -1;
1732 return;
1733 }
1734 oldintr = signal(SIGINT, proxabort);
1735 pswitch(1);
1736 if (c->c_conn && !connected) {
1737 fputs("Not connected.\n", ttyout);
1738 pswitch(0);
1739 (void)signal(SIGINT, oldintr);
1740 code = -1;
1741 return;
1742 }
1743 cmdpos = strcspn(line, " \t");
1744 if (cmdpos > 0) /* remove leading "proxy " from input buffer */
1745 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1746 (*c->c_handler)(argc-1, argv+1);
1747 if (connected) {
1748 proxflag = 1;
1749 }
1750 else {
1751 proxflag = 0;
1752 }
1753 pswitch(0);
1754 (void)signal(SIGINT, oldintr);
1755 }
1756
1757 void
1758 setcase(argc, argv)
1759 int argc;
1760 char *argv[];
1761 {
1762
1763 code = togglevar(argc, argv, &mcase, "Case mapping");
1764 }
1765
1766 void
1767 setcr(argc, argv)
1768 int argc;
1769 char *argv[];
1770 {
1771
1772 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1773 }
1774
1775 void
1776 setntrans(argc, argv)
1777 int argc;
1778 char *argv[];
1779 {
1780 if (argc == 1) {
1781 ntflag = 0;
1782 fputs("Ntrans off.\n", ttyout);
1783 code = ntflag;
1784 return;
1785 }
1786 ntflag++;
1787 code = ntflag;
1788 (void)strncpy(ntin, argv[1], sizeof(ntin) - 1);
1789 ntin[sizeof(ntin) - 1] = '\0';
1790 if (argc == 2) {
1791 ntout[0] = '\0';
1792 return;
1793 }
1794 (void)strncpy(ntout, argv[2], sizeof(ntout) - 1);
1795 ntout[sizeof(ntout) - 1] = '\0';
1796 }
1797
1798 char *
1799 dotrans(name)
1800 char *name;
1801 {
1802 static char new[MAXPATHLEN];
1803 char *cp1, *cp2 = new;
1804 int i, ostop, found;
1805
1806 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1807 continue;
1808 for (cp1 = name; *cp1; cp1++) {
1809 found = 0;
1810 for (i = 0; *(ntin + i) && i < 16; i++) {
1811 if (*cp1 == *(ntin + i)) {
1812 found++;
1813 if (i < ostop) {
1814 *cp2++ = *(ntout + i);
1815 }
1816 break;
1817 }
1818 }
1819 if (!found) {
1820 *cp2++ = *cp1;
1821 }
1822 }
1823 *cp2 = '\0';
1824 return (new);
1825 }
1826
1827 void
1828 setnmap(argc, argv)
1829 int argc;
1830 char *argv[];
1831 {
1832 char *cp;
1833
1834 if (argc == 1) {
1835 mapflag = 0;
1836 fputs("Nmap off.\n", ttyout);
1837 code = mapflag;
1838 return;
1839 }
1840 if ((argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1841 fprintf(ttyout, "usage: %s [mapin mapout]\n", argv[0]);
1842 code = -1;
1843 return;
1844 }
1845 mapflag = 1;
1846 code = 1;
1847 cp = strchr(altarg, ' ');
1848 if (proxy) {
1849 while(*++cp == ' ')
1850 continue;
1851 altarg = cp;
1852 cp = strchr(altarg, ' ');
1853 }
1854 *cp = '\0';
1855 (void)strncpy(mapin, altarg, MAXPATHLEN - 1);
1856 while (*++cp == ' ')
1857 continue;
1858 (void)strncpy(mapout, cp, MAXPATHLEN - 1);
1859 }
1860
1861 char *
1862 domap(name)
1863 char *name;
1864 {
1865 static char new[MAXPATHLEN];
1866 char *cp1 = name, *cp2 = mapin;
1867 char *tp[9], *te[9];
1868 int i, toks[9], toknum = 0, match = 1;
1869
1870 for (i=0; i < 9; ++i) {
1871 toks[i] = 0;
1872 }
1873 while (match && *cp1 && *cp2) {
1874 switch (*cp2) {
1875 case '\\':
1876 if (*++cp2 != *cp1) {
1877 match = 0;
1878 }
1879 break;
1880 case '$':
1881 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1882 if (*cp1 != *(++cp2+1)) {
1883 toks[toknum = *cp2 - '1']++;
1884 tp[toknum] = cp1;
1885 while (*++cp1 && *(cp2+1)
1886 != *cp1);
1887 te[toknum] = cp1;
1888 }
1889 cp2++;
1890 break;
1891 }
1892 /* FALLTHROUGH */
1893 default:
1894 if (*cp2 != *cp1) {
1895 match = 0;
1896 }
1897 break;
1898 }
1899 if (match && *cp1) {
1900 cp1++;
1901 }
1902 if (match && *cp2) {
1903 cp2++;
1904 }
1905 }
1906 if (!match && *cp1) /* last token mismatch */
1907 {
1908 toks[toknum] = 0;
1909 }
1910 cp1 = new;
1911 *cp1 = '\0';
1912 cp2 = mapout;
1913 while (*cp2) {
1914 match = 0;
1915 switch (*cp2) {
1916 case '\\':
1917 if (*(cp2 + 1)) {
1918 *cp1++ = *++cp2;
1919 }
1920 break;
1921 case '[':
1922 LOOP:
1923 if (*++cp2 == '$' &&
1924 isdigit((unsigned char)*(cp2+1))) {
1925 if (*++cp2 == '0') {
1926 char *cp3 = name;
1927
1928 while (*cp3) {
1929 *cp1++ = *cp3++;
1930 }
1931 match = 1;
1932 }
1933 else if (toks[toknum = *cp2 - '1']) {
1934 char *cp3 = tp[toknum];
1935
1936 while (cp3 != te[toknum]) {
1937 *cp1++ = *cp3++;
1938 }
1939 match = 1;
1940 }
1941 }
1942 else {
1943 while (*cp2 && *cp2 != ',' &&
1944 *cp2 != ']') {
1945 if (*cp2 == '\\') {
1946 cp2++;
1947 }
1948 else if (*cp2 == '$' &&
1949 isdigit((unsigned char)*(cp2+1))) {
1950 if (*++cp2 == '0') {
1951 char *cp3 = name;
1952
1953 while (*cp3) {
1954 *cp1++ = *cp3++;
1955 }
1956 }
1957 else if (toks[toknum =
1958 *cp2 - '1']) {
1959 char *cp3=tp[toknum];
1960
1961 while (cp3 !=
1962 te[toknum]) {
1963 *cp1++ = *cp3++;
1964 }
1965 }
1966 }
1967 else if (*cp2) {
1968 *cp1++ = *cp2++;
1969 }
1970 }
1971 if (!*cp2) {
1972 fputs(
1973 "nmap: unbalanced brackets.\n",
1974 ttyout);
1975 return (name);
1976 }
1977 match = 1;
1978 cp2--;
1979 }
1980 if (match) {
1981 while (*++cp2 && *cp2 != ']') {
1982 if (*cp2 == '\\' && *(cp2 + 1)) {
1983 cp2++;
1984 }
1985 }
1986 if (!*cp2) {
1987 fputs(
1988 "nmap: unbalanced brackets.\n",
1989 ttyout);
1990 return (name);
1991 }
1992 break;
1993 }
1994 switch (*++cp2) {
1995 case ',':
1996 goto LOOP;
1997 case ']':
1998 break;
1999 default:
2000 cp2--;
2001 goto LOOP;
2002 }
2003 break;
2004 case '$':
2005 if (isdigit((unsigned char)*(cp2 + 1))) {
2006 if (*++cp2 == '0') {
2007 char *cp3 = name;
2008
2009 while (*cp3) {
2010 *cp1++ = *cp3++;
2011 }
2012 }
2013 else if (toks[toknum = *cp2 - '1']) {
2014 char *cp3 = tp[toknum];
2015
2016 while (cp3 != te[toknum]) {
2017 *cp1++ = *cp3++;
2018 }
2019 }
2020 break;
2021 }
2022 /* intentional drop through */
2023 default:
2024 *cp1++ = *cp2;
2025 break;
2026 }
2027 cp2++;
2028 }
2029 *cp1 = '\0';
2030 if (!*new) {
2031 return (name);
2032 }
2033 return (new);
2034 }
2035
2036 void
2037 setpassive(argc, argv)
2038 int argc;
2039 char *argv[];
2040 {
2041
2042 code = togglevar(argc, argv, &passivemode,
2043 verbose ? "Passive mode" : NULL);
2044 }
2045
2046 void
2047 setsunique(argc, argv)
2048 int argc;
2049 char *argv[];
2050 {
2051
2052 code = togglevar(argc, argv, &sunique, "Store unique");
2053 }
2054
2055 void
2056 setrunique(argc, argv)
2057 int argc;
2058 char *argv[];
2059 {
2060
2061 code = togglevar(argc, argv, &runique, "Receive unique");
2062 }
2063
2064 int
2065 parserate(argc, argv, cmdlineopt)
2066 int argc;
2067 char *argv[];
2068 int cmdlineopt;
2069 {
2070 int dir, max, incr, showonly;
2071 sig_t oldusr1, oldusr2;
2072
2073 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) {
2074 usage:
2075 if (cmdlineopt)
2076 fprintf(ttyout,
2077 "usage: %s (all|get|put),maximum[,increment]]\n",
2078 argv[0]);
2079 else
2080 fprintf(ttyout,
2081 "usage: %s (all|get|put) [maximum [increment]]\n",
2082 argv[0]);
2083 return -1;
2084 }
2085 dir = max = incr = showonly = 0;
2086 #define RATE_GET 1
2087 #define RATE_PUT 2
2088 #define RATE_ALL (RATE_GET | RATE_PUT)
2089
2090 if (strcasecmp(argv[1], "all") == 0)
2091 dir = RATE_ALL;
2092 else if (strcasecmp(argv[1], "get") == 0)
2093 dir = RATE_GET;
2094 else if (strcasecmp(argv[1], "put") == 0)
2095 dir = RATE_PUT;
2096 else
2097 goto usage;
2098
2099 if (argc >= 3) {
2100 if ((max = strsuftoi(argv[2])) < 0)
2101 goto usage;
2102 } else
2103 showonly = 1;
2104
2105 if (argc == 4) {
2106 if ((incr = strsuftoi(argv[3])) <= 0)
2107 goto usage;
2108 } else
2109 incr = DEFAULTINCR;
2110
2111 oldusr1 = signal(SIGUSR1, SIG_IGN);
2112 oldusr2 = signal(SIGUSR2, SIG_IGN);
2113 if (dir & RATE_GET) {
2114 if (!showonly) {
2115 rate_get = max;
2116 rate_get_incr = incr;
2117 }
2118 if (!cmdlineopt || verbose)
2119 fprintf(ttyout,
2120 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
2121 onoff(rate_get), rate_get, rate_get_incr);
2122 }
2123 if (dir & RATE_PUT) {
2124 if (!showonly) {
2125 rate_put = max;
2126 rate_put_incr = incr;
2127 }
2128 if (!cmdlineopt || verbose)
2129 fprintf(ttyout,
2130 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
2131 onoff(rate_put), rate_put, rate_put_incr);
2132 }
2133 (void)signal(SIGUSR1, oldusr1);
2134 (void)signal(SIGUSR2, oldusr2);
2135 return 0;
2136 }
2137
2138 void
2139 setrate(argc, argv)
2140 int argc;
2141 char *argv[];
2142 {
2143
2144 code = parserate(argc, argv, 0);
2145 }
2146
2147 /* change directory to parent directory */
2148 void
2149 cdup(argc, argv)
2150 int argc;
2151 char *argv[];
2152 {
2153 int r;
2154
2155 r = command("CDUP");
2156 if (r == ERROR && code == 500) {
2157 if (verbose)
2158 fputs("CDUP command not recognized, trying XCUP.\n",
2159 ttyout);
2160 r = command("XCUP");
2161 }
2162 if (r == COMPLETE)
2163 dirchange = 1;
2164 }
2165
2166 /*
2167 * Restart transfer at specific point
2168 */
2169 void
2170 restart(argc, argv)
2171 int argc;
2172 char *argv[];
2173 {
2174
2175 if (argc > 2) {
2176 fprintf(ttyout, "usage: %s [restart_point]\n", argv[0]);
2177 code = -1;
2178 return;
2179 }
2180 if (argc == 2) {
2181 #ifndef NO_QUAD
2182 quad_t rp;
2183 #else
2184 long rp;
2185 #endif
2186 char *ep;
2187
2188 #ifndef NO_QUAD
2189 rp = strtoq(argv[1], &ep, 10);
2190 #else
2191 rp = strtol(argv[1], &ep, 10);
2192 #endif
2193 if (rp < 0 || *ep != '\0')
2194 fprintf(ttyout, "restart: Invalid offset `%s'\n",
2195 argv[1]);
2196 else
2197 restart_point = rp;
2198 }
2199 if (restart_point == 0)
2200 fputs("No restart point defined.\n", ttyout);
2201 else
2202 fprintf(ttyout,
2203 #ifndef NO_QUAD
2204 "Restarting at %qd for next get, put or append\n",
2205 (long long)restart_point);
2206 #else
2207 "Restarting at %ld for next get, put or append\n",
2208 (long)restart_point);
2209 #endif
2210 }
2211
2212 /*
2213 * Show remote system type
2214 */
2215 void
2216 syst(argc, argv)
2217 int argc;
2218 char *argv[];
2219 {
2220
2221 (void)command("SYST");
2222 }
2223
2224 void
2225 macdef(argc, argv)
2226 int argc;
2227 char *argv[];
2228 {
2229 char *tmp;
2230 int c;
2231
2232 if (macnum == 16) {
2233 fputs("Limit of 16 macros have already been defined.\n",
2234 ttyout);
2235 code = -1;
2236 return;
2237 }
2238 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2239 fprintf(ttyout, "usage: %s macro_name\n", argv[0]);
2240 code = -1;
2241 return;
2242 }
2243 if (interactive)
2244 fputs(
2245 "Enter macro line by line, terminating it with a null line.\n",
2246 ttyout);
2247 (void)strncpy(macros[macnum].mac_name, argv[1],
2248 sizeof(macros[macnum].mac_name) - 1);
2249 macros[macnum].mac_name[sizeof(macros[macnum].mac_name) - 1] = '\0';
2250 if (macnum == 0)
2251 macros[macnum].mac_start = macbuf;
2252 else
2253 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2254 tmp = macros[macnum].mac_start;
2255 while (tmp != macbuf+4096) {
2256 if ((c = getchar()) == EOF) {
2257 fputs("macdef: end of file encountered.\n", ttyout);
2258 code = -1;
2259 return;
2260 }
2261 if ((*tmp = c) == '\n') {
2262 if (tmp == macros[macnum].mac_start) {
2263 macros[macnum++].mac_end = tmp;
2264 code = 0;
2265 return;
2266 }
2267 if (*(tmp-1) == '\0') {
2268 macros[macnum++].mac_end = tmp - 1;
2269 code = 0;
2270 return;
2271 }
2272 *tmp = '\0';
2273 }
2274 tmp++;
2275 }
2276 while (1) {
2277 while ((c = getchar()) != '\n' && c != EOF)
2278 /* LOOP */;
2279 if (c == EOF || getchar() == '\n') {
2280 fputs("Macro not defined - 4K buffer exceeded.\n",
2281 ttyout);
2282 code = -1;
2283 return;
2284 }
2285 }
2286 }
2287
2288 /*
2289 * Get size of file on remote machine
2290 */
2291 void
2292 sizecmd(argc, argv)
2293 int argc;
2294 char *argv[];
2295 {
2296 off_t size;
2297
2298 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2299 fprintf(ttyout, "usage: %s filename\n", argv[0]);
2300 code = -1;
2301 return;
2302 }
2303 size = remotesize(argv[1], 1);
2304 if (size != -1)
2305 fprintf(ttyout,
2306 #ifndef NO_QUAD
2307 "%s\t%qd\n", argv[1], (long long)size);
2308 #else
2309 "%s\t%ld\n", argv[1], (long)size);
2310 #endif
2311 code = size;
2312 }
2313
2314 /*
2315 * Get last modification time of file on remote machine
2316 */
2317 void
2318 modtime(argc, argv)
2319 int argc;
2320 char *argv[];
2321 {
2322 time_t mtime;
2323
2324 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2325 fprintf(ttyout, "usage: %s filename\n", argv[0]);
2326 code = -1;
2327 return;
2328 }
2329 mtime = remotemodtime(argv[1], 1);
2330 if (mtime != -1)
2331 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
2332 code = mtime;
2333 }
2334
2335 /*
2336 * Show status on remote machine
2337 */
2338 void
2339 rmtstatus(argc, argv)
2340 int argc;
2341 char *argv[];
2342 {
2343
2344 (void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
2345 }
2346
2347 /*
2348 * Get file if modtime is more recent than current file
2349 */
2350 void
2351 newer(argc, argv)
2352 int argc;
2353 char *argv[];
2354 {
2355
2356 if (getit(argc, argv, -1, "w"))
2357 fprintf(ttyout,
2358 "Local file \"%s\" is newer than remote file \"%s\".\n",
2359 argv[2], argv[1]);
2360 }
2361
2362 /*
2363 * Display one file through $PAGER (defaults to "more").
2364 */
2365 void
2366 page(argc, argv)
2367 int argc;
2368 char *argv[];
2369 {
2370 int ohash, orestart_point, overbose, len;
2371 char *p, *pager, *oldargv1;
2372
2373 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2374 fprintf(ttyout, "usage: %s filename\n", argv[0]);
2375 code = -1;
2376 return;
2377 }
2378 oldargv1 = argv[1];
2379 if (!globulize(&argv[1])) {
2380 code = -1;
2381 return;
2382 }
2383 p = getenv("PAGER");
2384 if (p == NULL || p[0] == '\0')
2385 p = PAGER;
2386 len = strlen(p) + 2;
2387 pager = xmalloc(len);
2388 pager[0] = '|';
2389 strlcpy(pager + 1, p, len - 1);
2390
2391 ohash = hash;
2392 orestart_point = restart_point;
2393 overbose = verbose;
2394 hash = restart_point = verbose = 0;
2395 recvrequest("RETR", pager, argv[1], "r+w", 1, 0);
2396 (void)free(pager);
2397 hash = ohash;
2398 restart_point = orestart_point;
2399 verbose = overbose;
2400 if (oldargv1 != argv[1]) /* free up after globulize() */
2401 free(argv[1]);
2402 }
2403
2404 /*
2405 * Set the socket send or receive buffer size.
2406 */
2407 void
2408 setxferbuf(argc, argv)
2409 int argc;
2410 char *argv[];
2411 {
2412 int size, dir;
2413
2414 if (argc != 2) {
2415 usage:
2416 fprintf(ttyout, "usage: %s size\n", argv[0]);
2417 code = -1;
2418 return;
2419 }
2420 if (strcasecmp(argv[0], "sndbuf") == 0)
2421 dir = RATE_PUT;
2422 else if (strcasecmp(argv[0], "rcvbuf") == 0)
2423 dir = RATE_GET;
2424 else if (strcasecmp(argv[0], "xferbuf") == 0)
2425 dir = RATE_ALL;
2426 else
2427 goto usage;
2428
2429 if ((size = strsuftoi(argv[1])) == -1)
2430 goto usage;
2431
2432 if (size == 0) {
2433 fprintf(ttyout, "%s: size must be positive.\n", argv[0]);
2434 goto usage;
2435 }
2436
2437 if (dir & RATE_PUT)
2438 sndbuf_size = size;
2439 if (dir & RATE_GET)
2440 rcvbuf_size = size;
2441 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n",
2442 sndbuf_size, rcvbuf_size);
2443 }
2444
2445
2446 void
2447 setepsv4(argc, argv)
2448 int argc;
2449 char *argv[];
2450 {
2451
2452 code = togglevar(argc, argv, &epsv4,
2453 verbose ? "EPSV/EPRT on IPv4" : NULL);
2454 }
2455