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