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