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