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