diff options
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 1093 |
1 files changed, 1093 insertions, 0 deletions
diff --git a/main.c b/main.c new file mode 100644 index 0000000..0124714 --- /dev/null +++ b/main.c @@ -0,0 +1,1093 @@ +/* $OpenBSD: main.c,v 1.146 2023/12/23 23:03:00 kn Exp $ */ +/* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */ + +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * FTP User Program -- Command Interface. + */ +#include <sys/types.h> +#include <sys/socket.h> + +#include <ctype.h> +#include <fcntl.h> +#include <netdb.h> +#include <pwd.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifndef NOSSL +#include "tls_compat.h" +#endif + +#include "cmds.h" +#include "ftp_var.h" + +int trace; +int hash; +int mark; +int sendport; +int verbose; +int connected; +int fromatty; +int interactive; +#ifndef SMALL +int confirmrest; +int debug; +int bell; +char *altarg; +#endif /* !SMALL */ +int doglob; +int autologin; +int proxy; +int proxflag; +int gatemode; +char *gateserver; +int sunique; +int runique; +int mcase; +int ntflag; +int mapflag; +int preserve; +int progress; +int code; +int crflag; +char pasv[BUFSIZ]; +int passivemode; +int activefallback; +char ntin[17]; +char ntout[17]; +char mapin[PATH_MAX]; +char mapout[PATH_MAX]; +char typename[32]; +int type; +int curtype; +char structname[32]; +int stru; +char formname[32]; +int form; +char modename[32]; +int mode; +char bytename[32]; +int bytesize; +int anonftp; +int dirchange; +unsigned int retry_connect; +int ttywidth; +int epsv4; +int epsv4bad; + +#ifndef SMALL +int editing; +EditLine *el; +History *hist; +char *cursor_pos; +size_t cursor_argc; +size_t cursor_argo; +int resume; +char *srcaddr; +int timestamp; +#endif /* !SMALL */ + +char *cookiefile; + +off_t bytes; +off_t filesize; +char *direction; + +char *hostname; +int unix_server; +int unix_proxy; + +char *ftpport; +char *httpport; +#ifndef NOSSL +char *httpsport; +#endif /* !SMALL */ +char *httpuseragent; +char *gateport; + +jmp_buf toplevel; + +#ifndef SMALL +char line[FTPBUFLEN]; +char *argbase; +char *stringbase; +char argbuf[FTPBUFLEN]; +StringList *marg_sl; +int margc; +int options; +#endif /* !SMALL */ + +int cpend; +int mflag; + +#ifndef SMALL +int macnum; +struct macel macros[16]; +char macbuf[4096]; +#endif /* !SMALL */ + +FILE *ttyout; + +int connect_timeout; + +#ifndef SMALL +/* enable using server timestamps by default */ +int server_timestamps = 1; +#endif + +#ifndef NOSSL +char * const ssl_verify_opts[] = { +#define SSL_CAFILE 0 + "cafile", +#define SSL_CAPATH 1 + "capath", +#define SSL_CIPHERS 2 + "ciphers", +#define SSL_DONTVERIFY 3 + "dont", +#define SSL_DOVERIFY 4 + "do", +#define SSL_VERIFYDEPTH 5 + "depth", +#define SSL_MUSTSTAPLE 6 + "muststaple", +#define SSL_NOVERIFYTIME 7 + "noverifytime", +#define SSL_SESSION 8 + "session", +#define SSL_PROTOCOLS 9 + "protocols", + NULL +}; + +tls_config_t tls_config; +int tls_session_fd = -1; + +static void +process_ssl_options(char *cp) +{ + const char *errstr; + char *str; + int depth; + uint32_t protocols; + + while (*cp) { + switch (getsubopt(&cp, ssl_verify_opts, &str)) { + case SSL_CAFILE: + if (str == NULL) + errx(1, "missing CA file"); + if (tls_config_set_ca_file(tls_config, str) != 0) + errx(1, "tls ca file failed: %s", + tls_config_error(tls_config)); + break; + case SSL_CAPATH: + if (str == NULL) + errx(1, "missing CA directory path"); + if (tls_config_set_ca_path(tls_config, str) != 0) + errx(1, "tls ca path failed: %s", + tls_config_error(tls_config)); + break; + case SSL_CIPHERS: + if (str == NULL) + errx(1, "missing cipher list"); + if (tls_config_set_ciphers(tls_config, str) != 0) + errx(1, "tls ciphers failed: %s", + tls_config_error(tls_config)); + break; + case SSL_DONTVERIFY: + tls_config_insecure_noverifycert(tls_config); + tls_config_insecure_noverifyname(tls_config); + break; + case SSL_DOVERIFY: + tls_config_verify(tls_config); + break; + case SSL_VERIFYDEPTH: + if (str == NULL) + errx(1, "missing depth"); + depth = strtonum(str, 0, INT_MAX, &errstr); + if (errstr) + errx(1, "certificate validation depth is %s", + errstr); + tls_config_set_verify_depth(tls_config, depth); + break; + case SSL_MUSTSTAPLE: + tls_config_ocsp_require_stapling(tls_config); + break; + case SSL_NOVERIFYTIME: + tls_config_insecure_noverifytime(tls_config); + break; + case SSL_SESSION: + if (str == NULL) + errx(1, "missing session file"); + if ((tls_session_fd = open(str, O_RDWR|O_CREAT, + 0600)) == -1) + err(1, "failed to open or create session file " + "'%s'", str); + if (tls_config_set_session_fd(tls_config, + tls_session_fd) == -1) + errx(1, "failed to set session: %s", + tls_config_error(tls_config)); + break; + case SSL_PROTOCOLS: + if (str == NULL) + errx(1, "missing protocol name"); + if (tls_config_parse_protocols(&protocols, str) != 0) + errx(1, "failed to parse TLS protocols"); + if (tls_config_set_protocols(tls_config, protocols) != 0) + errx(1, "failed to set TLS protocols: %s", + tls_config_error(tls_config)); + break; + default: + errx(1, "unknown -S suboption `%s'", + suboptarg ? suboptarg : ""); + /* NOTREACHED */ + } + } +} +#endif /* !NOSSL */ + +int family = PF_UNSPEC; +int pipeout; + +int +main(volatile int argc, char *argv[]) +{ + int ch, rval; +#ifndef SMALL + int top; +#endif + struct passwd *pw = NULL; + char *cp, homedir[PATH_MAX]; + char *outfile = NULL; + const char *errstr; + int dumb_terminal = 0; + + ftpport = "ftp"; + httpport = "http"; +#ifndef NOSSL + httpsport = "https"; +#endif /* !NOSSL */ + gateport = getenv("FTPSERVERPORT"); + if (gateport == NULL || *gateport == '\0') + gateport = "ftpgate"; + doglob = 1; + interactive = 1; + autologin = 1; + passivemode = 1; + activefallback = 1; + preserve = 1; + verbose = 0; + progress = 0; + gatemode = 0; +#ifndef NOSSL + cookiefile = NULL; +#endif /* NOSSL */ +#ifndef SMALL + editing = 0; + el = NULL; + hist = NULL; + resume = 0; + timestamp = 0; + srcaddr = NULL; + marg_sl = sl_init(); +#endif /* !SMALL */ + mark = HASHBYTES; + epsv4 = 1; + epsv4bad = 0; + + /* Set default operation mode based on FTPMODE environment variable */ + if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') { + if (strcmp(cp, "passive") == 0) { + passivemode = 1; + activefallback = 0; + } else if (strcmp(cp, "active") == 0) { + passivemode = 0; + activefallback = 0; + } else if (strcmp(cp, "gate") == 0) { + gatemode = 1; + } else if (strcmp(cp, "auto") == 0) { + passivemode = 1; + activefallback = 1; + } else + warnx("unknown FTPMODE: %s. Using defaults", cp); + } + + if (strcmp(__progname, "gate-ftp") == 0) + gatemode = 1; + gateserver = getenv("FTPSERVER"); + if (gateserver == NULL) + gateserver = ""; + if (gatemode) { + if (*gateserver == '\0') { + warnx( +"Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp"); + gatemode = 0; + } + } + + cp = getenv("TERM"); + dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") || + !strcmp(cp, "emacs") || !strcmp(cp, "su")); + fromatty = isatty(fileno(stdin)); + if (fromatty) { + verbose = 1; /* verbose if from a tty */ +#ifndef SMALL + if (!dumb_terminal) + editing = 1; /* editing mode on if tty is usable */ +#endif /* !SMALL */ + } + + ttyout = stdout; + if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc()) + progress = 1; /* progress bar on if tty is usable */ + +#ifndef NOSSL + cookiefile = getenv("http_cookies"); + if (tls_config == NULL) { + tls_config = tls_config_new(); + if (tls_config == NULL) + errx(1, "tls config failed"); + if (tls_config_set_protocols(tls_config, + TLS_PROTOCOLS_ALL) != 0) + errx(1, "tls set protocols failed: %s", + tls_config_error(tls_config)); + if (tls_config_set_ciphers(tls_config, "DEFAULT") != 0) + errx(1, "tls set ciphers failed: %s", + tls_config_error(tls_config)); + } +#endif /* !NOSSL */ + + httpuseragent = NULL; + + while ((ch = getopt(argc, argv, + "46AaCc:dD:EeN:gik:Mmno:pP:r:S:s:TtU:uvVw:")) != -1) { + switch (ch) { + case '4': + family = PF_INET; + break; + case '6': + family = PF_INET6; + break; + case 'A': + activefallback = 0; + passivemode = 0; + break; + + case 'N': + setprogname(optarg); + break; + case 'a': + anonftp = 1; + break; + + case 'C': +#ifndef SMALL + resume = 1; +#endif /* !SMALL */ + break; + + case 'c': +#ifndef SMALL + cookiefile = optarg; +#endif /* !SMALL */ + break; + + case 'D': + action = optarg; + break; + case 'd': +#ifndef SMALL + options |= SO_DEBUG; + debug++; +#endif /* !SMALL */ + break; + + case 'E': + epsv4 = 0; + break; + + case 'e': +#ifndef SMALL + editing = 0; +#endif /* !SMALL */ + break; + + case 'g': + doglob = 0; + break; + + case 'i': + interactive = 0; + break; + + case 'k': + keep_alive_timeout = strtonum(optarg, 0, INT_MAX, + &errstr); + if (errstr != NULL) { + warnx("keep alive amount is %s: %s", errstr, + optarg); + usage(); + } + break; + case 'M': + progress = 0; + break; + case 'm': + progress = -1; + break; + + case 'n': + autologin = 0; + break; + + case 'o': + outfile = optarg; + pipeout = strcmp(outfile, "-") == 0; + ttyout = pipeout ? stderr : stdout; + break; + + case 'p': + passivemode = 1; + activefallback = 0; + break; + + case 'P': + ftpport = optarg; + break; + + case 'r': + retry_connect = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) { + warnx("retry amount is %s: %s", errstr, + optarg); + usage(); + } + break; + + case 'S': +#ifndef NOSSL + process_ssl_options(optarg); +#endif /* !NOSSL */ + break; + + case 's': +#ifndef SMALL + srcaddr = optarg; +#endif /* !SMALL */ + break; + +#ifndef SMALL + case 'T': + timestamp = 1; + break; +#endif /* !SMALL */ + case 't': + trace = 1; + break; + +#ifndef SMALL + case 'U': + free (httpuseragent); + if (strcspn(optarg, "\r\n") != strlen(optarg)) + errx(1, "Invalid User-Agent: %s.", optarg); + if (asprintf(&httpuseragent, "User-Agent: %s", + optarg) == -1) + errx(1, "Can't allocate memory for HTTP(S) " + "User-Agent"); + break; + case 'u': + server_timestamps = 0; + break; +#endif /* !SMALL */ + + case 'v': + verbose = 1; + break; + + case 'V': + verbose = 0; + break; + + case 'w': + connect_timeout = strtonum(optarg, 0, 200, &errstr); + if (errstr) + errx(1, "-w: %s", errstr); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + +#ifndef NOSSL + cookie_load(); +#endif /* !NOSSL */ + if (httpuseragent == NULL) + httpuseragent = HTTP_USER_AGENT; + + cpend = 0; /* no pending replies */ + proxy = 0; /* proxy not active */ + crflag = 1; /* strip c.r. on ascii gets */ + sendport = -1; /* not using ports */ + /* + * Set up the home directory in case we're globbing. + */ + cp = getlogin(); + if (cp != NULL) { + pw = getpwnam(cp); + } + if (pw == NULL) + pw = getpwuid(getuid()); + if (pw != NULL) { + (void)strlcpy(homedir, pw->pw_dir, sizeof homedir); + home = homedir; + } + + setttywidth(0); + (void)signal(SIGWINCH, setttywidth); + + if (argc > 0) { + if (isurl(argv[0])) { + if (pipeout) { + if (pledge("stdio rpath dns tty inet", + NULL) == -1) + err(1, "pledge"); + } else { +#ifndef SMALL + if (outfile == NULL) { + if (pledge("stdio rpath wpath cpath dns tty inet proc exec fattr", + NULL) == -1) + err(1, "pledge"); + } else +#endif /* !SMALL */ + if (pledge("stdio rpath wpath cpath dns tty inet fattr", + NULL) == -1) + err(1, "pledge"); + } + + rval = auto_fetch(argc, argv, outfile); + /* -1 == connected and cd-ed */ + if (rval >= 0 || outfile != NULL) + exit(rval); + } else { +#ifndef SMALL + char *xargv[5]; + + if (setjmp(toplevel)) + exit(0); + (void)signal(SIGINT, (sig_t)intr); + (void)signal(SIGPIPE, (sig_t)lostpeer); + xargv[0] = __progname; + xargv[1] = argv[0]; + xargv[2] = argv[1]; + xargv[3] = argv[2]; + xargv[4] = NULL; + do { + setpeer(argc+1, xargv); + if (!retry_connect) + break; + if (!connected) { + macnum = 0; + fputs("Retrying...\n", ttyout); + sleep(retry_connect); + } + } while (!connected); + retry_connect = 0; /* connected, stop hiding msgs */ +#endif /* !SMALL */ + } + } +#ifndef SMALL + controlediting(); + top = setjmp(toplevel) == 0; + if (top) { + (void)signal(SIGINT, (sig_t)intr); + (void)signal(SIGPIPE, (sig_t)lostpeer); + } + for (;;) { + cmdscanner(top); + top = 1; + } +#else /* !SMALL */ + usage(); +#endif /* !SMALL */ +} + +void +intr(void) +{ + int save_errno = errno; + + write(fileno(ttyout), "\n\r", 2); + alarmtimer(0); + + errno = save_errno; + longjmp(toplevel, 1); +} + +void +lostpeer(void) +{ + int save_errno = errno; + + alarmtimer(0); + if (connected) { + if (cout != NULL) { + (void)shutdown(fileno(cout), SHUT_RDWR); + (void)fclose(cout); + cout = NULL; + } + if (data >= 0) { + (void)shutdown(data, SHUT_RDWR); + (void)close(data); + data = -1; + } + connected = 0; + } + pswitch(1); + if (connected) { + if (cout != NULL) { + (void)shutdown(fileno(cout), SHUT_RDWR); + (void)fclose(cout); + cout = NULL; + } + connected = 0; + } + proxflag = 0; + pswitch(0); + errno = save_errno; +} + +#ifndef SMALL +/* + * Generate a prompt + */ +char * +prompt(void) +{ + return ("ftp> "); +} + +/* + * Command parser. + */ +void +cmdscanner(int top) +{ + struct cmd *c; + int num; + HistEvent hev; + + if (!top && !editing) + (void)putc('\n', ttyout); + for (;;) { + if (!editing) { + if (fromatty) { + fputs(prompt(), ttyout); + (void)fflush(ttyout); + } + if (fgets(line, sizeof(line), stdin) == NULL) + quit(0, 0); + num = strlen(line); + if (num == 0) + break; + if (line[--num] == '\n') { + if (num == 0) + break; + line[num] = '\0'; + } else if (num == sizeof(line) - 2) { + fputs("sorry, input line too long.\n", ttyout); + while ((num = getchar()) != '\n' && num != EOF) + /* void */; + break; + } /* else it was a line without a newline */ + } else { + const char *buf; + cursor_pos = NULL; + + if ((buf = el_gets(el, &num)) == NULL || num == 0) { + putc('\n', ttyout); + fflush(ttyout); + quit(0, 0); + } + if (buf[--num] == '\n') { + if (num == 0) + break; + } + if (num >= sizeof(line)) { + fputs("sorry, input line too long.\n", ttyout); + break; + } + memcpy(line, buf, (size_t)num); + line[num] = '\0'; + history(hist, &hev, H_ENTER, buf); + } + + makeargv(); + if (margc == 0) + continue; + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + fputs("?Ambiguous command.\n", ttyout); + continue; + } + if (c == 0) { + /* + * Give editline(3) a shot at unknown commands. + * XXX - bogus commands with a colon in + * them will not elicit an error. + */ + if (editing && + el_parse(el, margc, (const char **)margv) != 0) + fputs("?Invalid command.\n", ttyout); + continue; + } + if (c->c_conn && !connected) { + fputs("Not connected.\n", ttyout); + continue; + } + confirmrest = 0; + (*c->c_handler)(margc, margv); + if (bell && c->c_bell) + (void)putc('\007', ttyout); + if (c->c_handler != help) + break; + } + (void)signal(SIGINT, (sig_t)intr); + (void)signal(SIGPIPE, (sig_t)lostpeer); +} + +struct cmd * +getcmd(const char *name) +{ + const char *p, *q; + struct cmd *c, *found; + int nmatches, longest; + + if (name == NULL) + return (0); + + longest = 0; + nmatches = 0; + found = 0; + for (c = cmdtab; (p = c->c_name) != NULL; c++) { + for (q = name; *q == *p++; q++) + if (*q == 0) /* exact match? */ + return (c); + if (!*q) { /* the name was a prefix */ + if (q - name > longest) { + longest = q - name; + nmatches = 1; + found = c; + } else if (q - name == longest) + nmatches++; + } + } + if (nmatches > 1) + return ((struct cmd *)-1); + return (found); +} + +/* + * Slice a string up into argc/argv. + */ + +int slrflag; + +void +makeargv(void) +{ + char *argp; + + stringbase = line; /* scan from first of buffer */ + argbase = argbuf; /* store from first of buffer */ + slrflag = 0; + marg_sl->sl_cur = 0; /* reset to start of marg_sl */ + for (margc = 0; ; margc++) { + argp = slurpstring(); + sl_add(marg_sl, argp); + if (argp == NULL) + break; + } + if (cursor_pos == line) { + cursor_argc = 0; + cursor_argo = 0; + } else if (cursor_pos != NULL) { + cursor_argc = margc; + cursor_argo = strlen(margv[margc-1]); + } +} + +#define INC_CHKCURSOR(x) { (x)++ ; \ + if (x == cursor_pos) { \ + cursor_argc = margc; \ + cursor_argo = ap-argbase; \ + cursor_pos = NULL; \ + } } + +/* + * Parse string into argbuf; + * implemented with FSM to + * handle quoting and strings + */ +char * +slurpstring(void) +{ + int got_one = 0; + char *sb = stringbase; + char *ap = argbase; + char *tmp = argbase; /* will return this if token found */ + + if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ + switch (slrflag) { /* and $ as token for macro invoke */ + case 0: + slrflag++; + INC_CHKCURSOR(stringbase); + return ((*sb == '!') ? "!" : "$"); + /* NOTREACHED */ + case 1: + slrflag++; + altarg = stringbase; + break; + default: + break; + } + } + +S0: + switch (*sb) { + + case '\0': + goto OUT; + + case ' ': + case '\t': + INC_CHKCURSOR(sb); + goto S0; + + default: + switch (slrflag) { + case 0: + slrflag++; + break; + case 1: + slrflag++; + altarg = sb; + break; + default: + break; + } + goto S1; + } + +S1: + switch (*sb) { + + case ' ': + case '\t': + case '\0': + goto OUT; /* end of token */ + + case '\\': + INC_CHKCURSOR(sb); + goto S2; /* slurp next character */ + + case '"': + INC_CHKCURSOR(sb); + goto S3; /* slurp quoted string */ + + default: + *ap = *sb; /* add character to token */ + ap++; + INC_CHKCURSOR(sb); + got_one = 1; + goto S1; + } + +S2: + switch (*sb) { + + case '\0': + goto OUT; + + default: + *ap = *sb; + ap++; + INC_CHKCURSOR(sb); + got_one = 1; + goto S1; + } + +S3: + switch (*sb) { + + case '\0': + goto OUT; + + case '"': + INC_CHKCURSOR(sb); + goto S1; + + default: + *ap = *sb; + ap++; + INC_CHKCURSOR(sb); + got_one = 1; + goto S3; + } + +OUT: + if (got_one) + *ap++ = '\0'; + argbase = ap; /* update storage pointer */ + stringbase = sb; /* update scan pointer */ + if (got_one) { + return (tmp); + } + switch (slrflag) { + case 0: + slrflag++; + break; + case 1: + slrflag++; + altarg = (char *) 0; + break; + default: + break; + } + return (NULL); +} + +/* + * Help command. + * Call each command handler with argc == 0 and argv[0] == name. + */ +void +help(int argc, char *argv[]) +{ + struct cmd *c; + + if (argc == 1) { + StringList *buf; + + buf = sl_init(); + fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n", + proxy ? "Proxy c" : "C"); + for (c = cmdtab; c < &cmdtab[NCMDS]; c++) + if (c->c_name && (!proxy || c->c_proxy)) + sl_add(buf, c->c_name); + list_vertical(buf); + sl_free(buf, 0); + return; + } + +#define HELPINDENT ((int) sizeof("disconnect")) + + while (--argc > 0) { + char *arg; + + arg = *++argv; + c = getcmd(arg); + if (c == (struct cmd *)-1) + fprintf(ttyout, "?Ambiguous help command %s\n", arg); + else if (c == NULL) + fprintf(ttyout, "?Invalid help command %s\n", arg); + else + fprintf(ttyout, "%-*s\t%s\n", HELPINDENT, + c->c_name, c->c_help); + } +} +#endif /* !SMALL */ + +__dead void +usage(void) +{ + fprintf(stderr, "usage: " +#ifndef SMALL + "ftp [-46AadEegiMmnptVv] [-D title] [-k seconds] [-P port] " + "[-r seconds]\n" + " [-s sourceaddr] [host [port]]\n" + " ftp [-C] [-N name] [-o output] [-s sourceaddr]\n" + " ftp://[user:password@]host[:port]/file[/] ...\n" + " ftp [-CTu] [-c cookie] [-N name] [-o output] [-S ssl_options] " + "[-s sourceaddr]\n" + " [-U useragent] [-w seconds] " + "http[s]://[user:password@]host[:port]/file ...\n" + " ftp [-C] [-N name] [-o output] [-s sourceaddr] file:file ...\n" + " ftp [-C] [-N name] [-o output] [-s sourceaddr] host:/file[/] ...\n" +#else /* !SMALL */ + "ftp [-N name] [-o output] " + "ftp://[user:password@]host[:port]/file[/] ...\n" +#ifndef NOSSL + " ftp [-N name] [-o output] [-S ssl_options] [-w seconds] " + "http[s]://[user:password@]host[:port]/file ...\n" +#else + " ftp [-N name] [-o output] [-w seconds] http://host[:port]/file ...\n" +#endif /* NOSSL */ + " ftp [-N name] [-o output] file:file ...\n" + " ftp [-N name] [-o output] host:/file[/] ...\n" +#endif /* !SMALL */ + ); + exit(1); +} |