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