"Fossies" - the Fresh Open Source Software Archive

Member "tnftp-20200705/src/main.c" (4 Jul 2020, 24643 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 "main.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 20151004_vs_20200705.

    1 /*  $NetBSD: main.c,v 1.21 2020/07/04 09:59:07 lukem Exp $  */
    2 /*  from    NetBSD: main.c,v 1.126 2019/02/04 04:09:13 mrg Exp  */
    3 
    4 /*-
    5  * Copyright (c) 1996-2015 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  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   30  * POSSIBILITY OF SUCH DAMAGE.
   31  */
   32 
   33 /*
   34  * Copyright (c) 1985, 1989, 1993, 1994
   35  *  The Regents of the University of California.  All rights reserved.
   36  *
   37  * Redistribution and use in source and binary forms, with or without
   38  * modification, are permitted provided that the following conditions
   39  * are met:
   40  * 1. Redistributions of source code must retain the above copyright
   41  *    notice, this list of conditions and the following disclaimer.
   42  * 2. Redistributions in binary form must reproduce the above copyright
   43  *    notice, this list of conditions and the following disclaimer in the
   44  *    documentation and/or other materials provided with the distribution.
   45  * 3. Neither the name of the University nor the names of its contributors
   46  *    may be used to endorse or promote products derived from this software
   47  *    without specific prior written permission.
   48  *
   49  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   50  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   51  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   52  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   53  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   54  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   55  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   56  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   57  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   58  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   59  * SUCH DAMAGE.
   60  */
   61 
   62 /*
   63  * Copyright (C) 1997 and 1998 WIDE Project.
   64  * All rights reserved.
   65  *
   66  * Redistribution and use in source and binary forms, with or without
   67  * modification, are permitted provided that the following conditions
   68  * are met:
   69  * 1. Redistributions of source code must retain the above copyright
   70  *    notice, this list of conditions and the following disclaimer.
   71  * 2. Redistributions in binary form must reproduce the above copyright
   72  *    notice, this list of conditions and the following disclaimer in the
   73  *    documentation and/or other materials provided with the distribution.
   74  * 3. Neither the name of the project nor the names of its contributors
   75  *    may be used to endorse or promote products derived from this software
   76  *    without specific prior written permission.
   77  *
   78  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
   79  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   80  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   81  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
   82  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   83  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   84  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   85  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   86  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   87  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   88  * SUCH DAMAGE.
   89  */
   90 
   91 #include "tnftp.h"
   92 
   93 #if 0   /* tnftp */
   94 
   95 #include <sys/cdefs.h>
   96 #ifndef lint
   97 __COPYRIGHT("@(#) Copyright (c) 1985, 1989, 1993, 1994\
   98  The Regents of the University of California.  All rights reserved.\
   99   Copyright 1996-2015 The NetBSD Foundation, Inc.  All rights reserved");
  100 #endif /* not lint */
  101 
  102 #ifndef lint
  103 #if 0
  104 static char sccsid[] = "@(#)main.c  8.6 (Berkeley) 10/9/94";
  105 #else
  106 __RCSID(" NetBSD: main.c,v 1.126 2019/02/04 04:09:13 mrg Exp  ");
  107 #endif
  108 #endif /* not lint */
  109 
  110 /*
  111  * FTP User Program -- Command Interface.
  112  */
  113 #include <sys/types.h>
  114 #include <sys/socket.h>
  115 
  116 #include <err.h>
  117 #include <errno.h>
  118 #include <netdb.h>
  119 #include <paths.h>
  120 #include <pwd.h>
  121 #include <signal.h>
  122 #include <stdio.h>
  123 #include <stdlib.h>
  124 #include <string.h>
  125 #include <time.h>
  126 #include <unistd.h>
  127 #include <locale.h>
  128 
  129 #endif  /* tnftp */
  130 
  131 #define GLOBAL      /* force GLOBAL decls in ftp_var.h to be declared */
  132 #include "ftp_var.h"
  133 
  134 #define FTP_PROXY   "ftp_proxy" /* env var with FTP proxy location */
  135 #define HTTP_PROXY  "http_proxy"    /* env var with HTTP proxy location */
  136 #define HTTPS_PROXY "https_proxy"   /* env var with HTTPS proxy location */
  137 #define NO_PROXY    "no_proxy"  /* env var with list of non-proxied
  138                      * hosts, comma or space separated */
  139 
  140 __dead static void  usage(void);
  141 static void setupoption(const char *, const char *, const char *);
  142 
  143 int
  144 main(int volatile argc, char **volatile argv)
  145 {
  146     int ch, rval;
  147     struct passwd *pw;
  148     char *cp, *ep, *anonpass, *upload_path, *src_addr;
  149     const char *anonuser;
  150     int dumbterm, isupload;
  151     size_t len;
  152 
  153     tzset();
  154 #if defined(HAVE_SETLOCALE)
  155     setlocale(LC_ALL, "");
  156 #endif
  157     setprogname(argv[0]);
  158 
  159     sigint_raised = 0;
  160 
  161     ftpport = "ftp";
  162     httpport = "http";
  163 #ifdef WITH_SSL
  164     httpsport = "https";
  165 #endif
  166     gateport = NULL;
  167     cp = getenv("FTPSERVERPORT");
  168     if (cp != NULL)
  169         gateport = cp;
  170     else
  171         gateport = "ftpgate";
  172     doglob = 1;
  173     interactive = 1;
  174     autologin = 1;
  175     passivemode = 1;
  176     activefallback = 1;
  177     preserve = 1;
  178     verbose = 0;
  179     progress = 0;
  180     gatemode = 0;
  181     data = -1;
  182     outfile = NULL;
  183     restartautofetch = 0;
  184 #ifndef NO_EDITCOMPLETE
  185     editing = 0;
  186     el = NULL;
  187     hist = NULL;
  188 #endif
  189     bytes = 0;
  190     mark = HASHBYTES;
  191     rate_get = 0;
  192     rate_get_incr = DEFAULTINCR;
  193     rate_put = 0;
  194     rate_put_incr = DEFAULTINCR;
  195 #ifdef INET6
  196     epsv4 = 1;
  197     epsv6 = 1;  
  198 #else
  199     epsv4 = 0;
  200     epsv6 = 0;  
  201 #endif
  202     epsv4bad = 0;
  203     epsv6bad = 0;
  204     src_addr = NULL;
  205     upload_path = NULL;
  206     isupload = 0;
  207     reply_callback = NULL;
  208 #ifdef INET6
  209     family = AF_UNSPEC;
  210 #else
  211     family = AF_INET;   /* force AF_INET if no INET6 support */
  212 #endif
  213 
  214     netrc[0] = '\0';
  215     cp = getenv("NETRC");
  216     if (cp != NULL && strlcpy(netrc, cp, sizeof(netrc)) >= sizeof(netrc))
  217         errx(1, "$NETRC `%s': %s", cp, strerror(ENAMETOOLONG));
  218 
  219     marg_sl = ftp_sl_init();
  220     if ((tmpdir = getenv("TMPDIR")) == NULL)
  221         tmpdir = _PATH_TMP;
  222 
  223     /* Set default operation mode based on FTPMODE environment variable */
  224     if ((cp = getenv("FTPMODE")) != NULL) {
  225         if (strcasecmp(cp, "passive") == 0) {
  226             passivemode = 1;
  227             activefallback = 0;
  228         } else if (strcasecmp(cp, "active") == 0) {
  229             passivemode = 0;
  230             activefallback = 0;
  231         } else if (strcasecmp(cp, "gate") == 0) {
  232             gatemode = 1;
  233         } else if (strcasecmp(cp, "auto") == 0) {
  234             passivemode = 1;
  235             activefallback = 1;
  236         } else
  237             warnx("Unknown $FTPMODE `%s'; using defaults", cp);
  238     }
  239 
  240     if (strcmp(getprogname(), "pftp") == 0) {
  241         passivemode = 1;
  242         activefallback = 0;
  243     } else if (strcmp(getprogname(), "gate-ftp") == 0)
  244         gatemode = 1;
  245 
  246     gateserver = getenv("FTPSERVER");
  247     if (gateserver == NULL || *gateserver == '\0')
  248         gateserver = GATE_SERVER;
  249     if (gatemode) {
  250         if (*gateserver == '\0') {
  251             warnx(
  252 "Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp");
  253             gatemode = 0;
  254         }
  255     }
  256 
  257     cp = getenv("TERM");
  258     if (cp == NULL || strcmp(cp, "dumb") == 0)
  259         dumbterm = 1;
  260     else
  261         dumbterm = 0;
  262     fromatty = isatty(fileno(stdin));
  263     ttyout = stdout;
  264     if (isatty(fileno(ttyout))) {
  265         verbose = 1;        /* verbose if to a tty */
  266         if (! dumbterm) {
  267 #ifndef NO_EDITCOMPLETE
  268             if (fromatty)   /* editing mode on if tty is usable */
  269                 editing = 1;
  270 #endif
  271 #ifndef NO_PROGRESS
  272             if (foregroundproc())
  273                 progress = 1;   /* progress bar on if fg */
  274 #endif
  275         }
  276     }
  277 
  278     while ((ch = getopt(argc, argv, "46AadefginN:o:pP:q:r:Rs:tT:u:vVx:")) != -1) {
  279         switch (ch) {
  280         case '4':
  281             family = AF_INET;
  282             break;
  283 
  284         case '6':
  285 #ifdef INET6
  286             family = AF_INET6;
  287 #else
  288             warnx("INET6 support is not available; ignoring -6");
  289 #endif
  290             break;
  291 
  292         case 'A':
  293             activefallback = 0;
  294             passivemode = 0;
  295             break;
  296 
  297         case 'a':
  298             anonftp = 1;
  299             break;
  300 
  301         case 'd':
  302             options |= SO_DEBUG;
  303             ftp_debug++;
  304             break;
  305 
  306         case 'e':
  307 #ifndef NO_EDITCOMPLETE
  308             editing = 0;
  309 #endif
  310             break;
  311 
  312         case 'f':
  313             flushcache = 1;
  314             break;
  315 
  316         case 'g':
  317             doglob = 0;
  318             break;
  319 
  320         case 'i':
  321             interactive = 0;
  322             break;
  323 
  324         case 'n':
  325             autologin = 0;
  326             break;
  327 
  328         case 'N':
  329             if (strlcpy(netrc, optarg, sizeof(netrc))
  330                 >= sizeof(netrc))
  331                 errx(1, "%s: %s", optarg,
  332                     strerror(ENAMETOOLONG));
  333             break;
  334 
  335         case 'o':
  336             outfile = ftp_strdup(optarg);
  337             if (strcmp(outfile, "-") == 0)
  338                 ttyout = stderr;
  339             break;
  340 
  341         case 'p':
  342             passivemode = 1;
  343             activefallback = 0;
  344             break;
  345 
  346         case 'P':
  347             ftpport = optarg;
  348             break;
  349 
  350         case 'q':
  351             quit_time = strtol(optarg, &ep, 10);
  352             if (quit_time < 1 || *ep != '\0')
  353                 errx(1, "Bad quit value: %s", optarg);
  354             break;
  355 
  356         case 'r':
  357             retry_connect = strtol(optarg, &ep, 10);
  358             if (retry_connect < 1 || *ep != '\0')
  359                 errx(1, "Bad retry value: %s", optarg);
  360             break;
  361 
  362         case 'R':
  363             restartautofetch = 1;
  364             break;
  365 
  366         case 's':
  367             src_addr = optarg;
  368             break;
  369 
  370         case 't':
  371             trace = 1;
  372             break;
  373 
  374         case 'T':
  375         {
  376             int targc;
  377             char *targv[6], *oac;
  378             char cmdbuf[MAX_C_NAME];
  379 
  380                 /* look for `dir,max[,incr]' */
  381             targc = 0;
  382             (void)strlcpy(cmdbuf, "-T", sizeof(cmdbuf));
  383             targv[targc++] = cmdbuf;
  384             oac = ftp_strdup(optarg);
  385 
  386             while ((cp = strsep(&oac, ",")) != NULL) {
  387                 if (*cp == '\0') {
  388                     warnx("Bad throttle value `%s'",
  389                         optarg);
  390                     usage();
  391                     /* NOTREACHED */
  392                 }
  393                 targv[targc++] = cp;
  394                 if (targc >= 5)
  395                     break;
  396             }
  397             if (parserate(targc, targv, 1) == -1)
  398                 usage();
  399             free(oac);
  400             break;
  401         }
  402 
  403         case 'u':
  404         {
  405             isupload = 1;
  406             interactive = 0;
  407             upload_path = ftp_strdup(optarg);
  408 
  409             break;
  410         }
  411 
  412         case 'v':
  413             progress = verbose = 1;
  414             break;
  415 
  416         case 'V':
  417             progress = verbose = 0;
  418             break;
  419 
  420         case 'x':
  421             sndbuf_size = strsuftoi(optarg);
  422             if (sndbuf_size < 1)
  423                 errx(1, "Bad xferbuf value: %s", optarg);
  424             rcvbuf_size = sndbuf_size;
  425             break;
  426 
  427         default:
  428             usage();
  429         }
  430     }
  431             /* set line buffering on ttyout */
  432     setvbuf(ttyout, NULL, _IOLBF, 0);
  433     argc -= optind;
  434     argv += optind;
  435 
  436     cpend = 0;  /* no pending replies */
  437     proxy = 0;  /* proxy not active */
  438     crflag = 1; /* strip c.r. on ascii gets */
  439     sendport = -1;  /* not using ports */
  440 
  441     if (src_addr != NULL) {
  442         struct addrinfo hints;
  443         int error;
  444 
  445         memset(&hints, 0, sizeof(hints));
  446         hints.ai_family = family;
  447         hints.ai_socktype = SOCK_STREAM;
  448         hints.ai_flags = AI_PASSIVE;
  449         error = getaddrinfo(src_addr, NULL, &hints, &bindai);
  450         if (error) {
  451                 errx(1, "Can't lookup `%s': %s", src_addr,
  452                 (error == EAI_SYSTEM) ? strerror(errno)
  453                           : gai_strerror(error));
  454         }
  455     }
  456 
  457     /*
  458      * Cache the user name and home directory.
  459      */
  460     localhome = NULL;
  461     localname = NULL;
  462     anonuser = "anonymous";
  463     cp = getenv("HOME");
  464     if (! EMPTYSTRING(cp))
  465         localhome = ftp_strdup(cp);
  466     pw = NULL;
  467     cp = getlogin();
  468     if (cp != NULL)
  469         pw = getpwnam(cp);
  470     if (pw == NULL)
  471         pw = getpwuid(getuid());
  472     if (pw != NULL) {
  473         if (localhome == NULL && !EMPTYSTRING(pw->pw_dir))
  474             localhome = ftp_strdup(pw->pw_dir);
  475         localname = ftp_strdup(pw->pw_name);
  476     }
  477     if (netrc[0] == '\0' && localhome != NULL) {
  478         if (strlcpy(netrc, localhome, sizeof(netrc)) >= sizeof(netrc) ||
  479             strlcat(netrc, "/.netrc", sizeof(netrc)) >= sizeof(netrc)) {
  480             warnx("%s/.netrc: %s", localhome,
  481                 strerror(ENAMETOOLONG));
  482             netrc[0] = '\0';
  483         }
  484     }
  485     if (localhome == NULL)
  486         localhome = ftp_strdup("/");
  487 
  488     /*
  489      * Every anonymous FTP server I've encountered will accept the
  490      * string "username@", and will append the hostname itself. We
  491      * do this by default since many servers are picky about not
  492      * having a FQDN in the anonymous password.
  493      * - thorpej@NetBSD.org
  494      */
  495     len = strlen(anonuser) + 2;
  496     anonpass = ftp_malloc(len);
  497     (void)strlcpy(anonpass, anonuser, len);
  498     (void)strlcat(anonpass, "@",      len);
  499 
  500             /*
  501              * set all the defaults for options defined in
  502              * struct option optiontab[]  declared in cmdtab.c
  503              */
  504     setupoption("anonpass",     getenv("FTPANONPASS"),  anonpass);
  505     setupoption("ftp_proxy",    getenv(FTP_PROXY),  "");
  506     setupoption("http_proxy",   getenv(HTTP_PROXY), "");
  507     setupoption("https_proxy",  getenv(HTTPS_PROXY),    "");
  508     setupoption("no_proxy",     getenv(NO_PROXY),   "");
  509     setupoption("pager",        getenv("PAGER"),    DEFAULTPAGER);
  510     setupoption("prompt",       getenv("FTPPROMPT"),    DEFAULTPROMPT);
  511     setupoption("rprompt",      getenv("FTPRPROMPT"),   DEFAULTRPROMPT);
  512 
  513     free(anonpass);
  514 
  515     setttywidth(0);
  516 #ifdef SIGINFO
  517     (void)xsignal(SIGINFO, psummary);
  518 #endif
  519     (void)xsignal(SIGQUIT, psummary);
  520     (void)xsignal(SIGUSR1, crankrate);
  521     (void)xsignal(SIGUSR2, crankrate);
  522     (void)xsignal(SIGWINCH, setttywidth);
  523 
  524     if (argc > 0) {
  525         if (isupload) {
  526             rval = auto_put(argc, argv, upload_path);
  527  sigint_or_rval_exit:
  528             if (sigint_raised) {
  529                 (void)xsignal(SIGINT, SIG_DFL);
  530                 raise(SIGINT);
  531             }
  532             exit(rval);
  533         } else if (strchr(argv[0], ':') != NULL
  534                 && ! isipv6addr(argv[0])) {
  535             rval = auto_fetch(argc, argv);
  536             if (rval >= 0)      /* -1 == connected and cd-ed */
  537                 goto sigint_or_rval_exit;
  538         } else {
  539             char *xargv[4], *uuser, *host;
  540             char cmdbuf[MAXPATHLEN];
  541 
  542             if ((rval = sigsetjmp(toplevel, 1)))
  543                 goto sigint_or_rval_exit;
  544             (void)xsignal(SIGINT, intr);
  545             (void)xsignal(SIGPIPE, lostpeer);
  546             uuser = NULL;
  547             host = argv[0];
  548             cp = strchr(host, '@');
  549             if (cp) {
  550                 *cp = '\0';
  551                 uuser = host;
  552                 host = cp + 1;
  553             }
  554             (void)strlcpy(cmdbuf, getprogname(), sizeof(cmdbuf));
  555             xargv[0] = cmdbuf;
  556             xargv[1] = host;
  557             xargv[2] = argv[1];
  558             xargv[3] = NULL;
  559             do {
  560                 int oautologin;
  561 
  562                 oautologin = autologin;
  563                 if (uuser != NULL) {
  564                     anonftp = 0;
  565                     autologin = 0;
  566                 }
  567                 setpeer(argc+1, xargv);
  568                 autologin = oautologin;
  569                 if (connected == 1 && uuser != NULL)
  570                     (void)ftp_login(host, uuser, NULL);
  571                 if (!retry_connect)
  572                     break;
  573                 if (!connected) {
  574                     macnum = 0;
  575                     fprintf(ttyout,
  576                         "Retrying in %d seconds...\n",
  577                         retry_connect);
  578                     sleep(retry_connect);
  579                 }
  580             } while (!connected);
  581             retry_connect = 0; /* connected, stop hiding msgs */
  582         }
  583     }
  584     if (isupload)
  585         usage();
  586 
  587 #ifndef NO_EDITCOMPLETE
  588     controlediting();
  589 #endif /* !NO_EDITCOMPLETE */
  590 
  591     (void)sigsetjmp(toplevel, 1);
  592     (void)xsignal(SIGINT, intr);
  593     (void)xsignal(SIGPIPE, lostpeer);
  594     for (;;)
  595         cmdscanner();
  596 }
  597 
  598 /*
  599  * Generate a prompt
  600  */
  601 char *
  602 prompt(void)
  603 {
  604     static char **promptopt;
  605     static char   buf[MAXPATHLEN];
  606 
  607     if (promptopt == NULL) {
  608         struct option *o;
  609 
  610         o = getoption("prompt");
  611         if (o == NULL)
  612             errx(1, "prompt: no such option `prompt'");
  613         promptopt = &(o->value);
  614     }
  615     formatbuf(buf, sizeof(buf), *promptopt ? *promptopt : DEFAULTPROMPT);
  616     return (buf);
  617 }
  618 
  619 /*
  620  * Generate an rprompt
  621  */
  622 char *
  623 rprompt(void)
  624 {
  625     static char **rpromptopt;
  626     static char   buf[MAXPATHLEN];
  627 
  628     if (rpromptopt == NULL) {
  629         struct option *o;
  630 
  631         o = getoption("rprompt");
  632         if (o == NULL)
  633             errx(1, "rprompt: no such option `rprompt'");
  634         rpromptopt = &(o->value);
  635     }
  636     formatbuf(buf, sizeof(buf), *rpromptopt ? *rpromptopt : DEFAULTRPROMPT);
  637     return (buf);
  638 }
  639 
  640 /*
  641  * Command parser.
  642  */
  643 void
  644 cmdscanner(void)
  645 {
  646     struct cmd  *c;
  647     char        *p;
  648 #ifndef NO_EDITCOMPLETE
  649     int      ch;
  650     size_t       num;
  651 #endif
  652     int      len;
  653     char         cmdbuf[MAX_C_NAME];
  654 
  655     for (;;) {
  656 #ifndef NO_EDITCOMPLETE
  657         if (!editing) {
  658 #endif /* !NO_EDITCOMPLETE */
  659             if (fromatty) {
  660                 fputs(prompt(), ttyout);
  661                 p = rprompt();
  662                 if (*p)
  663                     fprintf(ttyout, "%s ", p);
  664             }
  665             (void)fflush(ttyout);
  666             len = get_line(stdin, line, sizeof(line), NULL);
  667             switch (len) {
  668             case -1:    /* EOF */
  669             case -2:    /* error */
  670                 if (fromatty)
  671                     putc('\n', ttyout);
  672                 justquit();
  673                 /* NOTREACHED */
  674             case -3:    /* too long; try again */
  675                 fputs("Sorry, input line is too long.\n",
  676                     ttyout);
  677                 continue;
  678             case 0:     /* empty; try again */
  679                 continue;
  680             default:    /* all ok */
  681                 break;
  682             }
  683 #ifndef NO_EDITCOMPLETE
  684         } else {
  685             const char *buf;
  686             HistEvent ev;
  687             cursor_pos = NULL;
  688 
  689             buf = el_gets(el, &ch);
  690             num = ch;
  691             if (buf == NULL || num == 0) {
  692                 if (fromatty)
  693                     putc('\n', ttyout);
  694                 justquit();
  695             }
  696             if (num >= sizeof(line)) {
  697                 fputs("Sorry, input line is too long.\n",
  698                     ttyout);
  699                 break;
  700             }
  701             memcpy(line, buf, num);
  702             if (line[--num] == '\n') {
  703                 line[num] = '\0';
  704                 if (num == 0)
  705                     break;
  706             }
  707             history(hist, &ev, H_ENTER, buf);
  708         }
  709 #endif /* !NO_EDITCOMPLETE */
  710 
  711         makeargv();
  712         if (margc == 0)
  713             continue;
  714         c = getcmd(margv[0]);
  715         if (c == (struct cmd *)-1) {
  716             fputs("?Ambiguous command.\n", ttyout);
  717             continue;
  718         }
  719         if (c == NULL) {
  720 #if !defined(NO_EDITCOMPLETE)
  721             /*
  722              * attempt to el_parse() unknown commands.
  723              * any command containing a ':' would be parsed
  724              * as "[prog:]cmd ...", and will result in a
  725              * false positive if prog != "ftp", so treat
  726              * such commands as invalid.
  727              */
  728             if (strchr(margv[0], ':') != NULL ||
  729                 !editing ||
  730                 el_parse(el, margc, (void *)margv) != 0)
  731 #endif /* !NO_EDITCOMPLETE */
  732                 fputs("?Invalid command.\n", ttyout);
  733             continue;
  734         }
  735         if (c->c_conn && !connected) {
  736             fputs("Not connected.\n", ttyout);
  737             continue;
  738         }
  739         confirmrest = 0;
  740         (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf));
  741         margv[0] = cmdbuf;
  742         (*c->c_handler)(margc, margv);
  743         if (bell && c->c_bell)
  744             (void)putc('\007', ttyout);
  745         if (c->c_handler != help)
  746             break;
  747     }
  748     (void)xsignal(SIGINT, intr);
  749     (void)xsignal(SIGPIPE, lostpeer);
  750 }
  751 
  752 struct cmd *
  753 getcmd(const char *name)
  754 {
  755     const char *p, *q;
  756     struct cmd *c, *found;
  757     int nmatches, longest;
  758 
  759     if (name == NULL)
  760         return (0);
  761 
  762     longest = 0;
  763     nmatches = 0;
  764     found = 0;
  765     for (c = cmdtab; (p = c->c_name) != NULL; c++) {
  766         for (q = name; *q == *p++; q++)
  767             if (*q == 0)        /* exact match? */
  768                 return (c);
  769         if (!*q) {          /* the name was a prefix */
  770             if (q - name > longest) {
  771                 longest = q - name;
  772                 nmatches = 1;
  773                 found = c;
  774             } else if (q - name == longest)
  775                 nmatches++;
  776         }
  777     }
  778     if (nmatches > 1)
  779         return ((struct cmd *)-1);
  780     return (found);
  781 }
  782 
  783 /*
  784  * Slice a string up into argc/argv.
  785  */
  786 
  787 int slrflag;
  788 
  789 void
  790 makeargv(void)
  791 {
  792     char *argp;
  793 
  794     stringbase = line;      /* scan from first of buffer */
  795     argbase = argbuf;       /* store from first of buffer */
  796     slrflag = 0;
  797     marg_sl->sl_cur = 0;        /* reset to start of marg_sl */
  798     for (margc = 0; ; margc++) {
  799         argp = slurpstring();
  800         ftp_sl_add(marg_sl, argp);
  801         if (argp == NULL)
  802             break;
  803     }
  804 #ifndef NO_EDITCOMPLETE
  805     if (cursor_pos == line) {
  806         cursor_argc = 0;
  807         cursor_argo = 0;
  808     } else if (cursor_pos != NULL) {
  809         cursor_argc = margc;
  810         cursor_argo = strlen(margv[margc-1]);
  811     }
  812 #endif /* !NO_EDITCOMPLETE */
  813 }
  814 
  815 #ifdef NO_EDITCOMPLETE
  816 #define INC_CHKCURSOR(x)    (x)++
  817 #else  /* !NO_EDITCOMPLETE */
  818 #define INC_CHKCURSOR(x)    { (x)++ ; \
  819                 if (x == cursor_pos) { \
  820                     cursor_argc = margc; \
  821                     cursor_argo = ap-argbase; \
  822                     cursor_pos = NULL; \
  823                 } }
  824 
  825 #endif /* !NO_EDITCOMPLETE */
  826 
  827 /*
  828  * Parse string into argbuf;
  829  * implemented with FSM to
  830  * handle quoting and strings
  831  */
  832 char *
  833 slurpstring(void)
  834 {
  835     static char bangstr[2] = { '!', '\0' };
  836     static char dollarstr[2] = { '$', '\0' };
  837     int got_one = 0;
  838     char *sb = stringbase;
  839     char *ap = argbase;
  840     char *tmp = argbase;        /* will return this if token found */
  841 
  842     if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
  843         switch (slrflag) {  /* and $ as token for macro invoke */
  844             case 0:
  845                 slrflag++;
  846                 INC_CHKCURSOR(stringbase);
  847                 return ((*sb == '!') ? bangstr : dollarstr);
  848                 /* NOTREACHED */
  849             case 1:
  850                 slrflag++;
  851                 altarg = stringbase;
  852                 break;
  853             default:
  854                 break;
  855         }
  856     }
  857 
  858 S0:
  859     switch (*sb) {
  860 
  861     case '\0':
  862         goto OUT;
  863 
  864     case ' ':
  865     case '\t':
  866         INC_CHKCURSOR(sb);
  867         goto S0;
  868 
  869     default:
  870         switch (slrflag) {
  871             case 0:
  872                 slrflag++;
  873                 break;
  874             case 1:
  875                 slrflag++;
  876                 altarg = sb;
  877                 break;
  878             default:
  879                 break;
  880         }
  881         goto S1;
  882     }
  883 
  884 S1:
  885     switch (*sb) {
  886 
  887     case ' ':
  888     case '\t':
  889     case '\0':
  890         goto OUT;   /* end of token */
  891 
  892     case '\\':
  893         INC_CHKCURSOR(sb);
  894         goto S2;    /* slurp next character */
  895 
  896     case '"':
  897         INC_CHKCURSOR(sb);
  898         goto S3;    /* slurp quoted string */
  899 
  900     default:
  901         *ap = *sb;  /* add character to token */
  902         ap++;
  903         INC_CHKCURSOR(sb);
  904         got_one = 1;
  905         goto S1;
  906     }
  907 
  908 S2:
  909     switch (*sb) {
  910 
  911     case '\0':
  912         goto OUT;
  913 
  914     default:
  915         *ap = *sb;
  916         ap++;
  917         INC_CHKCURSOR(sb);
  918         got_one = 1;
  919         goto S1;
  920     }
  921 
  922 S3:
  923     switch (*sb) {
  924 
  925     case '\0':
  926         goto OUT;
  927 
  928     case '"':
  929         INC_CHKCURSOR(sb);
  930         goto S1;
  931 
  932     default:
  933         *ap = *sb;
  934         ap++;
  935         INC_CHKCURSOR(sb);
  936         got_one = 1;
  937         goto S3;
  938     }
  939 
  940 OUT:
  941     if (got_one)
  942         *ap++ = '\0';
  943     argbase = ap;           /* update storage pointer */
  944     stringbase = sb;        /* update scan pointer */
  945     if (got_one) {
  946         return (tmp);
  947     }
  948     switch (slrflag) {
  949         case 0:
  950             slrflag++;
  951             break;
  952         case 1:
  953             slrflag++;
  954             altarg = NULL;
  955             break;
  956         default:
  957             break;
  958     }
  959     return (NULL);
  960 }
  961 
  962 /*
  963  * Help/usage command.
  964  * Call each command handler with argc == 0 and argv[0] == name.
  965  */
  966 void
  967 help(int argc, char *argv[])
  968 {
  969     struct cmd *c;
  970     char *nargv[1], *cmd;
  971     const char *p;
  972     int isusage;
  973 
  974     cmd = argv[0];
  975     isusage = (strcmp(cmd, "usage") == 0);
  976     if (argc == 0 || (isusage && argc == 1)) {
  977         UPRINTF("usage: %s [command [...]]\n", cmd);
  978         return;
  979     }
  980     if (argc == 1) {
  981         StringList *buf;
  982 
  983         buf = ftp_sl_init();
  984         fprintf(ttyout,
  985             "%sommands may be abbreviated.  Commands are:\n\n",
  986             proxy ? "Proxy c" : "C");
  987         for (c = cmdtab; (p = c->c_name) != NULL; c++)
  988             if (!proxy || c->c_proxy)
  989                 ftp_sl_add(buf, ftp_strdup(p));
  990         list_vertical(buf);
  991         sl_free(buf, 1);
  992         return;
  993     }
  994 
  995 #define HELPINDENT ((int) sizeof("disconnect"))
  996 
  997     while (--argc > 0) {
  998         char *arg;
  999         char cmdbuf[MAX_C_NAME];
 1000 
 1001         arg = *++argv;
 1002         c = getcmd(arg);
 1003         if (c == (struct cmd *)-1)
 1004             fprintf(ttyout, "?Ambiguous %s command `%s'\n",
 1005                 cmd, arg);
 1006         else if (c == NULL)
 1007             fprintf(ttyout, "?Invalid %s command `%s'\n",
 1008                 cmd, arg);
 1009         else {
 1010             if (isusage) {
 1011                 (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf));
 1012                 nargv[0] = cmdbuf;
 1013                 (*c->c_handler)(0, nargv);
 1014             } else
 1015                 fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
 1016                     c->c_name, c->c_help);
 1017         }
 1018     }
 1019 }
 1020 
 1021 struct option *
 1022 getoption(const char *name)
 1023 {
 1024     const char *p;
 1025     struct option *c;
 1026 
 1027     if (name == NULL)
 1028         return (NULL);
 1029     for (c = optiontab; (p = c->name) != NULL; c++) {
 1030         if (strcasecmp(p, name) == 0)
 1031             return (c);
 1032     }
 1033     return (NULL);
 1034 }
 1035 
 1036 char *
 1037 getoptionvalue(const char *name)
 1038 {
 1039     struct option *c;
 1040 
 1041     if (name == NULL)
 1042         errx(1, "getoptionvalue: invoked with NULL name");
 1043     c = getoption(name);
 1044     if (c != NULL)
 1045         return (c->value);
 1046     errx(1, "getoptionvalue: invoked with unknown option `%s'", name);
 1047     /* NOTREACHED */
 1048 }
 1049 
 1050 static void
 1051 setupoption(const char *name, const char *value, const char *defaultvalue)
 1052 {
 1053     set_option(name, value ? value : defaultvalue, 0);
 1054 }
 1055 
 1056 void
 1057 usage(void)
 1058 {
 1059     const char *progname = getprogname();
 1060 
 1061     (void)fprintf(stderr,
 1062 "usage: %s [-46AadefginpRtVv] [-N netrc] [-o outfile] [-P port] [-q quittime]\n"
 1063 "           [-r retry] [-s srcaddr] [-T dir,max[,inc]] [-x xferbufsize]\n"
 1064 "           [[user@]host [port]] [host:path[/]] [file:///file]\n"
 1065 "           [ftp://[user[:pass]@]host[:port]/path[/]]\n"
 1066 "           [http://[user[:pass]@]host[:port]/path] [...]\n"
 1067 #ifdef WITH_SSL
 1068 "           [https://[user[:pass]@]host[:port]/path] [...]\n"
 1069 #endif
 1070 "       %s -u URL file [...]\n", progname, progname);
 1071     exit(1);
 1072 }