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