xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/ftp/cmds.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  *	Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  *	Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	All Rights Reserved  	*/
29 
30 /*
31  *	University Copyright- Copyright (c) 1982, 1986, 1988
32  *	The Regents of the University of California
33  *	All Rights Reserved
34  *
35  *	University Acknowledgment- Portions of this document are derived from
36  *	software developed by the University of California, Berkeley, and its
37  *	contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 
43 /*
44  * FTP User Program -- Command Routines.
45  */
46 #define	FTP_NAMES
47 #include "ftp_var.h"
48 
49 FILE	*tmp_nlst = NULL;	/* tmp file; holds NLST results for mget, etc */
50 
51 static char *mname;
52 static jmp_buf jabort;
53 static jmp_buf abortprox;
54 
55 static char *remglob(char *argv[], int doswitch);
56 static char *onoff(int bool);
57 static int confirm(char *cmd, char *file);
58 static int globulize(char **cpp);
59 static void proxabort(int sig);
60 static void mabort(int sig);
61 static char *dotrans(char *name);
62 static char *domap(char *name);
63 static void getit(int argc, char *argv[], int restartit, char *mode);
64 
65 static char *getlevel(int);
66 
67 /* Prompt for command argument, add to buffer with space separator */
68 static int
69 prompt_for_arg(char *buffer, int buffer_size, char *prompt)
70 {
71 	if (strlen(buffer) > buffer_size - 2) {
72 		(void) printf("Line too long\n");
73 		return (-1);
74 	}
75 	strcat(buffer, " ");
76 	stop_timer();
77 	(void) printf("(%s) ", prompt);
78 	if (fgets(buffer + strlen(buffer), buffer_size - strlen(buffer), stdin)
79 	    == NULL) {
80 		reset_timer();
81 		return (-1);
82 	}
83 
84 	/* Flush what didn't fit in the buffer */
85 	if (buffer[strlen(buffer)-1] != '\n') {
86 		while (fgetc(stdin) != '\n' && !ferror(stdin) && !feof(stdin))
87 			;
88 		(void) printf("Line too long\n");
89 		reset_timer();
90 		return (-1);
91 	} else
92 		buffer[strlen(buffer)-1] = 0;
93 
94 	reset_timer();
95 	return (0);
96 }
97 
98 
99 /*
100  * Connect to peer server and
101  * auto-login, if possible.
102  */
103 void
104 setpeer(int argc, char *argv[])
105 {
106 	char *host;
107 
108 	if (connected) {
109 		(void) printf("Already connected to %s, use close first.\n",
110 			hostname);
111 		code = -1;
112 		return;
113 	}
114 	if (argc < 2) {
115 		if (prompt_for_arg(line, sizeof (line), "to") == -1) {
116 			code = -1;
117 			return;
118 		}
119 		makeargv();
120 		argc = margc;
121 		argv = margv;
122 	}
123 	if (argc > 3 || argc < 2) {
124 		(void) printf("usage: %s host-name [port]\n", argv[0]);
125 		code = -1;
126 		return;
127 	}
128 	strcpy(typename, "ascii");
129 	host = hookup(argv[1], (argc > 2 ? argv[2] : "ftp"));
130 	if (host) {
131 		int overbose;
132 		extern char reply_string[];
133 
134 		connected = 1;
135 		/*
136 		 * Set up defaults for FTP.
137 		 */
138 		clevel = dlevel = PROT_C;
139 		if (autoauth) {
140 			if (do_auth() && autoencrypt) {
141 			    clevel = PROT_P;
142 			    setpbsz(1<<20);
143 			    if (command("PROT P") == COMPLETE)
144 				dlevel = PROT_P;
145 			    else {
146 				(void) fprintf(stderr,
147 					"%s: couldn't enable encryption\n",
148 					argv[0]);
149 				/* unable to encrypt command channel, too! */
150 				dlevel = clevel = PROT_C;
151 			    }
152 			}
153 			if ((auth_type != AUTHTYPE_NONE) && (clevel == PROT_C))
154 				clevel = PROT_S;
155 		}
156 
157 		if (autologin)
158 			(void) login(argv[1]);
159 
160 		overbose = verbose;
161 		if (debug == 0)
162 			verbose = -1;
163 		if (command("SYST") == COMPLETE && overbose) {
164 			char *cp, c;
165 
166 			cp = index(reply_string+4, ' ');
167 			if (cp == NULL)
168 				cp = index(reply_string+4, '\r');
169 			if (cp) {
170 				if (cp[-1] == '.')
171 					cp--;
172 				c = *cp;
173 				*cp = '\0';
174 			}
175 
176 			(void) printf("Remote system type is %s.\n",
177 				reply_string+4);
178 			if (cp)
179 				*cp = c;
180 		}
181 		if (strncmp(reply_string, "215 UNIX Type: L8", 17) == 0) {
182 			setbinary(0, NULL);
183 			if (overbose)
184 				(void) printf(
185 				    "Using %s mode to transfer files.\n",
186 				    typename);
187 		} else if (overbose &&
188 		    strncmp(reply_string, "215 TOPS20", 10) == 0) {
189 			(void) printf(
190 			    "Remember to set tenex mode when transfering "
191 			    "binary files from this machine.\n");
192 		}
193 		verbose = overbose;
194 	}
195 }
196 
197 static struct types {
198 	char	*t_name;
199 	char	*t_mode;
200 	int	t_type;
201 	char	*t_arg;
202 } types[] = {
203 	{ "ascii",	"A",	TYPE_A,	0 },
204 	{ "binary",	"I",	TYPE_I,	0 },
205 	{ "image",	"I",	TYPE_I,	0 },
206 	{ "ebcdic",	"E",	TYPE_E,	0 },
207 	{ "tenex",	"L",	TYPE_L,	bytename },
208 	0
209 };
210 
211 /*
212  * Set transfer type.
213  */
214 void
215 settype(int argc, char *argv[])
216 {
217 	struct types *p;
218 	int comret;
219 
220 	if (argc > 2) {
221 		char *sep;
222 
223 		(void) printf("usage: %s [", argv[0]);
224 		sep = " ";
225 		for (p = types; p->t_name; p++) {
226 			(void) printf("%s%s", sep, p->t_name);
227 			if (*sep == ' ')
228 				sep = " | ";
229 		}
230 		(void) printf(" ]\n");
231 		code = -1;
232 		return;
233 	}
234 	if (argc < 2) {
235 		(void) printf("Using %s mode to transfer files.\n", typename);
236 		code = 0;
237 		return;
238 	}
239 	for (p = types; p->t_name; p++)
240 		if (strcmp(argv[1], p->t_name) == 0)
241 			break;
242 	if (p->t_name == 0) {
243 		(void) printf("%s: unknown mode\n", argv[1]);
244 		code = -1;
245 		return;
246 	}
247 	if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
248 		comret = command("TYPE %s %s", p->t_mode, p->t_arg);
249 	else
250 		comret = command("TYPE %s", p->t_mode);
251 	if (comret == COMPLETE) {
252 		(void) strcpy(typename, p->t_name);
253 		type = p->t_type;
254 	}
255 }
256 
257 /*
258  * Set binary transfer type.
259  */
260 /*ARGSUSED*/
261 void
262 setbinary(int argc, char *argv[])
263 {
264 	call(settype, "type", "binary", 0);
265 }
266 
267 /*
268  * Set ascii transfer type.
269  */
270 /*ARGSUSED*/
271 void
272 setascii(int argc, char *argv[])
273 {
274 	call(settype, "type", "ascii", 0);
275 }
276 
277 /*
278  * Set tenex transfer type.
279  */
280 /*ARGSUSED*/
281 void
282 settenex(int argc, char *argv[])
283 {
284 	call(settype, "type", "tenex", 0);
285 }
286 
287 /*
288  * Set ebcdic transfer type.
289  */
290 /*ARGSUSED*/
291 void
292 setebcdic(int argc, char *argv[])
293 {
294 	call(settype, "type", "ebcdic", 0);
295 }
296 
297 /*
298  * Set file transfer mode.
299  */
300 /*ARGSUSED*/
301 void
302 setmode(int argc, char *argv[])
303 {
304 	(void) printf("We only support %s mode, sorry.\n", modename);
305 	code = -1;
306 }
307 
308 /*
309  * Set file transfer format.
310  */
311 /*ARGSUSED*/
312 void
313 setform(int argc, char *argv[])
314 {
315 	(void) printf("We only support %s format, sorry.\n", formname);
316 	code = -1;
317 }
318 
319 /*
320  * Set file transfer structure.
321  */
322 /*ARGSUSED*/
323 void
324 setstruct(int argc, char *argv[])
325 {
326 
327 	(void) printf("We only support %s structure, sorry.\n", structname);
328 	code = -1;
329 }
330 
331 /*
332  * Send a single file.
333  */
334 void
335 put(int argc, char *argv[])
336 {
337 	char *cmd;
338 	int loc = 0;
339 	char *oldargv1;
340 
341 	if (argc == 2) {
342 		argc++;
343 		argv[2] = argv[1];
344 		loc++;
345 	}
346 	if (argc < 2) {
347 		if (prompt_for_arg(line, sizeof (line), "local-file") == -1) {
348 			code = -1;
349 			return;
350 		}
351 		makeargv();
352 		argc = margc;
353 		argv = margv;
354 	}
355 	if (argc < 2) {
356 usage:
357 		(void) printf("usage: %s local-file remote-file\n", argv[0]);
358 		code = -1;
359 		return;
360 	}
361 	if (argc < 3) {
362 		if (prompt_for_arg(line, sizeof (line), "remote-file") == -1) {
363 			code = -1;
364 			return;
365 		}
366 		makeargv();
367 		argc = margc;
368 		argv = margv;
369 	}
370 	if (argc < 3)
371 		goto usage;
372 	oldargv1 = argv[1];
373 	if (!globulize(&argv[1])) {
374 		code = -1;
375 		return;
376 	}
377 	/*
378 	 * If "globulize" modifies argv[1], and argv[2] is a copy of
379 	 * the old argv[1], make it a copy of the new argv[1].
380 	 */
381 	if (argv[1] != oldargv1 && argv[2] == oldargv1) {
382 		argv[2] = argv[1];
383 	}
384 	cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
385 	if (loc && ntflag) {
386 		argv[2] = dotrans(argv[2]);
387 	}
388 	if (loc && mapflag) {
389 		argv[2] = domap(argv[2]);
390 	}
391 	sendrequest(cmd, argv[1], argv[2], 1);
392 }
393 
394 /*ARGSUSED*/
395 static void
396 mabort(int sig)
397 {
398 	int ointer;
399 
400 	(void) printf("\n");
401 	(void) fflush(stdout);
402 	if (mflag && fromatty) {
403 		ointer = interactive;
404 		interactive = 1;
405 		if (confirm("Continue with", mname)) {
406 			interactive = ointer;
407 			longjmp(jabort, 0);
408 		}
409 		interactive = ointer;
410 	}
411 	mflag = 0;
412 	longjmp(jabort, 0);
413 }
414 
415 /*
416  * Send multiple files.
417  */
418 void
419 mput(int argc, char *argv[])
420 {
421 	int i;
422 	int ointer;
423 	void (*oldintr)();
424 	char *tp;
425 	int	len;
426 
427 	if (argc < 2) {
428 		if (prompt_for_arg(line, sizeof (line), "local-files") == -1) {
429 			code = -1;
430 			return;
431 		}
432 		makeargv();
433 		argc = margc;
434 		argv = margv;
435 	}
436 	if (argc < 2) {
437 		(void) printf("usage: %s local-files\n", argv[0]);
438 		code = -1;
439 		return;
440 	}
441 	mname = argv[0];
442 	mflag = 1;
443 	oldintr = signal(SIGINT, mabort);
444 	(void) setjmp(jabort);
445 	if (proxy) {
446 		char *cp, *tp2, tmpbuf[MAXPATHLEN];
447 
448 		while ((cp = remglob(argv, 0)) != NULL) {
449 			if (*cp == 0) {
450 				mflag = 0;
451 				continue;
452 			}
453 			if (mflag && confirm(argv[0], cp)) {
454 				tp = cp;
455 				if (mcase) {
456 					while (*tp) {
457 						if ((len =
458 						    mblen(tp, MB_CUR_MAX)) <= 0)
459 							len = 1;
460 						if (islower(*tp))
461 							break;
462 						tp += len;
463 					}
464 					if (!*tp) {
465 						tp = cp;
466 						tp2 = tmpbuf;
467 						while (*tp) {
468 							if ((len = mblen(tp,
469 							    MB_CUR_MAX)) <= 0)
470 								len = 1;
471 							memcpy(tp2, tp, len);
472 							if (isupper(*tp2)) {
473 								*tp2 = 'a' +
474 								    *tp2 - 'A';
475 							}
476 							tp += len;
477 							tp2 += len;
478 						}
479 						*tp2 = 0;
480 						tp = tmpbuf;
481 					}
482 				}
483 				if (ntflag) {
484 					tp = dotrans(tp);
485 				}
486 				if (mapflag) {
487 					tp = domap(tp);
488 				}
489 				sendrequest((sunique) ? "STOU" : "STOR",
490 				    cp, tp, 0);
491 				if (!mflag && fromatty) {
492 					ointer = interactive;
493 					interactive = 1;
494 					if (confirm("Continue with", "mput")) {
495 						mflag++;
496 					}
497 					interactive = ointer;
498 				}
499 			}
500 		}
501 		(void) signal(SIGINT, oldintr);
502 		mflag = 0;
503 		return;
504 	}
505 	for (i = 1; i < argc; i++) {
506 		char **cpp, **gargs;
507 
508 		if (!doglob) {
509 			if (mflag && confirm(argv[0], argv[i])) {
510 				tp = (ntflag) ? dotrans(argv[i]) : argv[i];
511 				tp = (mapflag) ? domap(tp) : tp;
512 				sendrequest((sunique) ? "STOU" : "STOR",
513 				    argv[i], tp, 1);
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 			continue;
524 		}
525 		gargs = glob(argv[i]);
526 		if (globerr != NULL) {
527 			(void) printf("%s\n", globerr);
528 			if (gargs)
529 				blkfree(gargs);
530 			continue;
531 		}
532 		for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
533 			if (mflag && confirm(argv[0], *cpp)) {
534 				tp = (ntflag) ? dotrans(*cpp) : *cpp;
535 				tp = (mapflag) ? domap(tp) : tp;
536 				sendrequest((sunique) ? "STOU" : "STOR",
537 				    *cpp, tp, 0);
538 				if (!mflag && fromatty) {
539 					ointer = interactive;
540 					interactive = 1;
541 					if (confirm("Continue with", "mput")) {
542 						mflag++;
543 					}
544 					interactive = ointer;
545 				}
546 			}
547 		}
548 		if (gargs != NULL)
549 			blkfree(gargs);
550 	}
551 	(void) signal(SIGINT, oldintr);
552 	mflag = 0;
553 }
554 
555 /*
556  * Restart transfer at a specific offset.
557  */
558 void
559 restart(int argc, char *argv[])
560 {
561 	off_t orestart_point = restart_point;
562 
563 	if (argc > 2) {
564 		(void) printf("usage: %s [marker]\n", argv[0]);
565 		code = -1;
566 		return;
567 	}
568 	if (argc == 2) {
569 		longlong_t rp;
570 		char *endp;
571 
572 		errno = 0;
573 		rp = strtoll(argv[1], &endp, 10);
574 		if (errno || rp < 0 || *endp != '\0')
575 			(void) printf("%s: Invalid offset `%s'\n",
576 				argv[0], argv[1]);
577 		else
578 			restart_point = rp;
579 	}
580 	if (restart_point == 0) {
581 		if (orestart_point == 0)
582 			(void) printf("No restart marker defined\n");
583 		else
584 			(void) printf("Restart marker cleared\n");
585 	} else
586 		(void) printf(
587 			"Restarting at %lld for next get, put or append\n",
588 			(longlong_t)restart_point);
589 }
590 
591 void
592 reget(int argc, char *argv[])
593 {
594 	getit(argc, argv, 1, "r+w");
595 }
596 
597 void
598 get(int argc, char *argv[])
599 {
600 	getit(argc, argv, 0, restart_point ? "r+w" : "w");
601 }
602 
603 /*
604  * Receive one file.
605  */
606 static void
607 getit(int argc, char *argv[], int restartit, char *mode)
608 {
609 	int loc = 0;
610 	int len;
611 	int allowpipe = 1;
612 
613 	if (argc == 2) {
614 		argc++;
615 		argv[2] = argv[1];
616 		/* Only permit !file if two arguments. */
617 		allowpipe = 0;
618 		loc++;
619 	}
620 	if (argc < 2) {
621 		if (prompt_for_arg(line, sizeof (line), "remote-file") == -1) {
622 			code = -1;
623 			return;
624 		}
625 		makeargv();
626 		argc = margc;
627 		argv = margv;
628 	}
629 	if (argc < 2) {
630 usage:
631 		(void) printf("usage: %s remote-file [ local-file ]\n",
632 			argv[0]);
633 		code = -1;
634 		return;
635 	}
636 	if (argc < 3) {
637 		if (prompt_for_arg(line, sizeof (line), "local-file") == -1) {
638 			code = -1;
639 			return;
640 		}
641 		makeargv();
642 		argc = margc;
643 		argv = margv;
644 	}
645 	if (argc < 3)
646 		goto usage;
647 	if (!globulize(&argv[2])) {
648 		code = -1;
649 		return;
650 	}
651 	if (loc && mcase) {
652 		char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
653 
654 		while (*tp) {
655 			if ((len = mblen(tp, MB_CUR_MAX)) <= 0)
656 				len = 1;
657 			if (islower(*tp))
658 				break;
659 			tp += len;
660 		}
661 		if (!*tp) {
662 			tp = argv[2];
663 			tp2 = tmpbuf;
664 			while (*tp) {
665 				if ((len = mblen(tp, MB_CUR_MAX)) <= 0)
666 					len = 1;
667 				memcpy(tp2, tp, len);
668 				if (isupper(*tp2))
669 					*tp2 = 'a' + *tp2 - 'A';
670 				tp += len;
671 				tp2 += len;
672 			}
673 			*tp2 = 0;
674 			argv[2] = tmpbuf;
675 		}
676 	}
677 	if (loc && ntflag) {
678 		argv[2] = dotrans(argv[2]);
679 	}
680 	if (loc && mapflag) {
681 		argv[2] = domap(argv[2]);
682 	}
683 	if (restartit) {
684 		struct stat stbuf;
685 
686 		if (stat(argv[2], &stbuf) < 0) {
687 			perror(argv[2]);
688 			code = -1;
689 			return;
690 		}
691 		restart_point = stbuf.st_size;
692 	}
693 	recvrequest("RETR", argv[2], argv[1], mode, allowpipe);
694 	restart_point = 0;
695 }
696 
697 /*
698  * Get multiple files.
699  */
700 void
701 mget(int argc, char *argv[])
702 {
703 	char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
704 	int ointer;
705 	void (*oldintr)();
706 	int need_convert;
707 	int	len;
708 
709 	if (argc < 2) {
710 		if (prompt_for_arg(line, sizeof (line), "remote-files") < 0) {
711 			code = -1;
712 			return;
713 		}
714 		makeargv();
715 		argc = margc;
716 		argv = margv;
717 	}
718 	if (argc < 2) {
719 		(void) printf("usage: %s remote-files\n", argv[0]);
720 		code = -1;
721 		return;
722 	}
723 	mname = argv[0];
724 	mflag = 1;
725 	oldintr = signal(SIGINT, mabort);
726 	(void) setjmp(jabort);
727 	while ((cp = remglob(argv, proxy)) != NULL) {
728 		if (*cp == '\0') {
729 			mflag = 0;
730 			continue;
731 		}
732 		if (mflag && confirm(argv[0], cp)) {
733 			strcpy(tmpbuf, cp);
734 			tp =  tmpbuf;
735 			need_convert = 1;
736 			if (mcase) {
737 				tp2 = tp;
738 				while (*tp2 && need_convert) {
739 				/* Need any case convert? */
740 					if (islower(*tp2))
741 						need_convert = 0;
742 					if ((len = mblen(tp2, MB_CUR_MAX)) <= 0)
743 						len = 1;
744 					tp2 += len;
745 				}
746 				tp2 = tp;
747 				while (need_convert && *tp2) {
748 				/* Convert to lower case */
749 					if (isupper(*tp2))
750 						*tp2 = tolower(*tp2);
751 					if ((len = mblen(tp2, MB_CUR_MAX)) <= 0)
752 						len = 1;
753 					tp2 += len;
754 				}
755 			}
756 
757 			if (ntflag) {
758 				tp = dotrans(tp);
759 			}
760 			if (mapflag) {
761 				tp = domap(tp);
762 			}
763 			recvrequest("RETR", tp, cp, "w", 0);
764 			restart_point = 0;
765 			if (!mflag && fromatty) {
766 				ointer = interactive;
767 				interactive = 1;
768 				if (confirm("Continue with", "mget")) {
769 					mflag++;
770 				}
771 				interactive = ointer;
772 			}
773 		}
774 	}
775 	(void) signal(SIGINT, oldintr);
776 	mflag = 0;
777 }
778 
779 static char *
780 remglob(char *argv[], int doswitch)
781 {
782 	static char buf[MAXPATHLEN];
783 	static char **args;
784 	int oldverbose, oldhash;
785 	char *cp;
786 
787 	if (!mflag) {
788 		if (!doglob) {
789 			args = NULL;
790 		} else {
791 			if (tmp_nlst != NULL) {
792 				(void) fclose(tmp_nlst);
793 				tmp_nlst = NULL;
794 			}
795 		}
796 		return (NULL);
797 	}
798 	if (!doglob) {
799 		if (args == NULL)
800 			args = argv;
801 		if ((cp = *++args) == NULL)
802 			args = NULL;
803 		return (cp);
804 	}
805 	if (tmp_nlst == NULL) {
806 		if ((tmp_nlst = tmpfile()) == NULL) {
807 			(void) printf("%s\n", strerror(errno));
808 			return (NULL);
809 		}
810 		oldverbose = verbose, verbose = 0;
811 		oldhash = hash, hash = 0;
812 		if (doswitch) {
813 			pswitch(!proxy);
814 		}
815 		for (; *++argv != NULL; )
816 			recvrequest("NLST", NULL, *argv, "", 0);
817 		rewind(tmp_nlst);
818 		if (doswitch) {
819 			pswitch(!proxy);
820 		}
821 		verbose = oldverbose; hash = oldhash;
822 	}
823 	reset_timer();
824 	if (fgets(buf, sizeof (buf), tmp_nlst) == NULL) {
825 		(void) fclose(tmp_nlst), tmp_nlst = NULL;
826 		return (NULL);
827 	}
828 	if ((cp = index(buf, '\n')) != NULL)
829 		*cp = '\0';
830 	return (buf);
831 }
832 
833 static char *
834 onoff(int bool)
835 {
836 	return (bool ? "on" : "off");
837 }
838 
839 /*
840  * Show status.
841  */
842 /*ARGSUSED*/
843 void
844 status(int argc, char *argv[])
845 {
846 	int i;
847 	char *levelp;
848 
849 	if (connected)
850 		(void) printf("Connected to %s.\n", hostname);
851 	else
852 		(void) printf("Not connected.\n");
853 	if (!proxy) {
854 		pswitch(1);
855 		if (connected) {
856 			(void) printf("Connected for proxy commands to %s.\n",
857 			    hostname);
858 		} else {
859 			(void) printf("No proxy connection.\n");
860 		}
861 		pswitch(0);
862 	}
863 
864 	if (auth_type != AUTHTYPE_NONE)
865 		(void) printf("Authentication type: %s\n",
866 			GSS_AUTHTYPE_NAME(auth_type));
867 	else
868 		(void) printf("Not authenticated.\n");
869 	(void) printf("Mechanism: %s\n", mechstr);
870 	(void) printf("Autoauth: %s; Autologin: %s\n",
871 		onoff(autoauth), onoff(autologin));
872 	levelp = getlevel(clevel);
873 	(void) printf("Control Channel Protection Level: %s\n",
874 		levelp ? levelp : "<unknown>");
875 	levelp = getlevel(dlevel);
876 	(void) printf("Data Channel Protection Level: %s\n",
877 		levelp ? levelp : "<unknown>");
878 
879 	(void) printf("Passive mode: %s.\n", onoff(passivemode));
880 	(void) printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
881 		modename, typename, formname, structname);
882 	(void) printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
883 		onoff(verbose), onoff(bell), onoff(interactive),
884 		onoff(doglob));
885 	(void) printf("Store unique: %s; Receive unique: %s\n", onoff(sunique),
886 		onoff(runique));
887 	(void) printf("Case: %s; CR stripping: %s\n",
888 		onoff(mcase), onoff(crflag));
889 	if (ntflag) {
890 		(void) printf("Ntrans: (in) %s (out) %s\n", ntin, ntout);
891 	} else {
892 		(void) printf("Ntrans: off\n");
893 	}
894 	if (mapflag) {
895 		(void) printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
896 	} else {
897 		(void) printf("Nmap: off\n");
898 	}
899 	(void) printf("Hash mark printing: %s; Use of PORT cmds: %s\n",
900 		onoff(hash), onoff(sendport));
901 	if (macnum > 0) {
902 		(void) printf("Macros:\n");
903 		for (i = 0; i < macnum; i++) {
904 			(void) printf("\t%s\n", macros[i].mac_name);
905 		}
906 	}
907 	code = 0;
908 }
909 
910 /*
911  * Set beep on cmd completed mode.
912  */
913 /*ARGSUSED*/
914 void
915 setbell(int argc, char *argv[])
916 {
917 	bell = !bell;
918 	(void) printf("Bell mode %s.\n", onoff(bell));
919 	code = bell;
920 }
921 
922 /*
923  * Turn on packet tracing.
924  */
925 /*ARGSUSED*/
926 void
927 settrace(int argc, char *argv[])
928 {
929 	trace = !trace;
930 	(void) printf("Packet tracing %s.\n", onoff(trace));
931 	code = trace;
932 }
933 
934 /*
935  * Toggle hash mark printing during transfers.
936  */
937 /*ARGSUSED*/
938 void
939 sethash(int argc, char *argv[])
940 {
941 	hash = !hash;
942 	(void) printf("Hash mark printing %s", onoff(hash));
943 	code = hash;
944 	if (hash)
945 		(void) printf(" (%d bytes/hash mark)", HASHSIZ);
946 	(void) printf(".\n");
947 }
948 
949 /*
950  * Turn on printing of server echo's.
951  */
952 /*ARGSUSED*/
953 void
954 setverbose(int argc, char *argv[])
955 {
956 	verbose = !verbose;
957 	(void) printf("Verbose mode %s.\n", onoff(verbose));
958 	code = verbose;
959 }
960 
961 /*
962  * Toggle PORT cmd use before each data connection.
963  */
964 /*ARGSUSED*/
965 void
966 setport(int argc, char *argv[])
967 {
968 	sendport = !sendport;
969 	(void) printf("Use of PORT cmds %s.\n", onoff(sendport));
970 	code = sendport;
971 }
972 
973 /*
974  * Turn on interactive prompting
975  * during mget, mput, and mdelete.
976  */
977 /*ARGSUSED*/
978 void
979 setprompt(int argc, char *argv[])
980 {
981 	interactive = !interactive;
982 	(void) printf("Interactive mode %s.\n", onoff(interactive));
983 	code = interactive;
984 }
985 
986 /*
987  * Toggle metacharacter interpretation
988  * on local file names.
989  */
990 /*ARGSUSED*/
991 void
992 setglob(int argc, char *argv[])
993 {
994 	doglob = !doglob;
995 	(void) printf("Globbing %s.\n", onoff(doglob));
996 	code = doglob;
997 }
998 
999 /*
1000  * Set debugging mode on/off and/or
1001  * set level of debugging.
1002  */
1003 void
1004 setdebug(int argc, char *argv[])
1005 {
1006 	int val;
1007 
1008 	if (argc > 1) {
1009 		val = atoi(argv[1]);
1010 		if (val < 0) {
1011 			(void) printf("%s: bad debugging value.\n", argv[1]);
1012 			code = -1;
1013 			return;
1014 		}
1015 	} else
1016 		val = !debug;
1017 	debug = val;
1018 	if (debug)
1019 		options |= SO_DEBUG;
1020 	else
1021 		options &= ~SO_DEBUG;
1022 	(void) printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
1023 	code = debug > 0;
1024 }
1025 
1026 /*
1027  * Set current working directory
1028  * on remote machine.
1029  */
1030 void
1031 cd(int argc, char *argv[])
1032 {
1033 	if (argc < 2) {
1034 		if (prompt_for_arg(line, sizeof (line), "remote-directory") <
1035 		    0) {
1036 			code = -1;
1037 			return;
1038 		}
1039 		makeargv();
1040 		argc = margc;
1041 		argv = margv;
1042 	}
1043 	if (argc < 2) {
1044 		(void) printf("usage: %s remote-directory\n", argv[0]);
1045 		code = -1;
1046 		return;
1047 	}
1048 	(void) command("CWD %s", argv[1]);
1049 }
1050 
1051 /*
1052  * Set current working directory
1053  * on local machine.
1054  */
1055 void
1056 lcd(int argc, char *argv[])
1057 {
1058 	char buf[MAXPATHLEN], *bufptr;
1059 
1060 	if (argc < 2)
1061 		argc++, argv[1] = home;
1062 	if (argc != 2) {
1063 		(void) printf("usage: %s local-directory\n", argv[0]);
1064 		code = -1;
1065 		return;
1066 	}
1067 	if (!globulize(&argv[1])) {
1068 		code = -1;
1069 		return;
1070 	}
1071 	if (chdir(argv[1]) < 0) {
1072 		perror(argv[1]);
1073 		code = -1;
1074 		return;
1075 	}
1076 	bufptr = getcwd(buf, MAXPATHLEN);
1077 	/*
1078 	 * Even though chdir may succeed, getcwd may fail if a component
1079 	 * of the pwd is unreadable. In this case, print the argument to
1080 	 * chdir as the resultant directory, since we know it succeeded above.
1081 	 */
1082 	(void) printf("Local directory now %s\n", (bufptr ? bufptr : argv[1]));
1083 	code = 0;
1084 }
1085 
1086 /*
1087  * Delete a single file.
1088  */
1089 void
1090 delete(int argc, char *argv[])
1091 {
1092 
1093 	if (argc < 2) {
1094 		if (prompt_for_arg(line, sizeof (line), "remote-file") < 0) {
1095 			code = -1;
1096 			return;
1097 		}
1098 		makeargv();
1099 		argc = margc;
1100 		argv = margv;
1101 	}
1102 	if (argc < 2) {
1103 		(void) printf("usage: %s remote-file\n", argv[0]);
1104 		code = -1;
1105 		return;
1106 	}
1107 	(void) command("DELE %s", argv[1]);
1108 }
1109 
1110 /*
1111  * Delete multiple files.
1112  */
1113 void
1114 mdelete(int argc, char *argv[])
1115 {
1116 	char *cp;
1117 	int ointer;
1118 	void (*oldintr)();
1119 
1120 	if (argc < 2) {
1121 		if (prompt_for_arg(line, sizeof (line), "remote-files") < 0) {
1122 			code = -1;
1123 			return;
1124 		}
1125 		makeargv();
1126 		argc = margc;
1127 		argv = margv;
1128 	}
1129 	if (argc < 2) {
1130 		(void) printf("usage: %s remote-files\n", argv[0]);
1131 		code = -1;
1132 		return;
1133 	}
1134 	mname = argv[0];
1135 	mflag = 1;
1136 	oldintr = signal(SIGINT, mabort);
1137 	(void) setjmp(jabort);
1138 	while ((cp = remglob(argv, 0)) != NULL) {
1139 		if (*cp == '\0') {
1140 			mflag = 0;
1141 			continue;
1142 		}
1143 		if (mflag && confirm(argv[0], cp)) {
1144 			(void) command("DELE %s", cp);
1145 			if (!mflag && fromatty) {
1146 				ointer = interactive;
1147 				interactive = 1;
1148 				if (confirm("Continue with", "mdelete")) {
1149 					mflag++;
1150 				}
1151 				interactive = ointer;
1152 			}
1153 		}
1154 	}
1155 	(void) signal(SIGINT, oldintr);
1156 	mflag = 0;
1157 }
1158 
1159 /*
1160  * Rename a remote file.
1161  */
1162 void
1163 renamefile(int argc, char *argv[])
1164 {
1165 
1166 	if (argc < 2) {
1167 		if (prompt_for_arg(line, sizeof (line), "from-name") < 0) {
1168 			code = -1;
1169 			return;
1170 		}
1171 		makeargv();
1172 		argc = margc;
1173 		argv = margv;
1174 	}
1175 	if (argc < 2) {
1176 usage:
1177 		(void) printf("%s from-name to-name\n", argv[0]);
1178 		code = -1;
1179 		return;
1180 	}
1181 	if (argc < 3) {
1182 		if (prompt_for_arg(line, sizeof (line), "to-name") < 0) {
1183 			code = -1;
1184 			return;
1185 		}
1186 		makeargv();
1187 		argc = margc;
1188 		argv = margv;
1189 	}
1190 	if (argc < 3)
1191 		goto usage;
1192 	if (command("RNFR %s", argv[1]) == CONTINUE)
1193 		(void) command("RNTO %s", argv[2]);
1194 }
1195 
1196 /*
1197  * Get a directory listing
1198  * of remote files.
1199  */
1200 void
1201 ls(int argc, char *argv[])
1202 {
1203 	char *cmd;
1204 
1205 	if (argc < 2)
1206 		argc++, argv[1] = NULL;
1207 	if (argc < 3)
1208 		argc++, argv[2] = "-";
1209 	if (argc > 3) {
1210 		(void) printf("usage: %s remote-directory local-file\n",
1211 			argv[0]);
1212 		code = -1;
1213 		return;
1214 	}
1215 	if (ls_invokes_NLST) {
1216 		cmd = ((argv[0][0] == 'l' || argv[0][0] == 'n') ?
1217 		    "NLST" : "LIST");
1218 	} else {
1219 		cmd = ((argv[0][0] == 'n') ? "NLST" : "LIST");
1220 	}
1221 	if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
1222 		code = -1;
1223 		return;
1224 	}
1225 	recvrequest(cmd, argv[2], argv[1], "w", 1);
1226 }
1227 
1228 /*
1229  * Get a directory listing
1230  * of multiple remote files.
1231  */
1232 void
1233 mls(int argc, char *argv[])
1234 {
1235 	char *cmd, mode[1], *dest;
1236 	int ointer, i;
1237 	void (*oldintr)();
1238 
1239 	if (argc < 2) {
1240 		if (prompt_for_arg(line, sizeof (line), "remote-files") < 0) {
1241 			code = -1;
1242 			return;
1243 		}
1244 		makeargv();
1245 		argc = margc;
1246 		argv = margv;
1247 	}
1248 	if (argc < 3) {
1249 		if (prompt_for_arg(line, sizeof (line), "local-file") < 0) {
1250 			code = -1;
1251 			return;
1252 		}
1253 		makeargv();
1254 		argc = margc;
1255 		argv = margv;
1256 	}
1257 	if (argc < 3) {
1258 		(void) printf("usage: %s remote-files local-file\n", argv[0]);
1259 		code = -1;
1260 		return;
1261 	}
1262 	dest = argv[argc - 1];
1263 	argv[argc - 1] = NULL;
1264 	if (strcmp(dest, "-") && *dest != '|')
1265 		if (!globulize(&dest) ||
1266 		    !confirm("output to local-file:", dest)) {
1267 			code = -1;
1268 			return;
1269 		}
1270 	cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
1271 	mname = argv[0];
1272 	mflag = 1;
1273 	oldintr = signal(SIGINT, mabort);
1274 	(void) setjmp(jabort);
1275 	for (i = 1; mflag && i < argc-1; ++i) {
1276 		*mode = (i == 1) ? 'w' : 'a';
1277 		recvrequest(cmd, dest, argv[i], mode, 1);
1278 		if (!mflag && fromatty) {
1279 			ointer = interactive;
1280 			interactive = 1;
1281 			if (confirm("Continue with", argv[0])) {
1282 				mflag ++;
1283 			}
1284 			interactive = ointer;
1285 		}
1286 	}
1287 	(void) signal(SIGINT, oldintr);
1288 	mflag = 0;
1289 }
1290 
1291 /*
1292  * Do a shell escape
1293  */
1294 /*ARGSUSED*/
1295 void
1296 shell(int argc, char *argv[])
1297 {
1298 	pid_t pid;
1299 	void (*old1)(), (*old2)();
1300 	char *shellstring, *namep;
1301 	int status;
1302 
1303 	stop_timer();
1304 	old1 = signal(SIGINT, SIG_IGN);
1305 	old2 = signal(SIGQUIT, SIG_IGN);
1306 	if ((pid = fork()) == 0) {
1307 		closefrom(STDERR_FILENO + 1);
1308 		(void) signal(SIGINT, SIG_DFL);
1309 		(void) signal(SIGQUIT, SIG_DFL);
1310 		shellstring = getenv("SHELL");
1311 		if (shellstring == NULL)
1312 			shellstring = "/bin/sh";
1313 		namep = rindex(shellstring, '/');
1314 		if (namep == NULL)
1315 			namep = shellstring;
1316 		if (argc > 1) {
1317 			if (debug) {
1318 				(void) printf("%s -c %s\n", shellstring,
1319 					altarg);
1320 				(void) fflush(stdout);
1321 			}
1322 			execl(shellstring, namep, "-c", altarg, (char *)0);
1323 		} else {
1324 			if (debug) {
1325 				(void) printf("%s\n", shellstring);
1326 				(void) fflush(stdout);
1327 			}
1328 			execl(shellstring, namep, (char *)0);
1329 		}
1330 		perror(shellstring);
1331 		code = -1;
1332 		exit(1);
1333 		}
1334 	if (pid > 0)
1335 		while (wait(&status) != pid)
1336 			;
1337 	(void) signal(SIGINT, old1);
1338 	(void) signal(SIGQUIT, old2);
1339 	reset_timer();
1340 	if (pid == (pid_t)-1) {
1341 		perror("Try again later");
1342 		code = -1;
1343 	} else {
1344 		code = 0;
1345 	}
1346 }
1347 
1348 /*
1349  * Send new user information (re-login)
1350  */
1351 void
1352 user(int argc, char *argv[])
1353 {
1354 	char acct[80];
1355 	int n, aflag = 0;
1356 
1357 	if (argc < 2) {
1358 		if (prompt_for_arg(line, sizeof (line), "username") < 0) {
1359 			code = -1;
1360 			return;
1361 		}
1362 		makeargv();
1363 		argc = margc;
1364 		argv = margv;
1365 	}
1366 	if (argc > 4) {
1367 		(void) printf("usage: %s username [password] [account]\n",
1368 			argv[0]);
1369 		code = -1;
1370 		return;
1371 	}
1372 	if (argv[1] == 0) {
1373 		(void) printf("access for user (nil) denied\n");
1374 		code = -1;
1375 		return;
1376 	}
1377 	n = command("USER %s", argv[1]);
1378 	if (n == CONTINUE) {
1379 		int oldclevel;
1380 		if (argc < 3)
1381 			argv[2] = mygetpass("Password: "), argc++;
1382 		if ((oldclevel = clevel) == PROT_S)
1383 			clevel = PROT_P;
1384 		n = command("PASS %s", argv[2]);
1385 		/* level may have changed */
1386 		if (clevel == PROT_P)
1387 			clevel = oldclevel;
1388 	}
1389 	if (n == CONTINUE) {
1390 		if (argc < 4) {
1391 			(void) printf("Account: "); (void) fflush(stdout);
1392 			stop_timer();
1393 			(void) fgets(acct, sizeof (acct) - 1, stdin);
1394 			reset_timer();
1395 			acct[strlen(acct) - 1] = '\0';
1396 			argv[3] = acct; argc++;
1397 		}
1398 		n = command("ACCT %s", argv[3]);
1399 		aflag++;
1400 	}
1401 	if (n != COMPLETE) {
1402 		(void) fprintf(stdout, "Login failed.\n");
1403 		return;
1404 	}
1405 	if (!aflag && argc == 4) {
1406 		(void) command("ACCT %s", argv[3]);
1407 	}
1408 }
1409 
1410 /*
1411  * Print working directory.
1412  */
1413 /*ARGSUSED*/
1414 void
1415 pwd(int argc, char *argv[])
1416 {
1417 	(void) command("PWD");
1418 }
1419 
1420 /*
1421  * Make a directory.
1422  */
1423 void
1424 makedir(int argc, char *argv[])
1425 {
1426 	if (argc < 2) {
1427 		if (prompt_for_arg(line, sizeof (line), "directory-name") <
1428 		    0) {
1429 			code = -1;
1430 			return;
1431 		}
1432 		makeargv();
1433 		argc = margc;
1434 		argv = margv;
1435 	}
1436 	if (argc < 2) {
1437 		(void) printf("usage: %s directory-name\n", argv[0]);
1438 		code = -1;
1439 		return;
1440 	}
1441 	(void) command("MKD %s", argv[1]);
1442 }
1443 
1444 /*
1445  * Remove a directory.
1446  */
1447 void
1448 removedir(int argc, char *argv[])
1449 {
1450 	if (argc < 2) {
1451 		if (prompt_for_arg(line, sizeof (line), "directory-name") <
1452 		    0) {
1453 			code = -1;
1454 			return;
1455 		}
1456 		makeargv();
1457 		argc = margc;
1458 		argv = margv;
1459 	}
1460 	if (argc < 2) {
1461 		(void) printf("usage: %s directory-name\n", argv[0]);
1462 		code = -1;
1463 		return;
1464 	}
1465 	(void) command("RMD %s", argv[1]);
1466 }
1467 
1468 /*
1469  * Send a line, verbatim, to the remote machine.
1470  */
1471 void
1472 quote(int argc, char *argv[])
1473 {
1474 	int i, n, len;
1475 	char buf[FTPBUFSIZ];
1476 
1477 	if (argc < 2) {
1478 		if (prompt_for_arg(line, sizeof (line),
1479 		    "command line to send") == -1) {
1480 			code = -1;
1481 			return;
1482 		}
1483 		makeargv();
1484 		argc = margc;
1485 		argv = margv;
1486 	}
1487 	if (argc < 2) {
1488 		(void) printf("usage: %s line-to-send\n", argv[0]);
1489 		code = -1;
1490 		return;
1491 	}
1492 	len = snprintf(buf, sizeof (buf), "%s", argv[1]);
1493 	if (len >= 0 && len < sizeof (buf) - 1) {
1494 		for (i = 2; i < argc; i++) {
1495 			n = snprintf(&buf[len], sizeof (buf) - len, " %s",
1496 					argv[i]);
1497 			if (n < 0 || n >= sizeof (buf) - len)
1498 				break;
1499 			len += n;
1500 		}
1501 	}
1502 	if (command("%s", buf) == PRELIM) {
1503 		while (getreply(0) == PRELIM)
1504 			;
1505 	}
1506 }
1507 
1508 /*
1509  * Send a line, verbatim, to the remote machine as a SITE command.
1510  */
1511 void
1512 site(int argc, char *argv[])
1513 {
1514 	int i, n, len;
1515 	char buf[FTPBUFSIZ];
1516 
1517 	if (argc < 2) {
1518 		if (prompt_for_arg(line, sizeof (line),
1519 		    "arguments to SITE command") == -1) {
1520 			code = -1;
1521 			return;
1522 		}
1523 		makeargv();
1524 		argc = margc;
1525 		argv = margv;
1526 	}
1527 	if (argc < 2) {
1528 		(void) printf("usage: %s arg1 [arg2] ...\n", argv[0]);
1529 		code = -1;
1530 		return;
1531 	}
1532 	len = snprintf(buf, sizeof (buf), "%s", argv[1]);
1533 	if (len >= 0 && len < sizeof (buf) - 1) {
1534 		for (i = 2; i < argc; i++) {
1535 			n = snprintf(&buf[len], sizeof (buf) - len, " %s",
1536 					argv[i]);
1537 			if (n < 0 || n >= sizeof (buf) - len)
1538 				break;
1539 			len += n;
1540 		}
1541 	}
1542 	if (command("SITE %s", buf) == PRELIM) {
1543 		while (getreply(0) == PRELIM)
1544 			;
1545 	}
1546 }
1547 
1548 /*
1549  * Ask the other side for help.
1550  */
1551 void
1552 rmthelp(int argc, char *argv[])
1553 {
1554 	int oldverbose = verbose;
1555 
1556 	verbose = 1;
1557 	(void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1558 	verbose = oldverbose;
1559 }
1560 
1561 /*
1562  * Terminate session and exit.
1563  */
1564 /*ARGSUSED*/
1565 void
1566 quit(int argc, char *argv[])
1567 {
1568 	if (connected)
1569 		disconnect(0, NULL);
1570 	pswitch(1);
1571 	if (connected) {
1572 		disconnect(0, NULL);
1573 	}
1574 	exit(0);
1575 }
1576 
1577 /*
1578  * Terminate session, but don't exit.
1579  */
1580 /*ARGSUSED*/
1581 void
1582 disconnect(int argc, char *argv[])
1583 {
1584 	extern FILE *ctrl_in, *ctrl_out;
1585 	extern int data;
1586 
1587 	if (!connected)
1588 		return;
1589 	(void) command("QUIT");
1590 	if (ctrl_in) {
1591 		reset_timer();
1592 		(void) fclose(ctrl_in);
1593 	}
1594 	if (ctrl_out) {
1595 		reset_timer();
1596 		(void) fclose(ctrl_out);
1597 	}
1598 	ctrl_out = ctrl_in = NULL;
1599 	connected = 0;
1600 	data = -1;
1601 	if (!proxy) {
1602 		macnum = 0;
1603 	}
1604 
1605 	auth_type = AUTHTYPE_NONE;
1606 	clevel = dlevel = PROT_C;
1607 	goteof = 0;
1608 }
1609 
1610 static int
1611 confirm(char *cmd, char *file)
1612 {
1613 	char line[FTPBUFSIZ];
1614 
1615 	if (!interactive)
1616 		return (1);
1617 	stop_timer();
1618 	(void) printf("%s %s? ", cmd, file);
1619 	(void) fflush(stdout);
1620 	*line = '\0';
1621 	(void) fgets(line, sizeof (line), stdin);
1622 	reset_timer();
1623 	return (*line != 'n' && *line != 'N');
1624 }
1625 
1626 void
1627 fatal(char *msg)
1628 {
1629 	(void) fprintf(stderr, "ftp: %s\n", msg);
1630 	exit(1);
1631 }
1632 
1633 /*
1634  * Glob a local file name specification with
1635  * the expectation of a single return value.
1636  * Can't control multiple values being expanded
1637  * from the expression, we return only the first.
1638  */
1639 static int
1640 globulize(char **cpp)
1641 {
1642 	char **globbed;
1643 
1644 	if (!doglob)
1645 		return (1);
1646 	globbed = glob(*cpp);
1647 	if (globbed != NULL && *globbed == NULL && globerr == NULL)
1648 		globerr = "No match";
1649 	if (globerr != NULL) {
1650 		(void) printf("%s: %s\n", *cpp, globerr);
1651 		if (globbed)
1652 			blkfree(globbed);
1653 		return (0);
1654 	}
1655 	if (globbed) {
1656 		*cpp = strdup(*globbed);
1657 		blkfree(globbed);
1658 		if (!*cpp)
1659 			return (0);
1660 	}
1661 	return (1);
1662 }
1663 
1664 void
1665 account(int argc, char *argv[])
1666 {
1667 	char acct[50], *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 	} else {
1681 		ap = mygetpass("Account:");
1682 	}
1683 	(void) command("ACCT %s", ap);
1684 }
1685 
1686 /*ARGSUSED*/
1687 static void
1688 proxabort(int sig)
1689 {
1690 	extern int proxy;
1691 
1692 	if (!proxy) {
1693 		pswitch(1);
1694 	}
1695 	if (connected) {
1696 		proxflag = 1;
1697 	} else {
1698 		proxflag = 0;
1699 	}
1700 	pswitch(0);
1701 	longjmp(abortprox, 1);
1702 }
1703 
1704 void
1705 doproxy(int argc, char *argv[])
1706 {
1707 	void (*oldintr)();
1708 	struct cmd *c;
1709 
1710 	if (argc < 2) {
1711 		if (prompt_for_arg(line, sizeof (line), "command") == -1) {
1712 			code = -1;
1713 			return;
1714 		}
1715 		makeargv();
1716 		argc = margc;
1717 		argv = margv;
1718 	}
1719 	if (argc < 2) {
1720 		(void) printf("usage: %s command\n", argv[0]);
1721 		code = -1;
1722 		return;
1723 	}
1724 	c = getcmd(argv[1]);
1725 	if (c == (struct cmd *)-1) {
1726 		(void) printf("?Ambiguous command\n");
1727 		(void) fflush(stdout);
1728 		code = -1;
1729 		return;
1730 	}
1731 	if (c == 0) {
1732 		(void) printf("?Invalid command\n");
1733 		(void) fflush(stdout);
1734 		code = -1;
1735 		return;
1736 	}
1737 	if (!c->c_proxy) {
1738 		(void) printf("?Invalid proxy command\n");
1739 		(void) fflush(stdout);
1740 		code = -1;
1741 		return;
1742 	}
1743 	if (setjmp(abortprox)) {
1744 		code = -1;
1745 		return;
1746 	}
1747 	oldintr = signal(SIGINT, (void (*)())proxabort);
1748 	pswitch(1);
1749 	if (c->c_conn && !connected) {
1750 		(void) printf("Not connected\n");
1751 		(void) fflush(stdout);
1752 		pswitch(0);
1753 		(void) signal(SIGINT, oldintr);
1754 		code = -1;
1755 		return;
1756 	}
1757 	(*c->c_handler)(argc-1, argv+1);
1758 	if (connected) {
1759 		proxflag = 1;
1760 	} else {
1761 		proxflag = 0;
1762 	}
1763 	pswitch(0);
1764 	(void) signal(SIGINT, oldintr);
1765 }
1766 
1767 /*ARGSUSED*/
1768 void
1769 setcase(int argc, char *argv[])
1770 {
1771 	mcase = !mcase;
1772 	(void) printf("Case mapping %s.\n", onoff(mcase));
1773 	code = mcase;
1774 }
1775 
1776 /*ARGSUSED*/
1777 void
1778 setcr(int argc, char *argv[])
1779 {
1780 	crflag = !crflag;
1781 	(void) printf("Carriage Return stripping %s.\n", onoff(crflag));
1782 	code = crflag;
1783 }
1784 
1785 void
1786 setntrans(int argc, char *argv[])
1787 {
1788 	if (argc == 1) {
1789 		ntflag = 0;
1790 		(void) printf("Ntrans off.\n");
1791 		code = ntflag;
1792 		return;
1793 	}
1794 	ntflag++;
1795 	code = ntflag;
1796 	(void) strncpy(ntin, argv[1], 16);
1797 	ntin[16] = '\0';
1798 	if (argc == 2) {
1799 		ntout[0] = '\0';
1800 		return;
1801 	}
1802 	(void) strncpy(ntout, argv[2], 16);
1803 	ntout[16] = '\0';
1804 }
1805 
1806 static char *
1807 dotrans(char *name)
1808 {
1809 	static char new[MAXPATHLEN];
1810 	char *cp1, *cp2 = new;
1811 	int i, ostop, found;
1812 
1813 	for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1814 		;
1815 	for (cp1 = name; *cp1; cp1++) {
1816 		found = 0;
1817 		for (i = 0; *(ntin + i) && i < 16; i++) {
1818 			if (*cp1 == *(ntin + i)) {
1819 				found++;
1820 				if (i < ostop) {
1821 					*cp2++ = *(ntout + i);
1822 				}
1823 				break;
1824 			}
1825 		}
1826 		if (!found) {
1827 			*cp2++ = *cp1;
1828 		}
1829 	}
1830 	*cp2 = '\0';
1831 	return (new);
1832 }
1833 
1834 void
1835 setnmap(int argc, char *argv[])
1836 {
1837 	char *cp;
1838 
1839 	if (argc == 1) {
1840 		mapflag = 0;
1841 		(void) printf("Nmap off.\n");
1842 		code = mapflag;
1843 		return;
1844 	}
1845 	if (argc < 3) {
1846 		if (prompt_for_arg(line, sizeof (line), "mapout") == -1) {
1847 			code = -1;
1848 			return;
1849 		}
1850 		makeargv();
1851 		argc = margc;
1852 		argv = margv;
1853 	}
1854 	if (argc < 3) {
1855 		(void) printf("Usage: %s [mapin mapout]\n", argv[0]);
1856 		code = -1;
1857 		return;
1858 	}
1859 	mapflag = 1;
1860 	code = 1;
1861 	cp = index(altarg, ' ');
1862 	if (proxy) {
1863 		while (*++cp == ' ')
1864 			/* NULL */;
1865 		altarg = cp;
1866 		cp = index(altarg, ' ');
1867 	}
1868 	*cp = '\0';
1869 	(void) strncpy(mapin, altarg, MAXPATHLEN - 1);
1870 	while (*++cp == ' ')
1871 		/* NULL */;
1872 	(void) strncpy(mapout, cp, MAXPATHLEN - 1);
1873 }
1874 
1875 static char *
1876 domap(char *name)
1877 {
1878 	static char new[MAXPATHLEN];
1879 	char *cp1 = name, *cp2 = mapin;
1880 	char *tp[9], *te[9];
1881 	int i, toks[9], toknum, match = 1;
1882 	wchar_t	wc1, wc2;
1883 	int	len1, len2;
1884 
1885 	for (i = 0; i < 9; ++i) {
1886 		toks[i] = 0;
1887 	}
1888 	while (match && *cp1 && *cp2) {
1889 		if ((len1 = mbtowc(&wc1, cp1, MB_CUR_MAX)) <= 0) {
1890 			wc1 = (unsigned char)*cp1;
1891 			len1 = 1;
1892 		}
1893 		cp1 += len1;
1894 		if ((len2 = mbtowc(&wc2, cp2, MB_CUR_MAX)) <= 0) {
1895 			wc2 = (unsigned char)*cp2;
1896 			len2 = 1;
1897 		}
1898 		cp2 += len2;
1899 
1900 		switch (wc2) {
1901 		case '\\':
1902 			if ((len2 = mbtowc(&wc2, cp2, MB_CUR_MAX)) <= 0) {
1903 				wc2 = (unsigned char)*cp2;
1904 				len2 = 1;
1905 			}
1906 			cp2 += len2;
1907 			if (wc2 != wc1)
1908 				match = 0;
1909 			break;
1910 
1911 		case '$':
1912 			if (*cp2 >= '1' && *cp2 <= '9') {
1913 				if ((len2 =
1914 				    mbtowc(&wc2, cp2 + 1, MB_CUR_MAX)) <= 0) {
1915 					wc2 = (unsigned char)*(cp2 + 1);
1916 					len2 = 1;
1917 				}
1918 				if (wc1 != wc2) {
1919 					toks[toknum = *cp2 - '1']++;
1920 					tp[toknum] = cp1 - len1;
1921 					while (*cp1) {
1922 						if ((len1 = mbtowc(&wc1,
1923 						    cp1, MB_CUR_MAX)) <= 0) {
1924 							wc1 =
1925 							    (unsigned char)*cp1;
1926 							len1 = 1;
1927 						}
1928 						cp1 += len1;
1929 						if (wc2 == wc1)
1930 							break;
1931 					}
1932 					if (*cp1 == 0 && wc2 != wc1)
1933 						te[toknum] = cp1;
1934 					else
1935 						te[toknum] = cp1 - len1;
1936 				}
1937 				cp2++;			/* Consume the digit */
1938 				if (wc2)
1939 					cp2 += len2;	/* Consume wide char */
1940 				break;
1941 			}
1942 			/* intentional drop through */
1943 		default:
1944 			if (wc2 != wc1)
1945 				match = 0;
1946 			break;
1947 		}
1948 	}
1949 
1950 	cp1 = new;
1951 	*cp1 = '\0';
1952 	cp2 = mapout;
1953 	while (*cp2) {
1954 		match = 0;
1955 		switch (*cp2) {
1956 		case '\\':
1957 			cp2++;
1958 			if (*cp2) {
1959 				if ((len2 = mblen(cp2, MB_CUR_MAX)) <= 0)
1960 					len2 = 1;
1961 				memcpy(cp1, cp2, len2);
1962 				cp1 += len2;
1963 				cp2 += len2;
1964 			}
1965 			break;
1966 
1967 		case '[':
1968 LOOP:
1969 			cp2++;
1970 			if (*cp2 == '$' && isdigit(*(cp2+1))) {
1971 				if (*++cp2 == '0') {
1972 					char *cp3 = name;
1973 
1974 					while (*cp3) {
1975 						*cp1++ = *cp3++;
1976 					}
1977 					match = 1;
1978 				} else if (toks[toknum = *cp2 - '1']) {
1979 					char *cp3 = tp[toknum];
1980 
1981 					while (cp3 != te[toknum]) {
1982 						*cp1++ = *cp3++;
1983 					}
1984 					match = 1;
1985 				}
1986 			} else {
1987 				while (*cp2 && *cp2 != ',' && *cp2 != ']') {
1988 					if (*cp2 == '\\') {
1989 						cp2++;
1990 						continue;
1991 					}
1992 
1993 					if (*cp2 == '$' && isdigit(*(cp2+1))) {
1994 						if (*++cp2 == '0') {
1995 							char *cp3 = name;
1996 
1997 							while (*cp3)
1998 								*cp1++ = *cp3++;
1999 							continue;
2000 						}
2001 						if (toks[toknum = *cp2 - '1']) {
2002 							char *cp3 = tp[toknum];
2003 
2004 							while (cp3 !=
2005 							    te[toknum])
2006 								*cp1++ = *cp3++;
2007 						}
2008 						continue;
2009 					}
2010 					if (*cp2) {
2011 						if ((len2 =
2012 						    mblen(cp2, MB_CUR_MAX)) <=
2013 						    0) {
2014 							len2 = 1;
2015 						}
2016 						memcpy(cp1, cp2, len2);
2017 						cp1 += len2;
2018 						cp2 += len2;
2019 					}
2020 				}
2021 				if (!*cp2) {
2022 					(void) printf(
2023 						"nmap: unbalanced brackets\n");
2024 					return (name);
2025 				}
2026 				match = 1;
2027 			}
2028 			if (match) {
2029 				while (*cp2 && *cp2 != ']') {
2030 					if (*cp2 == '\\' && *(cp2 + 1)) {
2031 						cp2++;
2032 					}
2033 					if ((len2 = mblen(cp2, MB_CUR_MAX)) <=
2034 					    0)
2035 						len2 = 1;
2036 					cp2 += len2;
2037 				}
2038 				if (!*cp2) {
2039 					(void) printf(
2040 						"nmap: unbalanced brackets\n");
2041 					return (name);
2042 				}
2043 				cp2++;
2044 				break;
2045 			}
2046 			switch (*++cp2) {
2047 				case ',':
2048 					goto LOOP;
2049 				case ']':
2050 					break;
2051 				default:
2052 					cp2--;
2053 					goto LOOP;
2054 			}
2055 			cp2++;
2056 			break;
2057 		case '$':
2058 			if (isdigit(*(cp2 + 1))) {
2059 				if (*++cp2 == '0') {
2060 					char *cp3 = name;
2061 
2062 					while (*cp3) {
2063 						*cp1++ = *cp3++;
2064 					}
2065 				} else if (toks[toknum = *cp2 - '1']) {
2066 					char *cp3 = tp[toknum];
2067 
2068 					while (cp3 != te[toknum]) {
2069 						*cp1++ = *cp3++;
2070 					}
2071 				}
2072 				cp2++;
2073 				break;
2074 			}
2075 			/* intentional drop through */
2076 		default:
2077 			if ((len2 = mblen(cp2, MB_CUR_MAX)) <= 0)
2078 				len2 = 1;
2079 			memcpy(cp1, cp2, len2);
2080 			cp1 += len2;
2081 			cp2 += len2;
2082 			break;
2083 		}
2084 	}
2085 	*cp1 = '\0';
2086 	if (!*new) {
2087 		return (name);
2088 	}
2089 	return (new);
2090 }
2091 
2092 /*ARGSUSED*/
2093 void
2094 setsunique(int argc, char *argv[])
2095 {
2096 	sunique = !sunique;
2097 	(void) printf("Store unique %s.\n", onoff(sunique));
2098 	code = sunique;
2099 }
2100 
2101 /*ARGSUSED*/
2102 void
2103 setrunique(int argc, char *argv[])
2104 {
2105 	runique = !runique;
2106 	(void) printf("Receive unique %s.\n", onoff(runique));
2107 	code = runique;
2108 }
2109 
2110 /*ARGSUSED*/
2111 void
2112 setpassive(int argc, char *argv[])
2113 {
2114 	passivemode = !passivemode;
2115 	(void) printf("Passive mode %s.\n", onoff(passivemode));
2116 	code = passivemode;
2117 }
2118 
2119 void
2120 settcpwindow(int argc, char *argv[])
2121 {
2122 	int owindowsize = tcpwindowsize;
2123 
2124 	if (argc > 2) {
2125 		(void) printf("usage: %s [size]\n", argv[0]);
2126 		code = -1;
2127 		return;
2128 	}
2129 	if (argc == 2) {
2130 		int window;
2131 		char *endp;
2132 
2133 		errno = 0;
2134 		window = (int)strtol(argv[1], &endp, 10);
2135 		if (errno || window < 0 || *endp != '\0')
2136 			(void) printf("%s: Invalid size `%s'\n",
2137 				argv[0], argv[1]);
2138 		else
2139 			tcpwindowsize = window;
2140 	}
2141 	if (tcpwindowsize == 0) {
2142 		if (owindowsize == 0)
2143 			(void) printf("No TCP window size defined\n");
2144 		else
2145 			(void) printf("TCP window size cleared\n");
2146 	} else
2147 		(void) printf("TCP window size is set to %d\n", tcpwindowsize);
2148 }
2149 
2150 /* change directory to parent directory */
2151 /*ARGSUSED*/
2152 void
2153 cdup(int argc, char *argv[])
2154 {
2155 	(void) command("CDUP");
2156 }
2157 
2158 void
2159 macdef(int argc, char *argv[])
2160 {
2161 	char *tmp;
2162 	int c;
2163 
2164 	if (macnum == 16) {
2165 		(void) printf("Limit of 16 macros have already been defined\n");
2166 		code = -1;
2167 		return;
2168 	}
2169 	if (argc < 2) {
2170 		if (prompt_for_arg(line, sizeof (line), "macro name") == -1) {
2171 			code = -1;
2172 			return;
2173 		}
2174 		makeargv();
2175 		argc = margc;
2176 		argv = margv;
2177 	}
2178 	if (argc != 2) {
2179 		(void) printf("Usage: %s macro_name\n", argv[0]);
2180 		code = -1;
2181 		return;
2182 	}
2183 	if (interactive) {
2184 		(void) printf("Enter macro line by line, terminating "
2185 		    "it with a null line\n");
2186 	}
2187 	(void) strncpy(macros[macnum].mac_name, argv[1], 8);
2188 	if (macnum == 0) {
2189 		macros[macnum].mac_start = macbuf;
2190 	} else {
2191 		macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2192 	}
2193 	tmp = macros[macnum].mac_start;
2194 	while (tmp != macbuf+4096) {
2195 		if ((c = getchar()) == EOF) {
2196 			(void) printf("macdef:end of file encountered\n");
2197 			code = -1;
2198 			return;
2199 		}
2200 		if ((*tmp = c) == '\n') {
2201 			if (tmp == macros[macnum].mac_start) {
2202 				macros[macnum++].mac_end = tmp;
2203 				code = 0;
2204 				return;
2205 			}
2206 			if (*(tmp-1) == '\0') {
2207 				macros[macnum++].mac_end = tmp - 1;
2208 				code = 0;
2209 				return;
2210 			}
2211 			*tmp = '\0';
2212 		}
2213 		tmp++;
2214 	}
2215 	for (;;) {
2216 		while ((c = getchar()) != '\n' && c != EOF)
2217 			/* NULL */;
2218 		if (c == EOF || getchar() == '\n') {
2219 			(void) printf(
2220 				"Macro not defined - 4k buffer exceeded\n");
2221 			code = -1;
2222 			return;
2223 		}
2224 	}
2225 }
2226 
2227 /*
2228  * The p_name strings are for the getlevel and setlevel commands.
2229  * The name strings for printing are in the arpa/ftp.h file in the
2230  * protnames[] array of strings.
2231  */
2232 static	struct	levels {
2233 	char	*p_name;
2234 	char	*p_mode;
2235 	int	p_level;
2236 } levels[] = {
2237 	{ "clear",	"C",	PROT_C },
2238 	{ "safe",	"S",	PROT_S },
2239 	{ "private",	"P",	PROT_P },
2240 	NULL
2241 };
2242 
2243 /*
2244  * Return a pointer to a string which is the readable version of the
2245  * protection level, or NULL if the input level is not found.
2246  */
2247 static char *
2248 getlevel(int level)
2249 {
2250 	struct levels *p;
2251 
2252 	for (p = levels; (p != NULL) && (p->p_level != level); p++)
2253 		;
2254 	return (p ? p->p_name : NULL);
2255 }
2256 
2257 static char *plevel[] = {
2258 	"protect",
2259 	"",
2260 	NULL
2261 };
2262 
2263 /*
2264  * Set control channel protection level.
2265  */
2266 void
2267 setclevel(int argc, char *argv[])
2268 {
2269 	struct levels *p;
2270 	char *levelp;
2271 	int comret;
2272 
2273 	if (argc > 2) {
2274 		char *sep;
2275 
2276 		(void) printf("usage: %s [", argv[0]);
2277 		sep = " ";
2278 		for (p = levels; p->p_name; p++) {
2279 			(void) printf("%s%s", sep, p->p_name);
2280 			if (*sep == ' ')
2281 				sep = " | ";
2282 		}
2283 		(void) printf(" ]\n");
2284 		code = -1;
2285 		return;
2286 	}
2287 	if (argc < 2) {
2288 		levelp = getlevel(clevel);
2289 		(void) printf("Using %s protection level for commands.\n",
2290 			levelp ? levelp : "<unknown>");
2291 		code = 0;
2292 		return;
2293 	}
2294 	for (p = levels; (p != NULL) && (p->p_name); p++)
2295 		if (strcmp(argv[1], p->p_name) == 0)
2296 			break;
2297 	if (p->p_name == 0) {
2298 		(void) printf("%s: unknown protection level\n", argv[1]);
2299 		code = -1;
2300 		return;
2301 	}
2302 	if (auth_type == AUTHTYPE_NONE) {
2303 		if (strcmp(p->p_name, "clear"))
2304 			(void) printf("Cannot set protection level to %s\n",
2305 				argv[1]);
2306 		return;
2307 	}
2308 	if (strcmp(p->p_name, "clear") == 0) {
2309 		comret = command("CCC");
2310 		if (comret == COMPLETE)
2311 			clevel = PROT_C;
2312 		return;
2313 	}
2314 	clevel = p->p_level;
2315 	(void) printf("Control channel protection level set to %s.\n",
2316 		p->p_name);
2317 }
2318 
2319 /*
2320  * Set data channel protection level.
2321  */
2322 void
2323 setdlevel(int argc, char *argv[])
2324 {
2325 	struct levels *p;
2326 	int comret;
2327 
2328 	if (argc != 2) {
2329 		char *sep;
2330 
2331 		(void) printf("usage: %s [", argv[0]);
2332 		sep = " ";
2333 		for (p = levels; p->p_name; p++) {
2334 			(void) printf("%s%s", sep, p->p_name);
2335 			if (*sep == ' ')
2336 				sep = " | ";
2337 		}
2338 		(void) printf(" ]\n");
2339 		code = -1;
2340 		return;
2341 	}
2342 	for (p = levels; p->p_name; p++)
2343 		if (strcmp(argv[1], p->p_name) == 0)
2344 			break;
2345 	if (p->p_name == 0) {
2346 		(void) printf("%s: unknown protection level\n", argv[1]);
2347 		code = -1;
2348 		return;
2349 	}
2350 	if (auth_type == AUTHTYPE_NONE) {
2351 		if (strcmp(p->p_name, "clear"))
2352 			(void) printf("Cannot set protection level to %s\n",
2353 				argv[1]);
2354 		return;
2355 	}
2356 	/* Start with a PBSZ of 1 meg */
2357 	if (p->p_level != PROT_C)
2358 		setpbsz(1<<20);
2359 	comret = command("PROT %s", p->p_mode);
2360 	if (comret == COMPLETE)
2361 		dlevel = p->p_level;
2362 }
2363 
2364 /*
2365  * Set clear command protection level.
2366  */
2367 /* VARARGS */
2368 void
2369 ccc(int argc, char *argv[])
2370 {
2371 	plevel[1] = "clear";
2372 	setclevel(2, plevel);
2373 }
2374 
2375 /*
2376  * Set clear data protection level.
2377  */
2378 /* VARARGS */
2379 void
2380 setclear(int argc, char *argv[])
2381 {
2382 	plevel[1] = "clear";
2383 	setdlevel(2, plevel);
2384 }
2385 
2386 /*
2387  * Set safe data protection level.
2388  */
2389 /* VARARGS */
2390 void
2391 setsafe(int argc, char *argv[])
2392 {
2393 	plevel[1] = "safe";
2394 	setdlevel(2, plevel);
2395 }
2396 
2397 /*
2398  * Set private data protection level.
2399  */
2400 /* VARARGS */
2401 void
2402 setprivate(int argc, char *argv[])
2403 {
2404 	plevel[1] = "private";
2405 	setdlevel(2, plevel);
2406 }
2407 
2408 /*
2409  * Set mechanism type
2410  */
2411 void
2412 setmech(int  argc, char *argv[])
2413 {
2414 	char	tempmech[MECH_SZ];
2415 
2416 	if (argc < 2) {
2417 		if (prompt_for_arg(line, sizeof (line), "mech-type") == -1) {
2418 			code = -1;
2419 			return;
2420 		}
2421 		makeargv();
2422 		argc = margc;
2423 		argv = margv;
2424 	}
2425 
2426 	if (argc != 2) {
2427 		(void) printf("usage: %s [ mechanism type ]\n", argv[0]);
2428 		code = -1;
2429 		return;
2430 	}
2431 
2432 	if ((strlcpy(tempmech, argv[1], MECH_SZ) >= MECH_SZ) ||
2433 		__gss_mech_to_oid(tempmech, (gss_OID*)&mechoid) !=
2434 			GSS_S_COMPLETE) {
2435 		(void) printf("%s: %s: not a valid security mechanism\n",
2436 			argv[0], tempmech);
2437 		code = -1;
2438 		return;
2439 	} else {
2440 		(void) strlcpy(mechstr, tempmech, MECH_SZ);
2441 		(void) printf("Using %s mechanism type\n", mechstr);
2442 		code = 0;
2443 		return;
2444 	}
2445 }
2446