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