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