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