xref: /csrg-svn/usr.bin/ftp/cmds.c (revision 66670)
1 /*
2  * Copyright (c) 1985, 1989, 1993
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.2 (Berkeley) 04/02/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 <netdb.h>
25 #include <signal.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31 
32 #include "ftp_var.h"
33 #include "pathnames.h"
34 
35 jmp_buf	jabort;
36 char   *mname;
37 
38 /*
39  * `Another' gets another argument, and stores the new argc and argv.
40  * It reverts to the top level (via main.c's intr()) on EOF/error.
41  *
42  * Returns false if no new arguments have been added.
43  */
44 int
45 another(pargc, pargv, prompt)
46 	int *pargc;
47 	char ***pargv;
48 	char *prompt;
49 {
50 	int len = strlen(line), ret;
51 
52 	if (len >= sizeof(line) - 3) {
53 		printf("sorry, arguments too long\n");
54 		intr();
55 	}
56 	printf("(%s) ", prompt);
57 	line[len++] = ' ';
58 	if (fgets(&line[len], sizeof(line) - len, stdin) == NULL)
59 		intr();
60 	len += strlen(&line[len]);
61 	if (len > 0 && line[len - 1] == '\n')
62 		line[len - 1] = '\0';
63 	makeargv();
64 	ret = margc > *pargc;
65 	*pargc = margc;
66 	*pargv = margv;
67 	return (ret);
68 }
69 
70 /*
71  * Connect to peer server and
72  * auto-login, if possible.
73  */
74 void
75 setpeer(argc, argv)
76 	int argc;
77 	char *argv[];
78 {
79 	char *host;
80 	short port;
81 
82 	if (connected) {
83 		printf("Already connected to %s, use close first.\n",
84 			hostname);
85 		code = -1;
86 		return;
87 	}
88 	if (argc < 2)
89 		(void) another(&argc, &argv, "to");
90 	if (argc < 2 || argc > 3) {
91 		printf("usage: %s host-name [port]\n", argv[0]);
92 		code = -1;
93 		return;
94 	}
95 	port = sp->s_port;
96 	if (argc > 2) {
97 		port = atoi(argv[2]);
98 		if (port <= 0) {
99 			printf("%s: bad port number-- %s\n", argv[1], argv[2]);
100 			printf ("usage: %s host-name [port]\n", argv[0]);
101 			code = -1;
102 			return;
103 		}
104 		port = htons(port);
105 	}
106 	host = hookup(argv[1], port);
107 	if (host) {
108 		int overbose;
109 
110 		connected = 1;
111 		/*
112 		 * Set up defaults for FTP.
113 		 */
114 		(void) strcpy(typename, "ascii"), type = TYPE_A;
115 		curtype = TYPE_A;
116 		(void) strcpy(formname, "non-print"), form = FORM_N;
117 		(void) strcpy(modename, "stream"), mode = MODE_S;
118 		(void) strcpy(structname, "file"), stru = STRU_F;
119 		(void) strcpy(bytename, "8"), bytesize = 8;
120 		if (autologin)
121 			(void) login(argv[1]);
122 
123 #if defined(unix) && NBBY == 8
124 /*
125  * this ifdef is to keep someone form "porting" this to an incompatible
126  * system and not checking this out. This way they have to think about it.
127  */
128 		overbose = verbose;
129 		if (debug == 0)
130 			verbose = -1;
131 		if (command("SYST") == COMPLETE && overbose) {
132 			char *cp, c;
133 			cp = strchr(reply_string+4, ' ');
134 			if (cp == NULL)
135 				cp = strchr(reply_string+4, '\r');
136 			if (cp) {
137 				if (cp[-1] == '.')
138 					cp--;
139 				c = *cp;
140 				*cp = '\0';
141 			}
142 
143 			printf("Remote system type is %s.\n",
144 				reply_string+4);
145 			if (cp)
146 				*cp = c;
147 		}
148 		if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
149 			if (proxy)
150 				unix_proxy = 1;
151 			else
152 				unix_server = 1;
153 			/*
154 			 * Set type to 0 (not specified by user),
155 			 * meaning binary by default, but don't bother
156 			 * telling server.  We can use binary
157 			 * for text files unless changed by the user.
158 			 */
159 			type = 0;
160 			(void) strcpy(typename, "binary");
161 			if (overbose)
162 			    printf("Using %s mode to transfer files.\n",
163 				typename);
164 		} else {
165 			if (proxy)
166 				unix_proxy = 0;
167 			else
168 				unix_server = 0;
169 			if (overbose &&
170 			    !strncmp(reply_string, "215 TOPS20", 10))
171 				printf(
172 "Remember to set tenex mode when transfering binary files from this machine.\n");
173 		}
174 		verbose = overbose;
175 #endif /* unix */
176 	}
177 }
178 
179 struct	types {
180 	char	*t_name;
181 	char	*t_mode;
182 	int	t_type;
183 	char	*t_arg;
184 } types[] = {
185 	{ "ascii",	"A",	TYPE_A,	0 },
186 	{ "binary",	"I",	TYPE_I,	0 },
187 	{ "image",	"I",	TYPE_I,	0 },
188 	{ "ebcdic",	"E",	TYPE_E,	0 },
189 	{ "tenex",	"L",	TYPE_L,	bytename },
190 	{ NULL }
191 };
192 
193 /*
194  * Set transfer type.
195  */
196 void
197 settype(argc, argv)
198 	int argc;
199 	char *argv[];
200 {
201 	struct types *p;
202 	int comret;
203 
204 	if (argc > 2) {
205 		char *sep;
206 
207 		printf("usage: %s [", argv[0]);
208 		sep = " ";
209 		for (p = types; p->t_name; p++) {
210 			printf("%s%s", sep, p->t_name);
211 			sep = " | ";
212 		}
213 		printf(" ]\n");
214 		code = -1;
215 		return;
216 	}
217 	if (argc < 2) {
218 		printf("Using %s mode to transfer files.\n", typename);
219 		code = 0;
220 		return;
221 	}
222 	for (p = types; p->t_name; p++)
223 		if (strcmp(argv[1], p->t_name) == 0)
224 			break;
225 	if (p->t_name == 0) {
226 		printf("%s: unknown mode\n", argv[1]);
227 		code = -1;
228 		return;
229 	}
230 	if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
231 		comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
232 	else
233 		comret = command("TYPE %s", p->t_mode);
234 	if (comret == COMPLETE) {
235 		(void) strcpy(typename, p->t_name);
236 		curtype = type = p->t_type;
237 	}
238 }
239 
240 /*
241  * Internal form of settype; changes current type in use with server
242  * without changing our notion of the type for data transfers.
243  * Used to change to and from ascii for listings.
244  */
245 void
246 changetype(newtype, show)
247 	int newtype, show;
248 {
249 	struct types *p;
250 	int comret, oldverbose = verbose;
251 
252 	if (newtype == 0)
253 		newtype = TYPE_I;
254 	if (newtype == curtype)
255 		return;
256 	if (debug == 0 && show == 0)
257 		verbose = 0;
258 	for (p = types; p->t_name; p++)
259 		if (newtype == p->t_type)
260 			break;
261 	if (p->t_name == 0) {
262 		printf("ftp: internal error: unknown type %d\n", newtype);
263 		return;
264 	}
265 	if (newtype == TYPE_L && bytename[0] != '\0')
266 		comret = command("TYPE %s %s", p->t_mode, bytename);
267 	else
268 		comret = command("TYPE %s", p->t_mode);
269 	if (comret == COMPLETE)
270 		curtype = newtype;
271 	verbose = oldverbose;
272 }
273 
274 char *stype[] = {
275 	"type",
276 	"",
277 	0
278 };
279 
280 /*
281  * Set binary transfer type.
282  */
283 /*VARARGS*/
284 void
285 setbinary(argc, argv)
286 	int argc;
287 	char **argv;
288 {
289 
290 	stype[1] = "binary";
291 	settype(2, stype);
292 }
293 
294 /*
295  * Set ascii transfer type.
296  */
297 /*VARARGS*/
298 void
299 setascii(argc, argv)
300 	int argc;
301 	char *argv[];
302 {
303 
304 	stype[1] = "ascii";
305 	settype(2, stype);
306 }
307 
308 /*
309  * Set tenex transfer type.
310  */
311 /*VARARGS*/
312 void
313 settenex(argc, argv)
314 	int argc;
315 	char *argv[];
316 {
317 
318 	stype[1] = "tenex";
319 	settype(2, stype);
320 }
321 
322 /*
323  * Set file transfer mode.
324  */
325 /*ARGSUSED*/
326 void
327 setftmode(argc, argv)
328 	int argc;
329 	char *argv[];
330 {
331 
332 	printf("We only support %s mode, sorry.\n", modename);
333 	code = -1;
334 }
335 
336 /*
337  * Set file transfer format.
338  */
339 /*ARGSUSED*/
340 void
341 setform(argc, argv)
342 	int argc;
343 	char *argv[];
344 {
345 
346 	printf("We only support %s format, sorry.\n", formname);
347 	code = -1;
348 }
349 
350 /*
351  * Set file transfer structure.
352  */
353 /*ARGSUSED*/
354 void
355 setstruct(argc, argv)
356 	int argc;
357 	char *argv[];
358 {
359 
360 	printf("We only support %s structure, sorry.\n", structname);
361 	code = -1;
362 }
363 
364 /*
365  * Send a single file.
366  */
367 void
368 put(argc, argv)
369 	int argc;
370 	char *argv[];
371 {
372 	char *cmd;
373 	int loc = 0;
374 	char *oldargv1, *oldargv2;
375 
376 	if (argc == 2) {
377 		argc++;
378 		argv[2] = argv[1];
379 		loc++;
380 	}
381 	if (argc < 2 && !another(&argc, &argv, "local-file"))
382 		goto usage;
383 	if (argc < 3 && !another(&argc, &argv, "remote-file")) {
384 usage:
385 		printf("usage: %s local-file remote-file\n", argv[0]);
386 		code = -1;
387 		return;
388 	}
389 	oldargv1 = argv[1];
390 	oldargv2 = argv[2];
391 	if (!globulize(&argv[1])) {
392 		code = -1;
393 		return;
394 	}
395 	/*
396 	 * If "globulize" modifies argv[1], and argv[2] is a copy of
397 	 * the old argv[1], make it a copy of the new argv[1].
398 	 */
399 	if (argv[1] != oldargv1 && argv[2] == oldargv1) {
400 		argv[2] = argv[1];
401 	}
402 	cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
403 	if (loc && ntflag) {
404 		argv[2] = dotrans(argv[2]);
405 	}
406 	if (loc && mapflag) {
407 		argv[2] = domap(argv[2]);
408 	}
409 	sendrequest(cmd, argv[1], argv[2],
410 	    argv[1] != oldargv1 || argv[2] != oldargv2);
411 }
412 
413 /*
414  * Send multiple files.
415  */
416 void
417 mput(argc, argv)
418 	int argc;
419 	char **argv;
420 {
421 	int i;
422 	sig_t oldintr;
423 	int ointer;
424 	char *tp;
425 
426 	if (argc < 2 && !another(&argc, &argv, "local-files")) {
427 		printf("usage: %s local-files\n", argv[0]);
428 		code = -1;
429 		return;
430 	}
431 	mname = argv[0];
432 	mflag = 1;
433 	oldintr = signal(SIGINT, mabort);
434 	(void) setjmp(jabort);
435 	if (proxy) {
436 		char *cp, *tp2, tmpbuf[MAXPATHLEN];
437 
438 		while ((cp = remglob(argv,0)) != NULL) {
439 			if (*cp == 0) {
440 				mflag = 0;
441 				continue;
442 			}
443 			if (mflag && confirm(argv[0], cp)) {
444 				tp = cp;
445 				if (mcase) {
446 					while (*tp && !islower(*tp)) {
447 						tp++;
448 					}
449 					if (!*tp) {
450 						tp = cp;
451 						tp2 = tmpbuf;
452 						while ((*tp2 = *tp) != NULL) {
453 						     if (isupper(*tp2)) {
454 						        *tp2 = 'a' + *tp2 - 'A';
455 						     }
456 						     tp++;
457 						     tp2++;
458 						}
459 					}
460 					tp = tmpbuf;
461 				}
462 				if (ntflag) {
463 					tp = dotrans(tp);
464 				}
465 				if (mapflag) {
466 					tp = domap(tp);
467 				}
468 				sendrequest((sunique) ? "STOU" : "STOR",
469 				    cp, tp, cp != tp || !interactive);
470 				if (!mflag && fromatty) {
471 					ointer = interactive;
472 					interactive = 1;
473 					if (confirm("Continue with","mput")) {
474 						mflag++;
475 					}
476 					interactive = ointer;
477 				}
478 			}
479 		}
480 		(void) signal(SIGINT, oldintr);
481 		mflag = 0;
482 		return;
483 	}
484 	for (i = 1; i < argc; i++) {
485 		char **cpp, **gargs;
486 
487 		if (!doglob) {
488 			if (mflag && confirm(argv[0], argv[i])) {
489 				tp = (ntflag) ? dotrans(argv[i]) : argv[i];
490 				tp = (mapflag) ? domap(tp) : tp;
491 				sendrequest((sunique) ? "STOU" : "STOR",
492 				    argv[i], tp, tp != argv[i] || !interactive);
493 				if (!mflag && fromatty) {
494 					ointer = interactive;
495 					interactive = 1;
496 					if (confirm("Continue with","mput")) {
497 						mflag++;
498 					}
499 					interactive = ointer;
500 				}
501 			}
502 			continue;
503 		}
504 		gargs = ftpglob(argv[i]);
505 		if (globerr != NULL) {
506 			printf("%s\n", globerr);
507 			if (gargs) {
508 				blkfree(gargs);
509 				free((char *)gargs);
510 			}
511 			continue;
512 		}
513 		for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
514 			if (mflag && confirm(argv[0], *cpp)) {
515 				tp = (ntflag) ? dotrans(*cpp) : *cpp;
516 				tp = (mapflag) ? domap(tp) : tp;
517 				sendrequest((sunique) ? "STOU" : "STOR",
518 				    *cpp, tp, *cpp != tp || !interactive);
519 				if (!mflag && fromatty) {
520 					ointer = interactive;
521 					interactive = 1;
522 					if (confirm("Continue with","mput")) {
523 						mflag++;
524 					}
525 					interactive = ointer;
526 				}
527 			}
528 		}
529 		if (gargs != NULL) {
530 			blkfree(gargs);
531 			free((char *)gargs);
532 		}
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 	printf("Local directory now %s\n", getwd(buf));
1058 	code = 0;
1059 }
1060 
1061 /*
1062  * Delete a single file.
1063  */
1064 void
1065 delete(argc, argv)
1066 	int argc;
1067 	char *argv[];
1068 {
1069 
1070 	if (argc < 2 && !another(&argc, &argv, "remote-file")) {
1071 		printf("usage: %s remote-file\n", argv[0]);
1072 		code = -1;
1073 		return;
1074 	}
1075 	(void) command("DELE %s", argv[1]);
1076 }
1077 
1078 /*
1079  * Delete multiple files.
1080  */
1081 void
1082 mdelete(argc, argv)
1083 	int argc;
1084 	char **argv;
1085 {
1086 	sig_t oldintr;
1087 	int ointer;
1088 	char *cp;
1089 
1090 	if (argc < 2 && !another(&argc, &argv, "remote-files")) {
1091 		printf("usage: %s remote-files\n", argv[0]);
1092 		code = -1;
1093 		return;
1094 	}
1095 	mname = argv[0];
1096 	mflag = 1;
1097 	oldintr = signal(SIGINT, mabort);
1098 	(void) setjmp(jabort);
1099 	while ((cp = remglob(argv,0)) != NULL) {
1100 		if (*cp == '\0') {
1101 			mflag = 0;
1102 			continue;
1103 		}
1104 		if (mflag && confirm(argv[0], cp)) {
1105 			(void) command("DELE %s", cp);
1106 			if (!mflag && fromatty) {
1107 				ointer = interactive;
1108 				interactive = 1;
1109 				if (confirm("Continue with", "mdelete")) {
1110 					mflag++;
1111 				}
1112 				interactive = ointer;
1113 			}
1114 		}
1115 	}
1116 	(void) signal(SIGINT, oldintr);
1117 	mflag = 0;
1118 }
1119 
1120 /*
1121  * Rename a remote file.
1122  */
1123 void
1124 renamefile(argc, argv)
1125 	int argc;
1126 	char *argv[];
1127 {
1128 
1129 	if (argc < 2 && !another(&argc, &argv, "from-name"))
1130 		goto usage;
1131 	if (argc < 3 && !another(&argc, &argv, "to-name")) {
1132 usage:
1133 		printf("%s from-name to-name\n", argv[0]);
1134 		code = -1;
1135 		return;
1136 	}
1137 	if (command("RNFR %s", argv[1]) == CONTINUE)
1138 		(void) command("RNTO %s", argv[2]);
1139 }
1140 
1141 /*
1142  * Get a directory listing
1143  * of remote files.
1144  */
1145 void
1146 ls(argc, argv)
1147 	int argc;
1148 	char *argv[];
1149 {
1150 	char *cmd;
1151 
1152 	if (argc < 2)
1153 		argc++, argv[1] = NULL;
1154 	if (argc < 3)
1155 		argc++, argv[2] = "-";
1156 	if (argc > 3) {
1157 		printf("usage: %s remote-directory local-file\n", argv[0]);
1158 		code = -1;
1159 		return;
1160 	}
1161 	cmd = argv[0][0] == 'n' ? "NLST" : "LIST";
1162 	if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
1163 		code = -1;
1164 		return;
1165 	}
1166 	if (strcmp(argv[2], "-") && *argv[2] != '|')
1167 		if (!globulize(&argv[2]) || !confirm("output to local-file:", argv[2])) {
1168 			code = -1;
1169 			return;
1170 	}
1171 	recvrequest(cmd, argv[2], argv[1], "w", 0);
1172 }
1173 
1174 /*
1175  * Get a directory listing
1176  * of multiple remote files.
1177  */
1178 void
1179 mls(argc, argv)
1180 	int argc;
1181 	char **argv;
1182 {
1183 	sig_t oldintr;
1184 	int ointer, i;
1185 	char *cmd, mode[1], *dest;
1186 
1187 	if (argc < 2 && !another(&argc, &argv, "remote-files"))
1188 		goto usage;
1189 	if (argc < 3 && !another(&argc, &argv, "local-file")) {
1190 usage:
1191 		printf("usage: %s remote-files local-file\n", argv[0]);
1192 		code = -1;
1193 		return;
1194 	}
1195 	dest = argv[argc - 1];
1196 	argv[argc - 1] = NULL;
1197 	if (strcmp(dest, "-") && *dest != '|')
1198 		if (!globulize(&dest) ||
1199 		    !confirm("output to local-file:", dest)) {
1200 			code = -1;
1201 			return;
1202 	}
1203 	cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
1204 	mname = argv[0];
1205 	mflag = 1;
1206 	oldintr = signal(SIGINT, mabort);
1207 	(void) setjmp(jabort);
1208 	for (i = 1; mflag && i < argc-1; ++i) {
1209 		*mode = (i == 1) ? 'w' : 'a';
1210 		recvrequest(cmd, dest, argv[i], mode, 0);
1211 		if (!mflag && fromatty) {
1212 			ointer = interactive;
1213 			interactive = 1;
1214 			if (confirm("Continue with", argv[0])) {
1215 				mflag ++;
1216 			}
1217 			interactive = ointer;
1218 		}
1219 	}
1220 	(void) signal(SIGINT, oldintr);
1221 	mflag = 0;
1222 }
1223 
1224 /*
1225  * Do a shell escape
1226  */
1227 /*ARGSUSED*/
1228 void
1229 shell(argc, argv)
1230 	int argc;
1231 	char **argv;
1232 {
1233 	pid_t pid;
1234 	sig_t old1, old2;
1235 	char shellnam[40], *shell, *namep;
1236 	union wait status;
1237 
1238 	old1 = signal (SIGINT, SIG_IGN);
1239 	old2 = signal (SIGQUIT, SIG_IGN);
1240 	if ((pid = fork()) == 0) {
1241 		for (pid = 3; pid < 20; pid++)
1242 			(void) close(pid);
1243 		(void) signal(SIGINT, SIG_DFL);
1244 		(void) signal(SIGQUIT, SIG_DFL);
1245 		shell = getenv("SHELL");
1246 		if (shell == NULL)
1247 			shell = _PATH_BSHELL;
1248 		namep = strrchr(shell,'/');
1249 		if (namep == NULL)
1250 			namep = shell;
1251 		(void) strcpy(shellnam,"-");
1252 		(void) strcat(shellnam, ++namep);
1253 		if (strcmp(namep, "sh") != 0)
1254 			shellnam[0] = '+';
1255 		if (debug) {
1256 			printf ("%s\n", shell);
1257 			(void) fflush (stdout);
1258 		}
1259 		if (argc > 1) {
1260 			execl(shell,shellnam,"-c",altarg,(char *)0);
1261 		}
1262 		else {
1263 			execl(shell,shellnam,(char *)0);
1264 		}
1265 		warn("%s", shell);
1266 		code = -1;
1267 		exit(1);
1268 	}
1269 	if (pid > 0)
1270 		while (wait((int *)&status) != pid)
1271 			;
1272 	(void) signal(SIGINT, old1);
1273 	(void) signal(SIGQUIT, old2);
1274 	if (pid == -1) {
1275 		warn("%s", "Try again later");
1276 		code = -1;
1277 	}
1278 	else {
1279 		code = 0;
1280 	}
1281 }
1282 
1283 /*
1284  * Send new user information (re-login)
1285  */
1286 void
1287 user(argc, argv)
1288 	int argc;
1289 	char **argv;
1290 {
1291 	char acct[80];
1292 	int n, aflag = 0;
1293 
1294 	if (argc < 2)
1295 		(void) another(&argc, &argv, "username");
1296 	if (argc < 2 || argc > 4) {
1297 		printf("usage: %s username [password] [account]\n", argv[0]);
1298 		code = -1;
1299 		return;
1300 	}
1301 	n = command("USER %s", argv[1]);
1302 	if (n == CONTINUE) {
1303 		if (argc < 3 )
1304 			argv[2] = getpass("Password: "), argc++;
1305 		n = command("PASS %s", argv[2]);
1306 	}
1307 	if (n == CONTINUE) {
1308 		if (argc < 4) {
1309 			printf("Account: "); (void) fflush(stdout);
1310 			(void) fgets(acct, sizeof(acct) - 1, stdin);
1311 			acct[strlen(acct) - 1] = '\0';
1312 			argv[3] = acct; argc++;
1313 		}
1314 		n = command("ACCT %s", argv[3]);
1315 		aflag++;
1316 	}
1317 	if (n != COMPLETE) {
1318 		fprintf(stdout, "Login failed.\n");
1319 		return;
1320 	}
1321 	if (!aflag && argc == 4) {
1322 		(void) command("ACCT %s", argv[3]);
1323 	}
1324 }
1325 
1326 /*
1327  * Print working directory.
1328  */
1329 /*VARARGS*/
1330 void
1331 pwd(argc, argv)
1332 	int argc;
1333 	char *argv[];
1334 {
1335 	int oldverbose = verbose;
1336 
1337 	/*
1338 	 * If we aren't verbose, this doesn't do anything!
1339 	 */
1340 	verbose = 1;
1341 	if (command("PWD") == ERROR && code == 500) {
1342 		printf("PWD command not recognized, trying XPWD\n");
1343 		(void) command("XPWD");
1344 	}
1345 	verbose = oldverbose;
1346 }
1347 
1348 /*
1349  * Make a directory.
1350  */
1351 void
1352 makedir(argc, argv)
1353 	int argc;
1354 	char *argv[];
1355 {
1356 
1357 	if (argc < 2 && !another(&argc, &argv, "directory-name")) {
1358 		printf("usage: %s directory-name\n", argv[0]);
1359 		code = -1;
1360 		return;
1361 	}
1362 	if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1363 		if (verbose)
1364 			printf("MKD command not recognized, trying XMKD\n");
1365 		(void) command("XMKD %s", argv[1]);
1366 	}
1367 }
1368 
1369 /*
1370  * Remove a directory.
1371  */
1372 void
1373 removedir(argc, argv)
1374 	int argc;
1375 	char *argv[];
1376 {
1377 
1378 	if (argc < 2 && !another(&argc, &argv, "directory-name")) {
1379 		printf("usage: %s directory-name\n", argv[0]);
1380 		code = -1;
1381 		return;
1382 	}
1383 	if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1384 		if (verbose)
1385 			printf("RMD command not recognized, trying XRMD\n");
1386 		(void) command("XRMD %s", argv[1]);
1387 	}
1388 }
1389 
1390 /*
1391  * Send a line, verbatim, to the remote machine.
1392  */
1393 void
1394 quote(argc, argv)
1395 	int argc;
1396 	char *argv[];
1397 {
1398 
1399 	if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1400 		printf("usage: %s line-to-send\n", argv[0]);
1401 		code = -1;
1402 		return;
1403 	}
1404 	quote1("", argc, argv);
1405 }
1406 
1407 /*
1408  * Send a SITE command to the remote machine.  The line
1409  * is sent verbatim to the remote machine, except that the
1410  * word "SITE" is added at the front.
1411  */
1412 void
1413 site(argc, argv)
1414 	int argc;
1415 	char *argv[];
1416 {
1417 
1418 	if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1419 		printf("usage: %s line-to-send\n", argv[0]);
1420 		code = -1;
1421 		return;
1422 	}
1423 	quote1("SITE ", argc, argv);
1424 }
1425 
1426 /*
1427  * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1428  * Send the result as a one-line command and get response.
1429  */
1430 void
1431 quote1(initial, argc, argv)
1432 	char *initial;
1433 	int argc;
1434 	char **argv;
1435 {
1436 	int i, len;
1437 	char buf[BUFSIZ];		/* must be >= sizeof(line) */
1438 
1439 	(void) strcpy(buf, initial);
1440 	if (argc > 1) {
1441 		len = strlen(buf);
1442 		len += strlen(strcpy(&buf[len], argv[1]));
1443 		for (i = 2; i < argc; i++) {
1444 			buf[len++] = ' ';
1445 			len += strlen(strcpy(&buf[len], argv[i]));
1446 		}
1447 	}
1448 	if (command(buf) == PRELIM) {
1449 		while (getreply(0) == PRELIM)
1450 			continue;
1451 	}
1452 }
1453 
1454 void
1455 do_chmod(argc, argv)
1456 	int argc;
1457 	char *argv[];
1458 {
1459 
1460 	if (argc < 2 && !another(&argc, &argv, "mode"))
1461 		goto usage;
1462 	if (argc < 3 && !another(&argc, &argv, "file-name")) {
1463 usage:
1464 		printf("usage: %s mode file-name\n", argv[0]);
1465 		code = -1;
1466 		return;
1467 	}
1468 	(void) command("SITE CHMOD %s %s", argv[1], argv[2]);
1469 }
1470 
1471 void
1472 do_umask(argc, argv)
1473 	int argc;
1474 	char *argv[];
1475 {
1476 	int oldverbose = verbose;
1477 
1478 	verbose = 1;
1479 	(void) command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1480 	verbose = oldverbose;
1481 }
1482 
1483 void
1484 idle(argc, argv)
1485 	int argc;
1486 	char *argv[];
1487 {
1488 	int oldverbose = verbose;
1489 
1490 	verbose = 1;
1491 	(void) command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1492 	verbose = oldverbose;
1493 }
1494 
1495 /*
1496  * Ask the other side for help.
1497  */
1498 void
1499 rmthelp(argc, argv)
1500 	int argc;
1501 	char *argv[];
1502 {
1503 	int oldverbose = verbose;
1504 
1505 	verbose = 1;
1506 	(void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1507 	verbose = oldverbose;
1508 }
1509 
1510 /*
1511  * Terminate session and exit.
1512  */
1513 /*VARARGS*/
1514 void
1515 quit(argc, argv)
1516 	int argc;
1517 	char *argv[];
1518 {
1519 
1520 	if (connected)
1521 		disconnect(0, 0);
1522 	pswitch(1);
1523 	if (connected) {
1524 		disconnect(0, 0);
1525 	}
1526 	exit(0);
1527 }
1528 
1529 /*
1530  * Terminate session, but don't exit.
1531  */
1532 void
1533 disconnect(argc, argv)
1534 	int argc;
1535 	char *argv[];
1536 {
1537 
1538 	if (!connected)
1539 		return;
1540 	(void) command("QUIT");
1541 	if (cout) {
1542 		(void) fclose(cout);
1543 	}
1544 	cout = NULL;
1545 	connected = 0;
1546 	data = -1;
1547 	if (!proxy) {
1548 		macnum = 0;
1549 	}
1550 }
1551 
1552 int
1553 confirm(cmd, file)
1554 	char *cmd, *file;
1555 {
1556 	char line[BUFSIZ];
1557 
1558 	if (!interactive)
1559 		return (1);
1560 	printf("%s %s? ", cmd, file);
1561 	(void) fflush(stdout);
1562 	if (fgets(line, sizeof line, stdin) == NULL)
1563 		return (0);
1564 	return (*line != 'n' && *line != 'N');
1565 }
1566 
1567 void
1568 fatal(msg)
1569 	char *msg;
1570 {
1571 
1572 	errx(1, "%s", msg);
1573 }
1574 
1575 /*
1576  * Glob a local file name specification with
1577  * the expectation of a single return value.
1578  * Can't control multiple values being expanded
1579  * from the expression, we return only the first.
1580  */
1581 int
1582 globulize(cpp)
1583 	char **cpp;
1584 {
1585 	char **globbed;
1586 
1587 	if (!doglob)
1588 		return (1);
1589 	globbed = ftpglob(*cpp);
1590 	if (globerr != NULL) {
1591 		printf("%s: %s\n", *cpp, globerr);
1592 		if (globbed) {
1593 			blkfree(globbed);
1594 			free((char *)globbed);
1595 		}
1596 		return (0);
1597 	}
1598 	if (globbed) {
1599 		*cpp = *globbed++;
1600 		/* don't waste too much memory */
1601 		if (globbed) {
1602 			blkfree(globbed);
1603 			free((char *)*globbed);
1604 		}
1605 	}
1606 	return (1);
1607 }
1608 
1609 void
1610 account(argc,argv)
1611 	int argc;
1612 	char **argv;
1613 {
1614 	char acct[50], *getpass(), *ap;
1615 
1616 	if (argc > 1) {
1617 		++argv;
1618 		--argc;
1619 		(void) strncpy(acct,*argv,49);
1620 		acct[49] = '\0';
1621 		while (argc > 1) {
1622 			--argc;
1623 			++argv;
1624 			(void) strncat(acct,*argv, 49-strlen(acct));
1625 		}
1626 		ap = acct;
1627 	}
1628 	else {
1629 		ap = getpass("Account:");
1630 	}
1631 	(void) command("ACCT %s", ap);
1632 }
1633 
1634 jmp_buf abortprox;
1635 
1636 void
1637 proxabort()
1638 {
1639 
1640 	if (!proxy) {
1641 		pswitch(1);
1642 	}
1643 	if (connected) {
1644 		proxflag = 1;
1645 	}
1646 	else {
1647 		proxflag = 0;
1648 	}
1649 	pswitch(0);
1650 	longjmp(abortprox,1);
1651 }
1652 
1653 void
1654 doproxy(argc, argv)
1655 	int argc;
1656 	char *argv[];
1657 {
1658 	struct cmd *c;
1659 	sig_t oldintr;
1660 
1661 	if (argc < 2 && !another(&argc, &argv, "command")) {
1662 		printf("usage: %s command\n", argv[0]);
1663 		code = -1;
1664 		return;
1665 	}
1666 	c = getcmd(argv[1]);
1667 	if (c == (struct cmd *) -1) {
1668 		printf("?Ambiguous command\n");
1669 		(void) fflush(stdout);
1670 		code = -1;
1671 		return;
1672 	}
1673 	if (c == 0) {
1674 		printf("?Invalid command\n");
1675 		(void) fflush(stdout);
1676 		code = -1;
1677 		return;
1678 	}
1679 	if (!c->c_proxy) {
1680 		printf("?Invalid proxy command\n");
1681 		(void) fflush(stdout);
1682 		code = -1;
1683 		return;
1684 	}
1685 	if (setjmp(abortprox)) {
1686 		code = -1;
1687 		return;
1688 	}
1689 	oldintr = signal(SIGINT, proxabort);
1690 	pswitch(1);
1691 	if (c->c_conn && !connected) {
1692 		printf("Not connected\n");
1693 		(void) fflush(stdout);
1694 		pswitch(0);
1695 		(void) signal(SIGINT, oldintr);
1696 		code = -1;
1697 		return;
1698 	}
1699 	(*c->c_handler)(argc-1, argv+1);
1700 	if (connected) {
1701 		proxflag = 1;
1702 	}
1703 	else {
1704 		proxflag = 0;
1705 	}
1706 	pswitch(0);
1707 	(void) signal(SIGINT, oldintr);
1708 }
1709 
1710 void
1711 setcase(argc, argv)
1712 	int argc;
1713 	char *argv[];
1714 {
1715 
1716 	mcase = !mcase;
1717 	printf("Case mapping %s.\n", onoff(mcase));
1718 	code = mcase;
1719 }
1720 
1721 void
1722 setcr(argc, argv)
1723 	int argc;
1724 	char *argv[];
1725 {
1726 
1727 	crflag = !crflag;
1728 	printf("Carriage Return stripping %s.\n", onoff(crflag));
1729 	code = crflag;
1730 }
1731 
1732 void
1733 setntrans(argc,argv)
1734 	int argc;
1735 	char *argv[];
1736 {
1737 	if (argc == 1) {
1738 		ntflag = 0;
1739 		printf("Ntrans off.\n");
1740 		code = ntflag;
1741 		return;
1742 	}
1743 	ntflag++;
1744 	code = ntflag;
1745 	(void) strncpy(ntin, argv[1], 16);
1746 	ntin[16] = '\0';
1747 	if (argc == 2) {
1748 		ntout[0] = '\0';
1749 		return;
1750 	}
1751 	(void) strncpy(ntout, argv[2], 16);
1752 	ntout[16] = '\0';
1753 }
1754 
1755 char *
1756 dotrans(name)
1757 	char *name;
1758 {
1759 	static char new[MAXPATHLEN];
1760 	char *cp1, *cp2 = new;
1761 	int i, ostop, found;
1762 
1763 	for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1764 		continue;
1765 	for (cp1 = name; *cp1; cp1++) {
1766 		found = 0;
1767 		for (i = 0; *(ntin + i) && i < 16; i++) {
1768 			if (*cp1 == *(ntin + i)) {
1769 				found++;
1770 				if (i < ostop) {
1771 					*cp2++ = *(ntout + i);
1772 				}
1773 				break;
1774 			}
1775 		}
1776 		if (!found) {
1777 			*cp2++ = *cp1;
1778 		}
1779 	}
1780 	*cp2 = '\0';
1781 	return (new);
1782 }
1783 
1784 void
1785 setnmap(argc, argv)
1786 	int argc;
1787 	char *argv[];
1788 {
1789 	char *cp;
1790 
1791 	if (argc == 1) {
1792 		mapflag = 0;
1793 		printf("Nmap off.\n");
1794 		code = mapflag;
1795 		return;
1796 	}
1797 	if (argc < 3 && !another(&argc, &argv, "mapout")) {
1798 		printf("Usage: %s [mapin mapout]\n",argv[0]);
1799 		code = -1;
1800 		return;
1801 	}
1802 	mapflag = 1;
1803 	code = 1;
1804 	cp = strchr(altarg, ' ');
1805 	if (proxy) {
1806 		while(*++cp == ' ')
1807 			continue;
1808 		altarg = cp;
1809 		cp = strchr(altarg, ' ');
1810 	}
1811 	*cp = '\0';
1812 	(void) strncpy(mapin, altarg, MAXPATHLEN - 1);
1813 	while (*++cp == ' ')
1814 		continue;
1815 	(void) strncpy(mapout, cp, MAXPATHLEN - 1);
1816 }
1817 
1818 char *
1819 domap(name)
1820 	char *name;
1821 {
1822 	static char new[MAXPATHLEN];
1823 	char *cp1 = name, *cp2 = mapin;
1824 	char *tp[9], *te[9];
1825 	int i, toks[9], toknum = 0, match = 1;
1826 
1827 	for (i=0; i < 9; ++i) {
1828 		toks[i] = 0;
1829 	}
1830 	while (match && *cp1 && *cp2) {
1831 		switch (*cp2) {
1832 			case '\\':
1833 				if (*++cp2 != *cp1) {
1834 					match = 0;
1835 				}
1836 				break;
1837 			case '$':
1838 				if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1839 					if (*cp1 != *(++cp2+1)) {
1840 						toks[toknum = *cp2 - '1']++;
1841 						tp[toknum] = cp1;
1842 						while (*++cp1 && *(cp2+1)
1843 							!= *cp1);
1844 						te[toknum] = cp1;
1845 					}
1846 					cp2++;
1847 					break;
1848 				}
1849 				/* FALLTHROUGH */
1850 			default:
1851 				if (*cp2 != *cp1) {
1852 					match = 0;
1853 				}
1854 				break;
1855 		}
1856 		if (match && *cp1) {
1857 			cp1++;
1858 		}
1859 		if (match && *cp2) {
1860 			cp2++;
1861 		}
1862 	}
1863 	if (!match && *cp1) /* last token mismatch */
1864 	{
1865 		toks[toknum] = 0;
1866 	}
1867 	cp1 = new;
1868 	*cp1 = '\0';
1869 	cp2 = mapout;
1870 	while (*cp2) {
1871 		match = 0;
1872 		switch (*cp2) {
1873 			case '\\':
1874 				if (*(cp2 + 1)) {
1875 					*cp1++ = *++cp2;
1876 				}
1877 				break;
1878 			case '[':
1879 LOOP:
1880 				if (*++cp2 == '$' && isdigit(*(cp2+1))) {
1881 					if (*++cp2 == '0') {
1882 						char *cp3 = name;
1883 
1884 						while (*cp3) {
1885 							*cp1++ = *cp3++;
1886 						}
1887 						match = 1;
1888 					}
1889 					else if (toks[toknum = *cp2 - '1']) {
1890 						char *cp3 = tp[toknum];
1891 
1892 						while (cp3 != te[toknum]) {
1893 							*cp1++ = *cp3++;
1894 						}
1895 						match = 1;
1896 					}
1897 				}
1898 				else {
1899 					while (*cp2 && *cp2 != ',' &&
1900 					    *cp2 != ']') {
1901 						if (*cp2 == '\\') {
1902 							cp2++;
1903 						}
1904 						else if (*cp2 == '$' &&
1905    						        isdigit(*(cp2+1))) {
1906 							if (*++cp2 == '0') {
1907 							   char *cp3 = name;
1908 
1909 							   while (*cp3) {
1910 								*cp1++ = *cp3++;
1911 							   }
1912 							}
1913 							else if (toks[toknum =
1914 							    *cp2 - '1']) {
1915 							   char *cp3=tp[toknum];
1916 
1917 							   while (cp3 !=
1918 								  te[toknum]) {
1919 								*cp1++ = *cp3++;
1920 							   }
1921 							}
1922 						}
1923 						else if (*cp2) {
1924 							*cp1++ = *cp2++;
1925 						}
1926 					}
1927 					if (!*cp2) {
1928 						printf("nmap: unbalanced brackets\n");
1929 						return (name);
1930 					}
1931 					match = 1;
1932 					cp2--;
1933 				}
1934 				if (match) {
1935 					while (*++cp2 && *cp2 != ']') {
1936 					      if (*cp2 == '\\' && *(cp2 + 1)) {
1937 							cp2++;
1938 					      }
1939 					}
1940 					if (!*cp2) {
1941 						printf("nmap: unbalanced brackets\n");
1942 						return (name);
1943 					}
1944 					break;
1945 				}
1946 				switch (*++cp2) {
1947 					case ',':
1948 						goto LOOP;
1949 					case ']':
1950 						break;
1951 					default:
1952 						cp2--;
1953 						goto LOOP;
1954 				}
1955 				break;
1956 			case '$':
1957 				if (isdigit(*(cp2 + 1))) {
1958 					if (*++cp2 == '0') {
1959 						char *cp3 = name;
1960 
1961 						while (*cp3) {
1962 							*cp1++ = *cp3++;
1963 						}
1964 					}
1965 					else if (toks[toknum = *cp2 - '1']) {
1966 						char *cp3 = tp[toknum];
1967 
1968 						while (cp3 != te[toknum]) {
1969 							*cp1++ = *cp3++;
1970 						}
1971 					}
1972 					break;
1973 				}
1974 				/* intentional drop through */
1975 			default:
1976 				*cp1++ = *cp2;
1977 				break;
1978 		}
1979 		cp2++;
1980 	}
1981 	*cp1 = '\0';
1982 	if (!*new) {
1983 		return (name);
1984 	}
1985 	return (new);
1986 }
1987 
1988 void
1989 setsunique(argc, argv)
1990 	int argc;
1991 	char *argv[];
1992 {
1993 
1994 	sunique = !sunique;
1995 	printf("Store unique %s.\n", onoff(sunique));
1996 	code = sunique;
1997 }
1998 
1999 void
2000 setrunique(argc, argv)
2001 	int argc;
2002 	char *argv[];
2003 {
2004 
2005 	runique = !runique;
2006 	printf("Receive unique %s.\n", onoff(runique));
2007 	code = runique;
2008 }
2009 
2010 /* change directory to perent directory */
2011 void
2012 cdup(argc, argv)
2013 	int argc;
2014 	char *argv[];
2015 {
2016 
2017 	if (command("CDUP") == ERROR && code == 500) {
2018 		if (verbose)
2019 			printf("CDUP command not recognized, trying XCUP\n");
2020 		(void) command("XCUP");
2021 	}
2022 }
2023 
2024 /* restart transfer at specific point */
2025 void
2026 restart(argc, argv)
2027 	int argc;
2028 	char *argv[];
2029 {
2030 
2031 	if (argc != 2)
2032 		printf("restart: offset not specified\n");
2033 	else {
2034 		restart_point = atol(argv[1]);
2035 		printf("restarting at %qd. %s\n", restart_point,
2036 		    "execute get, put or append to initiate transfer");
2037 	}
2038 }
2039 
2040 /* show remote system type */
2041 void
2042 syst(argc, argv)
2043 	int argc;
2044 	char *argv[];
2045 {
2046 
2047 	(void) command("SYST");
2048 }
2049 
2050 void
2051 macdef(argc, argv)
2052 	int argc;
2053 	char *argv[];
2054 {
2055 	char *tmp;
2056 	int c;
2057 
2058 	if (macnum == 16) {
2059 		printf("Limit of 16 macros have already been defined\n");
2060 		code = -1;
2061 		return;
2062 	}
2063 	if (argc < 2 && !another(&argc, &argv, "macro name")) {
2064 		printf("Usage: %s macro_name\n",argv[0]);
2065 		code = -1;
2066 		return;
2067 	}
2068 	if (interactive) {
2069 		printf("Enter macro line by line, terminating it with a null line\n");
2070 	}
2071 	(void) strncpy(macros[macnum].mac_name, argv[1], 8);
2072 	if (macnum == 0) {
2073 		macros[macnum].mac_start = macbuf;
2074 	}
2075 	else {
2076 		macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2077 	}
2078 	tmp = macros[macnum].mac_start;
2079 	while (tmp != macbuf+4096) {
2080 		if ((c = getchar()) == EOF) {
2081 			printf("macdef:end of file encountered\n");
2082 			code = -1;
2083 			return;
2084 		}
2085 		if ((*tmp = c) == '\n') {
2086 			if (tmp == macros[macnum].mac_start) {
2087 				macros[macnum++].mac_end = tmp;
2088 				code = 0;
2089 				return;
2090 			}
2091 			if (*(tmp-1) == '\0') {
2092 				macros[macnum++].mac_end = tmp - 1;
2093 				code = 0;
2094 				return;
2095 			}
2096 			*tmp = '\0';
2097 		}
2098 		tmp++;
2099 	}
2100 	while (1) {
2101 		while ((c = getchar()) != '\n' && c != EOF)
2102 			/* LOOP */;
2103 		if (c == EOF || getchar() == '\n') {
2104 			printf("Macro not defined - 4k buffer exceeded\n");
2105 			code = -1;
2106 			return;
2107 		}
2108 	}
2109 }
2110 
2111 /*
2112  * get size of file on remote machine
2113  */
2114 void
2115 sizecmd(argc, argv)
2116 	int argc;
2117 	char *argv[];
2118 {
2119 
2120 	if (argc < 2 && !another(&argc, &argv, "filename")) {
2121 		printf("usage: %s filename\n", argv[0]);
2122 		code = -1;
2123 		return;
2124 	}
2125 	(void) command("SIZE %s", argv[1]);
2126 }
2127 
2128 /*
2129  * get last modification time of file on remote machine
2130  */
2131 void
2132 modtime(argc, argv)
2133 	int argc;
2134 	char *argv[];
2135 {
2136 	int overbose;
2137 
2138 	if (argc < 2 && !another(&argc, &argv, "filename")) {
2139 		printf("usage: %s filename\n", argv[0]);
2140 		code = -1;
2141 		return;
2142 	}
2143 	overbose = verbose;
2144 	if (debug == 0)
2145 		verbose = -1;
2146 	if (command("MDTM %s", argv[1]) == COMPLETE) {
2147 		int yy, mo, day, hour, min, sec;
2148 		sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
2149 			&day, &hour, &min, &sec);
2150 		/* might want to print this in local time */
2151 		printf("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1],
2152 			mo, day, yy, hour, min, sec);
2153 	} else
2154 		printf("%s\n", reply_string);
2155 	verbose = overbose;
2156 }
2157 
2158 /*
2159  * show status on reomte machine
2160  */
2161 void
2162 rmtstatus(argc, argv)
2163 	int argc;
2164 	char *argv[];
2165 {
2166 
2167 	(void) command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
2168 }
2169 
2170 /*
2171  * get file if modtime is more recent than current file
2172  */
2173 void
2174 newer(argc, argv)
2175 	int argc;
2176 	char *argv[];
2177 {
2178 
2179 	if (getit(argc, argv, -1, "w"))
2180 		printf("Local file \"%s\" is newer than remote file \"%s\"\n",
2181 			argv[2], argv[1]);
2182 }
2183