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