xref: /netbsd-src/usr.bin/ftp/cmds.c (revision 4d7e773266e3c3f48566c86c0ad52d51c6454fd1)
1 /*	$NetBSD: cmds.c,v 1.32 1997/11/02 00:18: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.32 1997/11/02 00:18: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;
785 		char *ep;
786 
787 		nmark = strtol(argv[1], &ep, 10);
788 		if (nmark < 1 || *ep != '\0') {
789 			printf("mark: bad bytecount value `%s'.\n", argv[1]);
790 			code = -1;
791 			return;
792 		}
793 		mark = nmark;
794 		hash = 1;
795 	}
796 	printf("Hash mark printing %s", onoff(hash));
797 	if (hash)
798 		printf(" (%d bytes/hash mark)", mark);
799 	puts(".");
800 	code = hash;
801 }
802 
803 /*
804  * Turn on printing of server echo's.
805  */
806 /*VARARGS*/
807 void
808 setverbose(argc, argv)
809 	int argc;
810 	char *argv[];
811 {
812 
813 	code = togglevar(argc, argv, &verbose, "Verbose mode");
814 }
815 
816 /*
817  * Toggle PORT cmd use before each data connection.
818  */
819 /*VARARGS*/
820 void
821 setport(argc, argv)
822 	int argc;
823 	char *argv[];
824 {
825 
826 	code = togglevar(argc, argv, &sendport, "Use of PORT cmds");
827 }
828 
829 /*
830  * Toggle transfer progress bar.
831  */
832 /*VARARGS*/
833 void
834 setprogress(argc, argv)
835 	int argc;
836 	char *argv[];
837 {
838 
839 	code = togglevar(argc, argv, &progress, "Progress bar");
840 }
841 
842 /*
843  * Turn on interactive prompting during mget, mput, and mdelete.
844  */
845 /*VARARGS*/
846 void
847 setprompt(argc, argv)
848 	int argc;
849 	char *argv[];
850 {
851 
852 	code = togglevar(argc, argv, &interactive, "Interactive mode");
853 }
854 
855 /*
856  * Toggle gate-ftp mode, or set gate-ftp server
857  */
858 /*VARARGS*/
859 void
860 setgate(argc, argv)
861 	int argc;
862 	char *argv[];
863 {
864 	static char gsbuf[MAXHOSTNAMELEN];
865 
866 	if (argc > 3) {
867 		printf("usage: %s [ on | off | gateserver [ port ] ]\n",
868 		    argv[0]);
869 		code = -1;
870 		return;
871 	} else if (argc < 2) {
872 		gatemode = !gatemode;
873 	} else {
874 		if (argc == 2 && strcasecmp(argv[1], "on") == 0)
875 			gatemode = 1;
876 		else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
877 			gatemode = 0;
878 		else {
879 			if (argc == 3) {
880 				char *ep;
881 				long port;
882 
883 				port = strtol(argv[2], &ep, 10);
884 				if (port < 0 || port > 0xffff || *ep != '\0') {
885 					printf("%s: bad gateport value.\n",
886 					    argv[2]);
887 					code = -1;
888 					return;
889 				}
890 				gateport = htons(port);
891 			}
892 			strncpy(gsbuf, argv[1], sizeof(gsbuf) - 1);
893 			gsbuf[sizeof(gsbuf) - 1] = '\0';
894 			gateserver = gsbuf;
895 			gatemode = 1;
896 		}
897 	}
898 	if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
899 		printf(
900 		    "Disabling gate-ftp mode - no gate-ftp server defined.\n");
901 		gatemode = 0;
902 	} else {
903 		printf("Gate ftp: %s, server %s, port %d.\n", onoff(gatemode),
904 		    *gateserver ? gateserver : "(none)", ntohs(gateport));
905 	}
906 	code = gatemode;
907 }
908 
909 /*
910  * Toggle metacharacter interpretation on local file names.
911  */
912 /*VARARGS*/
913 void
914 setglob(argc, argv)
915 	int argc;
916 	char *argv[];
917 {
918 
919 	code = togglevar(argc, argv, &doglob, "Globbing");
920 }
921 
922 /*
923  * Toggle preserving modification times on retreived files.
924  */
925 /*VARARGS*/
926 void
927 setpreserve(argc, argv)
928 	int argc;
929 	char *argv[];
930 {
931 
932 	code = togglevar(argc, argv, &preserve, "Preserve modification times");
933 }
934 
935 /*
936  * Set debugging mode on/off and/or set level of debugging.
937  */
938 /*VARARGS*/
939 void
940 setdebug(argc, argv)
941 	int argc;
942 	char *argv[];
943 {
944 	if (argc > 2) {
945 		printf("usage: %s [ on | off | debuglevel ]\n", argv[0]);
946 		code = -1;
947 		return;
948 	} else if (argc == 2) {
949 		if (strcasecmp(argv[1], "on") == 0)
950 			debug = 1;
951 		else if (strcasecmp(argv[1], "off") == 0)
952 			debug = 0;
953 		else {
954 			char *ep;
955 			long val;
956 
957 			val = strtol(argv[1], &ep, 10);
958 			if (val < 0 || val > INT_MAX || *ep != '\0') {
959 				printf("%s: bad debugging value.\n", argv[1]);
960 				code = -1;
961 				return;
962 			}
963 			debug = (int)val;
964 		}
965 	} else
966 		debug = !debug;
967 	if (debug)
968 		options |= SO_DEBUG;
969 	else
970 		options &= ~SO_DEBUG;
971 	printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
972 	code = debug > 0;
973 }
974 
975 /*
976  * Set current working directory on remote machine.
977  */
978 void
979 cd(argc, argv)
980 	int argc;
981 	char *argv[];
982 {
983 	int r;
984 
985 	if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
986 	    argc > 2) {
987 		printf("usage: %s remote-directory\n", argv[0]);
988 		code = -1;
989 		return;
990 	}
991 	r = command("CWD %s", argv[1]);
992 	if (r == ERROR && code == 500) {
993 		if (verbose)
994 			puts("CWD command not recognized, trying XCWD.");
995 		r = command("XCWD %s", argv[1]);
996 	}
997 	if (r == COMPLETE)
998 		dirchange = 1;
999 }
1000 
1001 /*
1002  * Set current working directory on local machine.
1003  */
1004 void
1005 lcd(argc, argv)
1006 	int argc;
1007 	char *argv[];
1008 {
1009 	char buf[MAXPATHLEN];
1010 	char *oldargv1;
1011 
1012 	if (argc < 2)
1013 		argc++, argv[1] = home;
1014 	if (argc != 2) {
1015 		printf("usage: %s local-directory\n", argv[0]);
1016 		code = -1;
1017 		return;
1018 	}
1019 	oldargv1 = argv[1];
1020 	if (!globulize(&argv[1])) {
1021 		code = -1;
1022 		return;
1023 	}
1024 	if (chdir(argv[1]) < 0) {
1025 		warn("local: %s", argv[1]);
1026 		code = -1;
1027 	} else {
1028 		if (getcwd(buf, sizeof(buf)) != NULL)
1029 			printf("Local directory now %s\n", buf);
1030 		else
1031 			warn("getcwd: %s", argv[1]);
1032 		code = 0;
1033 	}
1034 	if (oldargv1 != argv[1])	/* free up after globulize() */
1035 		free(argv[1]);
1036 }
1037 
1038 /*
1039  * Delete a single file.
1040  */
1041 void
1042 delete(argc, argv)
1043 	int argc;
1044 	char *argv[];
1045 {
1046 
1047 	if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) {
1048 		printf("usage: %s remote-file\n", argv[0]);
1049 		code = -1;
1050 		return;
1051 	}
1052 	(void)command("DELE %s", argv[1]);
1053 }
1054 
1055 /*
1056  * Delete multiple files.
1057  */
1058 void
1059 mdelete(argc, argv)
1060 	int argc;
1061 	char *argv[];
1062 {
1063 	sig_t oldintr;
1064 	int ointer;
1065 	char *cp;
1066 
1067 	if (argc < 2 && !another(&argc, &argv, "remote-files")) {
1068 		printf("usage: %s remote-files\n", argv[0]);
1069 		code = -1;
1070 		return;
1071 	}
1072 	mname = argv[0];
1073 	mflag = 1;
1074 	oldintr = signal(SIGINT, mabort);
1075 	(void)setjmp(jabort);
1076 	while ((cp = remglob(argv, 0, NULL)) != NULL) {
1077 		if (*cp == '\0') {
1078 			mflag = 0;
1079 			continue;
1080 		}
1081 		if (mflag && confirm(argv[0], cp)) {
1082 			(void)command("DELE %s", cp);
1083 			if (!mflag && fromatty) {
1084 				ointer = interactive;
1085 				interactive = 1;
1086 				if (confirm("Continue with", "mdelete")) {
1087 					mflag++;
1088 				}
1089 				interactive = ointer;
1090 			}
1091 		}
1092 	}
1093 	(void)signal(SIGINT, oldintr);
1094 	mflag = 0;
1095 }
1096 
1097 /*
1098  * Rename a remote file.
1099  */
1100 void
1101 renamefile(argc, argv)
1102 	int argc;
1103 	char *argv[];
1104 {
1105 
1106 	if (argc < 2 && !another(&argc, &argv, "from-name"))
1107 		goto usage;
1108 	if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1109 usage:
1110 		printf("usage: %s from-name to-name\n", argv[0]);
1111 		code = -1;
1112 		return;
1113 	}
1114 	if (command("RNFR %s", argv[1]) == CONTINUE)
1115 		(void)command("RNTO %s", argv[2]);
1116 }
1117 
1118 /*
1119  * Get a directory listing of remote files.
1120  */
1121 void
1122 ls(argc, argv)
1123 	int argc;
1124 	char *argv[];
1125 {
1126 	const char *cmd;
1127 	char *oldargv2, *globargv2;
1128 
1129 	if (argc < 2)
1130 		argc++, argv[1] = NULL;
1131 	if (argc < 3)
1132 		argc++, argv[2] = "-";
1133 	if (argc > 3) {
1134 		printf("usage: %s remote-directory local-file\n", argv[0]);
1135 		code = -1;
1136 		return;
1137 	}
1138 	cmd = strcmp(argv[0], "dir") == 0 ? "LIST" : "NLST";
1139 	oldargv2 = argv[2];
1140 	if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
1141 		code = -1;
1142 		return;
1143 	}
1144 	globargv2 = argv[2];
1145 	if (strcmp(argv[2], "-") && *argv[2] != '|')
1146 		if (!globulize(&argv[2]) || !confirm("output to local-file:",
1147 		    argv[2])) {
1148 			code = -1;
1149 			goto freels;
1150 	}
1151 	recvrequest(cmd, argv[2], argv[1], "w", 0, 0);
1152 
1153 	/* flush results in case commands are coming from a pipe */
1154 	fflush(stdout);
1155 freels:
1156 	if (argv[2] != globargv2)		/* free up after globulize() */
1157 		free(argv[2]);
1158 	if (globargv2 != oldargv2)
1159 		free(globargv2);
1160 }
1161 
1162 /*
1163  * Get a directory listing of multiple remote files.
1164  */
1165 void
1166 mls(argc, argv)
1167 	int argc;
1168 	char *argv[];
1169 {
1170 	sig_t oldintr;
1171 	int ointer, i;
1172 	int dolist;
1173 	char mode[1], *dest, *odest;
1174 
1175 	if (argc < 2 && !another(&argc, &argv, "remote-files"))
1176 		goto usage;
1177 	if (argc < 3 && !another(&argc, &argv, "local-file")) {
1178 usage:
1179 		printf("usage: %s remote-files local-file\n", argv[0]);
1180 		code = -1;
1181 		return;
1182 	}
1183 	odest = dest = argv[argc - 1];
1184 	argv[argc - 1] = NULL;
1185 	if (strcmp(dest, "-") && *dest != '|')
1186 		if (!globulize(&dest) ||
1187 		    !confirm("output to local-file:", dest)) {
1188 			code = -1;
1189 			return;
1190 	}
1191 	dolist = strcmp(argv[0], "mls");
1192 	mname = argv[0];
1193 	mflag = 1;
1194 	oldintr = signal(SIGINT, mabort);
1195 	(void)setjmp(jabort);
1196 	for (i = 1; mflag && i < argc-1; ++i) {
1197 		*mode = (i == 1) ? 'w' : 'a';
1198 		recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode,
1199 		    0, 0);
1200 		if (!mflag && fromatty) {
1201 			ointer = interactive;
1202 			interactive = 1;
1203 			if (confirm("Continue with", argv[0])) {
1204 				mflag ++;
1205 			}
1206 			interactive = ointer;
1207 		}
1208 	}
1209 	(void)signal(SIGINT, oldintr);
1210 	mflag = 0;
1211 	if (dest != odest)			/* free up after globulize() */
1212 		free(dest);
1213 }
1214 
1215 /*
1216  * Do a shell escape
1217  */
1218 /*ARGSUSED*/
1219 void
1220 shell(argc, argv)
1221 	int argc;
1222 	char *argv[];
1223 {
1224 	pid_t pid;
1225 	sig_t old1, old2;
1226 	char shellnam[MAXPATHLEN], *shell, *namep;
1227 	int wait_status;
1228 
1229 	old1 = signal (SIGINT, SIG_IGN);
1230 	old2 = signal (SIGQUIT, SIG_IGN);
1231 	if ((pid = fork()) == 0) {
1232 		for (pid = 3; pid < 20; pid++)
1233 			(void)close(pid);
1234 		(void)signal(SIGINT, SIG_DFL);
1235 		(void)signal(SIGQUIT, SIG_DFL);
1236 		shell = getenv("SHELL");
1237 		if (shell == NULL)
1238 			shell = _PATH_BSHELL;
1239 		namep = strrchr(shell, '/');
1240 		if (namep == NULL)
1241 			namep = shell;
1242 		shellnam[0] = '-';
1243 		(void)strncpy(shellnam + 1, ++namep, sizeof(shellnam) - 2);
1244 		shellnam[sizeof(shellnam) - 1] = '\0';
1245 		if (strcmp(namep, "sh") != 0)
1246 			shellnam[0] = '+';
1247 		if (debug) {
1248 			puts(shell);
1249 			(void)fflush(stdout);
1250 		}
1251 		if (argc > 1) {
1252 			execl(shell, shellnam, "-c", altarg, (char *)0);
1253 		}
1254 		else {
1255 			execl(shell, shellnam, (char *)0);
1256 		}
1257 		warn("%s", shell);
1258 		code = -1;
1259 		exit(1);
1260 	}
1261 	if (pid > 0)
1262 		while (wait(&wait_status) != pid)
1263 			;
1264 	(void)signal(SIGINT, old1);
1265 	(void)signal(SIGQUIT, old2);
1266 	if (pid == -1) {
1267 		warn("Try again later");
1268 		code = -1;
1269 	}
1270 	else {
1271 		code = 0;
1272 	}
1273 }
1274 
1275 /*
1276  * Send new user information (re-login)
1277  */
1278 void
1279 user(argc, argv)
1280 	int argc;
1281 	char *argv[];
1282 {
1283 	char acct[80];
1284 	int n, aflag = 0;
1285 
1286 	if (argc < 2)
1287 		(void)another(&argc, &argv, "username");
1288 	if (argc < 2 || argc > 4) {
1289 		printf("usage: %s username [password] [account]\n", argv[0]);
1290 		code = -1;
1291 		return;
1292 	}
1293 	n = command("USER %s", argv[1]);
1294 	if (n == CONTINUE) {
1295 		if (argc < 3 )
1296 			argv[2] = getpass("Password: "), argc++;
1297 		n = command("PASS %s", argv[2]);
1298 	}
1299 	if (n == CONTINUE) {
1300 		if (argc < 4) {
1301 			(void)fputs("Account: ", stdout);
1302 			(void)fflush(stdout);
1303 			(void)fgets(acct, sizeof(acct) - 1, stdin);
1304 			acct[strlen(acct) - 1] = '\0';
1305 			argv[3] = acct; argc++;
1306 		}
1307 		n = command("ACCT %s", argv[3]);
1308 		aflag++;
1309 	}
1310 	if (n != COMPLETE) {
1311 		puts("Login failed.");
1312 		return;
1313 	}
1314 	if (!aflag && argc == 4) {
1315 		(void)command("ACCT %s", argv[3]);
1316 	}
1317 	connected = -1;
1318 }
1319 
1320 /*
1321  * Print working directory on remote machine.
1322  */
1323 /*VARARGS*/
1324 void
1325 pwd(argc, argv)
1326 	int argc;
1327 	char *argv[];
1328 {
1329 	int oldverbose = verbose;
1330 
1331 	/*
1332 	 * If we aren't verbose, this doesn't do anything!
1333 	 */
1334 	verbose = 1;
1335 	if (command("PWD") == ERROR && code == 500) {
1336 		puts("PWD command not recognized, trying XPWD.");
1337 		(void)command("XPWD");
1338 	}
1339 	verbose = oldverbose;
1340 }
1341 
1342 /*
1343  * Print working directory on local machine.
1344  */
1345 void
1346 lpwd(argc, argv)
1347 	int argc;
1348 	char *argv[];
1349 {
1350 	char buf[MAXPATHLEN];
1351 
1352 	if (getcwd(buf, sizeof(buf)) != NULL)
1353 		printf("Local directory %s\n", buf);
1354 	else
1355 		warn("getcwd");
1356 	code = 0;
1357 }
1358 
1359 /*
1360  * Make a directory.
1361  */
1362 void
1363 makedir(argc, argv)
1364 	int argc;
1365 	char *argv[];
1366 {
1367 
1368 	if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1369 	    argc > 2) {
1370 		printf("usage: %s directory-name\n", argv[0]);
1371 		code = -1;
1372 		return;
1373 	}
1374 	if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1375 		if (verbose)
1376 			puts("MKD command not recognized, trying XMKD.");
1377 		(void)command("XMKD %s", argv[1]);
1378 	}
1379 }
1380 
1381 /*
1382  * Remove a directory.
1383  */
1384 void
1385 removedir(argc, argv)
1386 	int argc;
1387 	char *argv[];
1388 {
1389 
1390 	if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1391 	    argc > 2) {
1392 		printf("usage: %s directory-name\n", argv[0]);
1393 		code = -1;
1394 		return;
1395 	}
1396 	if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1397 		if (verbose)
1398 			puts("RMD command not recognized, trying XRMD.");
1399 		(void)command("XRMD %s", argv[1]);
1400 	}
1401 }
1402 
1403 /*
1404  * Send a line, verbatim, to the remote machine.
1405  */
1406 void
1407 quote(argc, argv)
1408 	int argc;
1409 	char *argv[];
1410 {
1411 
1412 	if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1413 		printf("usage: %s line-to-send\n", argv[0]);
1414 		code = -1;
1415 		return;
1416 	}
1417 	quote1("", argc, argv);
1418 }
1419 
1420 /*
1421  * Send a SITE command to the remote machine.  The line
1422  * is sent verbatim to the remote machine, except that the
1423  * word "SITE" is added at the front.
1424  */
1425 void
1426 site(argc, argv)
1427 	int argc;
1428 	char *argv[];
1429 {
1430 
1431 	if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1432 		printf("usage: %s line-to-send\n", argv[0]);
1433 		code = -1;
1434 		return;
1435 	}
1436 	quote1("SITE ", argc, argv);
1437 }
1438 
1439 /*
1440  * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1441  * Send the result as a one-line command and get response.
1442  */
1443 void
1444 quote1(initial, argc, argv)
1445 	const char *initial;
1446 	int argc;
1447 	char *argv[];
1448 {
1449 	int i, len;
1450 	char buf[BUFSIZ];		/* must be >= sizeof(line) */
1451 
1452 	(void)strncpy(buf, initial, sizeof(buf) - 1);
1453 	buf[sizeof(buf) - 1] = '\0';
1454 	if (argc > 1) {
1455 		len = strlen(buf);
1456 		len += strlen(strncpy(&buf[len], argv[1],
1457 		    sizeof(buf) - len - 1));
1458 		for (i = 2; i < argc && len < sizeof(buf); i++) {
1459 			buf[len++] = ' ';
1460 			len += strlen(strncpy(&buf[len], argv[i],
1461 			    sizeof(buf) - len) - 1);
1462 		}
1463 	}
1464 	if (command(buf) == PRELIM) {
1465 		while (getreply(0) == PRELIM)
1466 			continue;
1467 	}
1468 }
1469 
1470 void
1471 do_chmod(argc, argv)
1472 	int argc;
1473 	char *argv[];
1474 {
1475 
1476 	if (argc < 2 && !another(&argc, &argv, "mode"))
1477 		goto usage;
1478 	if ((argc < 3 && !another(&argc, &argv, "file-name")) || argc > 3) {
1479 usage:
1480 		printf("usage: %s mode file-name\n", argv[0]);
1481 		code = -1;
1482 		return;
1483 	}
1484 	(void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1485 }
1486 
1487 void
1488 do_umask(argc, argv)
1489 	int argc;
1490 	char *argv[];
1491 {
1492 	int oldverbose = verbose;
1493 
1494 	verbose = 1;
1495 	(void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1496 	verbose = oldverbose;
1497 }
1498 
1499 void
1500 idle(argc, argv)
1501 	int argc;
1502 	char *argv[];
1503 {
1504 	int oldverbose = verbose;
1505 
1506 	verbose = 1;
1507 	(void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1508 	verbose = oldverbose;
1509 }
1510 
1511 /*
1512  * Ask the other side for help.
1513  */
1514 void
1515 rmthelp(argc, argv)
1516 	int argc;
1517 	char *argv[];
1518 {
1519 	int oldverbose = verbose;
1520 
1521 	verbose = 1;
1522 	(void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1523 	verbose = oldverbose;
1524 }
1525 
1526 /*
1527  * Terminate session and exit.
1528  */
1529 /*VARARGS*/
1530 void
1531 quit(argc, argv)
1532 	int argc;
1533 	char *argv[];
1534 {
1535 
1536 	if (connected)
1537 		disconnect(0, 0);
1538 	pswitch(1);
1539 	if (connected) {
1540 		disconnect(0, 0);
1541 	}
1542 	exit(0);
1543 }
1544 
1545 /*
1546  * Terminate session, but don't exit.
1547  */
1548 void
1549 disconnect(argc, argv)
1550 	int argc;
1551 	char *argv[];
1552 {
1553 
1554 	if (!connected)
1555 		return;
1556 	(void)command("QUIT");
1557 	if (cout) {
1558 		(void)fclose(cout);
1559 	}
1560 	cout = NULL;
1561 	connected = 0;
1562 	data = -1;
1563 	if (!proxy) {
1564 		macnum = 0;
1565 	}
1566 }
1567 
1568 void
1569 account(argc, argv)
1570 	int argc;
1571 	char *argv[];
1572 {
1573 	char *ap;
1574 
1575 	if (argc > 2) {
1576 		printf("usage: %s [password]\n", argv[0]);
1577 		code = -1;
1578 		return;
1579 	}
1580 	else if (argc == 2)
1581 		ap = argv[1];
1582 	else
1583 		ap = getpass("Account:");
1584 	(void)command("ACCT %s", ap);
1585 }
1586 
1587 jmp_buf abortprox;
1588 
1589 void
1590 proxabort(notused)
1591 	int notused;
1592 {
1593 
1594 	alarmtimer(0);
1595 	if (!proxy) {
1596 		pswitch(1);
1597 	}
1598 	if (connected) {
1599 		proxflag = 1;
1600 	}
1601 	else {
1602 		proxflag = 0;
1603 	}
1604 	pswitch(0);
1605 	longjmp(abortprox, 1);
1606 }
1607 
1608 void
1609 doproxy(argc, argv)
1610 	int argc;
1611 	char *argv[];
1612 {
1613 	struct cmd *c;
1614 	int cmdpos;
1615 	sig_t oldintr;
1616 
1617 	if (argc < 2 && !another(&argc, &argv, "command")) {
1618 		printf("usage: %s command\n", argv[0]);
1619 		code = -1;
1620 		return;
1621 	}
1622 	c = getcmd(argv[1]);
1623 	if (c == (struct cmd *) -1) {
1624 		puts("?Ambiguous command.");
1625 		(void)fflush(stdout);
1626 		code = -1;
1627 		return;
1628 	}
1629 	if (c == 0) {
1630 		puts("?Invalid command.");
1631 		(void)fflush(stdout);
1632 		code = -1;
1633 		return;
1634 	}
1635 	if (!c->c_proxy) {
1636 		puts("?Invalid proxy command.");
1637 		(void)fflush(stdout);
1638 		code = -1;
1639 		return;
1640 	}
1641 	if (setjmp(abortprox)) {
1642 		code = -1;
1643 		return;
1644 	}
1645 	oldintr = signal(SIGINT, proxabort);
1646 	pswitch(1);
1647 	if (c->c_conn && !connected) {
1648 		puts("Not connected.");
1649 		(void)fflush(stdout);
1650 		pswitch(0);
1651 		(void)signal(SIGINT, oldintr);
1652 		code = -1;
1653 		return;
1654 	}
1655 	cmdpos = strcspn(line, " \t");
1656 	if (cmdpos > 0)		/* remove leading "proxy " from input buffer */
1657 		memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1658 	(*c->c_handler)(argc-1, argv+1);
1659 	if (connected) {
1660 		proxflag = 1;
1661 	}
1662 	else {
1663 		proxflag = 0;
1664 	}
1665 	pswitch(0);
1666 	(void)signal(SIGINT, oldintr);
1667 }
1668 
1669 void
1670 setcase(argc, argv)
1671 	int argc;
1672 	char *argv[];
1673 {
1674 
1675 	code = togglevar(argc, argv, &mcase, "Case mapping");
1676 }
1677 
1678 void
1679 setcr(argc, argv)
1680 	int argc;
1681 	char *argv[];
1682 {
1683 
1684 	code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1685 }
1686 
1687 void
1688 setntrans(argc, argv)
1689 	int argc;
1690 	char *argv[];
1691 {
1692 	if (argc == 1) {
1693 		ntflag = 0;
1694 		puts("Ntrans off.");
1695 		code = ntflag;
1696 		return;
1697 	}
1698 	ntflag++;
1699 	code = ntflag;
1700 	(void)strncpy(ntin, argv[1], sizeof(ntin) - 1);
1701 	ntin[sizeof(ntin) - 1] = '\0';
1702 	if (argc == 2) {
1703 		ntout[0] = '\0';
1704 		return;
1705 	}
1706 	(void)strncpy(ntout, argv[2], sizeof(ntout) - 1);
1707 	ntout[sizeof(ntout) - 1] = '\0';
1708 }
1709 
1710 char *
1711 dotrans(name)
1712 	char *name;
1713 {
1714 	static char new[MAXPATHLEN];
1715 	char *cp1, *cp2 = new;
1716 	int i, ostop, found;
1717 
1718 	for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1719 		continue;
1720 	for (cp1 = name; *cp1; cp1++) {
1721 		found = 0;
1722 		for (i = 0; *(ntin + i) && i < 16; i++) {
1723 			if (*cp1 == *(ntin + i)) {
1724 				found++;
1725 				if (i < ostop) {
1726 					*cp2++ = *(ntout + i);
1727 				}
1728 				break;
1729 			}
1730 		}
1731 		if (!found) {
1732 			*cp2++ = *cp1;
1733 		}
1734 	}
1735 	*cp2 = '\0';
1736 	return (new);
1737 }
1738 
1739 void
1740 setnmap(argc, argv)
1741 	int argc;
1742 	char *argv[];
1743 {
1744 	char *cp;
1745 
1746 	if (argc == 1) {
1747 		mapflag = 0;
1748 		puts("Nmap off.");
1749 		code = mapflag;
1750 		return;
1751 	}
1752 	if ((argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1753 		printf("usage: %s [mapin mapout]\n", argv[0]);
1754 		code = -1;
1755 		return;
1756 	}
1757 	mapflag = 1;
1758 	code = 1;
1759 	cp = strchr(altarg, ' ');
1760 	if (proxy) {
1761 		while(*++cp == ' ')
1762 			continue;
1763 		altarg = cp;
1764 		cp = strchr(altarg, ' ');
1765 	}
1766 	*cp = '\0';
1767 	(void)strncpy(mapin, altarg, MAXPATHLEN - 1);
1768 	while (*++cp == ' ')
1769 		continue;
1770 	(void)strncpy(mapout, cp, MAXPATHLEN - 1);
1771 }
1772 
1773 char *
1774 domap(name)
1775 	char *name;
1776 {
1777 	static char new[MAXPATHLEN];
1778 	char *cp1 = name, *cp2 = mapin;
1779 	char *tp[9], *te[9];
1780 	int i, toks[9], toknum = 0, match = 1;
1781 
1782 	for (i=0; i < 9; ++i) {
1783 		toks[i] = 0;
1784 	}
1785 	while (match && *cp1 && *cp2) {
1786 		switch (*cp2) {
1787 			case '\\':
1788 				if (*++cp2 != *cp1) {
1789 					match = 0;
1790 				}
1791 				break;
1792 			case '$':
1793 				if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1794 					if (*cp1 != *(++cp2+1)) {
1795 						toks[toknum = *cp2 - '1']++;
1796 						tp[toknum] = cp1;
1797 						while (*++cp1 && *(cp2+1)
1798 							!= *cp1);
1799 						te[toknum] = cp1;
1800 					}
1801 					cp2++;
1802 					break;
1803 				}
1804 				/* FALLTHROUGH */
1805 			default:
1806 				if (*cp2 != *cp1) {
1807 					match = 0;
1808 				}
1809 				break;
1810 		}
1811 		if (match && *cp1) {
1812 			cp1++;
1813 		}
1814 		if (match && *cp2) {
1815 			cp2++;
1816 		}
1817 	}
1818 	if (!match && *cp1) /* last token mismatch */
1819 	{
1820 		toks[toknum] = 0;
1821 	}
1822 	cp1 = new;
1823 	*cp1 = '\0';
1824 	cp2 = mapout;
1825 	while (*cp2) {
1826 		match = 0;
1827 		switch (*cp2) {
1828 			case '\\':
1829 				if (*(cp2 + 1)) {
1830 					*cp1++ = *++cp2;
1831 				}
1832 				break;
1833 			case '[':
1834 LOOP:
1835 				if (*++cp2 == '$' && isdigit(*(cp2+1))) {
1836 					if (*++cp2 == '0') {
1837 						char *cp3 = name;
1838 
1839 						while (*cp3) {
1840 							*cp1++ = *cp3++;
1841 						}
1842 						match = 1;
1843 					}
1844 					else if (toks[toknum = *cp2 - '1']) {
1845 						char *cp3 = tp[toknum];
1846 
1847 						while (cp3 != te[toknum]) {
1848 							*cp1++ = *cp3++;
1849 						}
1850 						match = 1;
1851 					}
1852 				}
1853 				else {
1854 					while (*cp2 && *cp2 != ',' &&
1855 					    *cp2 != ']') {
1856 						if (*cp2 == '\\') {
1857 							cp2++;
1858 						}
1859 						else if (*cp2 == '$' &&
1860    						        isdigit(*(cp2+1))) {
1861 							if (*++cp2 == '0') {
1862 							   char *cp3 = name;
1863 
1864 							   while (*cp3) {
1865 								*cp1++ = *cp3++;
1866 							   }
1867 							}
1868 							else if (toks[toknum =
1869 							    *cp2 - '1']) {
1870 							   char *cp3=tp[toknum];
1871 
1872 							   while (cp3 !=
1873 								  te[toknum]) {
1874 								*cp1++ = *cp3++;
1875 							   }
1876 							}
1877 						}
1878 						else if (*cp2) {
1879 							*cp1++ = *cp2++;
1880 						}
1881 					}
1882 					if (!*cp2) {
1883 						puts(
1884 "nmap: unbalanced brackets.");
1885 						return (name);
1886 					}
1887 					match = 1;
1888 					cp2--;
1889 				}
1890 				if (match) {
1891 					while (*++cp2 && *cp2 != ']') {
1892 					      if (*cp2 == '\\' && *(cp2 + 1)) {
1893 							cp2++;
1894 					      }
1895 					}
1896 					if (!*cp2) {
1897 						puts(
1898 "nmap: unbalanced brackets.");
1899 						return (name);
1900 					}
1901 					break;
1902 				}
1903 				switch (*++cp2) {
1904 					case ',':
1905 						goto LOOP;
1906 					case ']':
1907 						break;
1908 					default:
1909 						cp2--;
1910 						goto LOOP;
1911 				}
1912 				break;
1913 			case '$':
1914 				if (isdigit(*(cp2 + 1))) {
1915 					if (*++cp2 == '0') {
1916 						char *cp3 = name;
1917 
1918 						while (*cp3) {
1919 							*cp1++ = *cp3++;
1920 						}
1921 					}
1922 					else if (toks[toknum = *cp2 - '1']) {
1923 						char *cp3 = tp[toknum];
1924 
1925 						while (cp3 != te[toknum]) {
1926 							*cp1++ = *cp3++;
1927 						}
1928 					}
1929 					break;
1930 				}
1931 				/* intentional drop through */
1932 			default:
1933 				*cp1++ = *cp2;
1934 				break;
1935 		}
1936 		cp2++;
1937 	}
1938 	*cp1 = '\0';
1939 	if (!*new) {
1940 		return (name);
1941 	}
1942 	return (new);
1943 }
1944 
1945 void
1946 setpassive(argc, argv)
1947 	int argc;
1948 	char *argv[];
1949 {
1950 
1951 	code = togglevar(argc, argv, &passivemode,
1952 	    verbose ? "Passive mode" : NULL);
1953 }
1954 
1955 void
1956 setsunique(argc, argv)
1957 	int argc;
1958 	char *argv[];
1959 {
1960 
1961 	code = togglevar(argc, argv, &sunique, "Store unique");
1962 }
1963 
1964 void
1965 setrunique(argc, argv)
1966 	int argc;
1967 	char *argv[];
1968 {
1969 
1970 	code = togglevar(argc, argv, &runique, "Receive unique");
1971 }
1972 
1973 /* change directory to parent directory */
1974 void
1975 cdup(argc, argv)
1976 	int argc;
1977 	char *argv[];
1978 {
1979 	int r;
1980 
1981 	r = command("CDUP");
1982 	if (r == ERROR && code == 500) {
1983 		if (verbose)
1984 			puts("CDUP command not recognized, trying XCUP.");
1985 		r = command("XCUP");
1986 	}
1987 	if (r == COMPLETE)
1988 		dirchange = 1;
1989 }
1990 
1991 /*
1992  * Restart transfer at specific point
1993  */
1994 void
1995 restart(argc, argv)
1996 	int argc;
1997 	char *argv[];
1998 {
1999 
2000 	if (argc > 2) {
2001 		printf("usage: %s [restart_point]\n", argv[0]);
2002 		code = -1;
2003 		return;
2004 	}
2005 	if (argc == 2) {
2006 		quad_t	rp;
2007 		char *ep;
2008 
2009 		rp = strtoq(argv[1], &ep, 10);
2010 		if (rp < 0 || *ep != '\0')
2011 			printf("restart: Invalid offset `%s'\n", argv[1]);
2012 		else
2013 			restart_point = rp;
2014 	}
2015 	if (restart_point == 0)
2016 		puts("No restart point defined");
2017 	else
2018 		printf("Restarting at %qd for next get, put or append\n",
2019 		    (long long)restart_point);
2020 }
2021 
2022 /*
2023  * Show remote system type
2024  */
2025 void
2026 syst(argc, argv)
2027 	int argc;
2028 	char *argv[];
2029 {
2030 
2031 	(void)command("SYST");
2032 }
2033 
2034 void
2035 macdef(argc, argv)
2036 	int argc;
2037 	char *argv[];
2038 {
2039 	char *tmp;
2040 	int c;
2041 
2042 	if (macnum == 16) {
2043 		puts("Limit of 16 macros have already been defined.");
2044 		code = -1;
2045 		return;
2046 	}
2047 	if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2048 		printf("usage: %s macro_name\n", argv[0]);
2049 		code = -1;
2050 		return;
2051 	}
2052 	if (interactive)
2053 		puts(
2054 "Enter macro line by line, terminating it with a null line.");
2055 	(void)strncpy(macros[macnum].mac_name, argv[1],
2056 	    sizeof(macros[macnum].mac_name) - 1);
2057 	macros[macnum].mac_name[sizeof(macros[macnum].mac_name) - 1] = '\0';
2058 	if (macnum == 0)
2059 		macros[macnum].mac_start = macbuf;
2060 	else
2061 		macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2062 	tmp = macros[macnum].mac_start;
2063 	while (tmp != macbuf+4096) {
2064 		if ((c = getchar()) == EOF) {
2065 			puts("macdef: end of file encountered.");
2066 			code = -1;
2067 			return;
2068 		}
2069 		if ((*tmp = c) == '\n') {
2070 			if (tmp == macros[macnum].mac_start) {
2071 				macros[macnum++].mac_end = tmp;
2072 				code = 0;
2073 				return;
2074 			}
2075 			if (*(tmp-1) == '\0') {
2076 				macros[macnum++].mac_end = tmp - 1;
2077 				code = 0;
2078 				return;
2079 			}
2080 			*tmp = '\0';
2081 		}
2082 		tmp++;
2083 	}
2084 	while (1) {
2085 		while ((c = getchar()) != '\n' && c != EOF)
2086 			/* LOOP */;
2087 		if (c == EOF || getchar() == '\n') {
2088 			puts("Macro not defined - 4K buffer exceeded.");
2089 			code = -1;
2090 			return;
2091 		}
2092 	}
2093 }
2094 
2095 /*
2096  * Get size of file on remote machine
2097  */
2098 void
2099 sizecmd(argc, argv)
2100 	int argc;
2101 	char *argv[];
2102 {
2103 	off_t size;
2104 
2105 	if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2106 		printf("usage: %s filename\n", argv[0]);
2107 		code = -1;
2108 		return;
2109 	}
2110 	size = remotesize(argv[1], 1);
2111 	if (size != -1)
2112 		printf("%s\t%qd\n", argv[1], (long long)size);
2113 	code = size;
2114 }
2115 
2116 /*
2117  * Get last modification time of file on remote machine
2118  */
2119 void
2120 modtime(argc, argv)
2121 	int argc;
2122 	char *argv[];
2123 {
2124 	time_t mtime;
2125 
2126 	if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2127 		printf("usage: %s filename\n", argv[0]);
2128 		code = -1;
2129 		return;
2130 	}
2131 	mtime = remotemodtime(argv[1], 1);
2132 	if (mtime != -1)
2133 		printf("%s\t%s", argv[1], asctime(localtime(&mtime)));
2134 	code = mtime;
2135 }
2136 
2137 /*
2138  * Show status on remote machine
2139  */
2140 void
2141 rmtstatus(argc, argv)
2142 	int argc;
2143 	char *argv[];
2144 {
2145 
2146 	(void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
2147 }
2148 
2149 /*
2150  * Get file if modtime is more recent than current file
2151  */
2152 void
2153 newer(argc, argv)
2154 	int argc;
2155 	char *argv[];
2156 {
2157 
2158 	if (getit(argc, argv, -1, "w"))
2159 		printf("Local file \"%s\" is newer than remote file \"%s\".\n",
2160 			argv[2], argv[1]);
2161 }
2162 
2163 /*
2164  * Display one file through $PAGER (defaults to "more").
2165  */
2166 void
2167 page(argc, argv)
2168 	int argc;
2169 	char *argv[];
2170 {
2171 	int ohash, overbose;
2172 	char *p, *pager, *oldargv1;
2173 
2174 	if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2175 		printf("usage: %s filename\n", argv[0]);
2176 		code = -1;
2177 		return;
2178 	}
2179 	oldargv1 = argv[1];
2180 	if (!globulize(&argv[1])) {
2181 		code = -1;
2182 		return;
2183 	}
2184 	p = getenv("PAGER");
2185 	if (p == NULL)
2186 		p = PAGER;
2187 	if ((pager = malloc(strlen(p) + 2)) == NULL)
2188 		errx(1, "Can't allocate memory for $PAGER");
2189 	(void)sprintf(pager, "|%s", p);
2190 
2191 	ohash = hash;
2192 	overbose = verbose;
2193 	hash = verbose = 0;
2194 	recvrequest("RETR", pager, argv[1], "r+w", 1, 0);
2195 	(void)free(pager);
2196 	hash = ohash;
2197 	verbose = overbose;
2198 	if (oldargv1 != argv[1])	/* free up after globulize() */
2199 		free(argv[1]);
2200 }
2201