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