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