"Fossies" - the Fresh Open Source Software Archive

Member "tnftp-20200705/src/cmds.c" (4 Jul 2020, 58407 Bytes) of package /linux/privat/tnftp-20200705.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cmds.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 20151004_vs_20200705.

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