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