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