summaryrefslogtreecommitdiffstats
path: root/usr.bin/ftp
diff options
context:
space:
mode:
authorrgrimes <rgrimes@FreeBSD.org>1994-05-27 12:33:43 +0000
committerrgrimes <rgrimes@FreeBSD.org>1994-05-27 12:33:43 +0000
commitf9ab90d9d6d02989a075d0f0074496d5b1045e4b (patch)
treeadd7e996bac5289cdc55e6935750c352505560a9 /usr.bin/ftp
parentbe22b15ae2ff8d7fe06b6e14fddf0c5b444a95da (diff)
downloadFreeBSD-src-f9ab90d9d6d02989a075d0f0074496d5b1045e4b.zip
FreeBSD-src-f9ab90d9d6d02989a075d0f0074496d5b1045e4b.tar.gz
BSD 4.4 Lite Usr.bin Sources
Diffstat (limited to 'usr.bin/ftp')
-rw-r--r--usr.bin/ftp/Makefile6
-rw-r--r--usr.bin/ftp/cmds.c2206
-rw-r--r--usr.bin/ftp/cmdtab.c186
-rw-r--r--usr.bin/ftp/domacro.c148
-rw-r--r--usr.bin/ftp/extern.h152
-rw-r--r--usr.bin/ftp/ftp.11136
-rw-r--r--usr.bin/ftp/ftp.c1470
-rw-r--r--usr.bin/ftp/ftp_var.h125
-rw-r--r--usr.bin/ftp/main.c514
-rw-r--r--usr.bin/ftp/pathnames.h39
-rw-r--r--usr.bin/ftp/ruserpass.c281
11 files changed, 6263 insertions, 0 deletions
diff --git a/usr.bin/ftp/Makefile b/usr.bin/ftp/Makefile
new file mode 100644
index 0000000..fbb5a91
--- /dev/null
+++ b/usr.bin/ftp/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.2 (Berkeley) 4/3/94
+
+PROG= ftp
+SRCS= cmds.c cmdtab.c ftp.c main.c ruserpass.c domacro.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ftp/cmds.c b/usr.bin/ftp/cmds.c
new file mode 100644
index 0000000..8da2b20
--- /dev/null
+++ b/usr.bin/ftp/cmds.c
@@ -0,0 +1,2206 @@
+/*
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmds.c 8.5 (Berkeley) 4/6/94";
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Command Routines.
+ */
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/ftp.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <glob.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "ftp_var.h"
+#include "pathnames.h"
+
+jmp_buf jabort;
+char *mname;
+char *home = "/";
+
+/*
+ * `Another' gets another argument, and stores the new argc and argv.
+ * It reverts to the top level (via main.c's intr()) on EOF/error.
+ *
+ * Returns false if no new arguments have been added.
+ */
+int
+another(pargc, pargv, prompt)
+ int *pargc;
+ char ***pargv;
+ char *prompt;
+{
+ int len = strlen(line), ret;
+
+ if (len >= sizeof(line) - 3) {
+ printf("sorry, arguments too long\n");
+ intr();
+ }
+ printf("(%s) ", prompt);
+ line[len++] = ' ';
+ if (fgets(&line[len], sizeof(line) - len, stdin) == NULL)
+ intr();
+ len += strlen(&line[len]);
+ if (len > 0 && line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ makeargv();
+ ret = margc > *pargc;
+ *pargc = margc;
+ *pargv = margv;
+ return (ret);
+}
+
+/*
+ * Connect to peer server and
+ * auto-login, if possible.
+ */
+void
+setpeer(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *host;
+ short port;
+
+ if (connected) {
+ printf("Already connected to %s, use close first.\n",
+ hostname);
+ code = -1;
+ return;
+ }
+ if (argc < 2)
+ (void) another(&argc, &argv, "to");
+ if (argc < 2 || argc > 3) {
+ printf("usage: %s host-name [port]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ port = sp->s_port;
+ if (argc > 2) {
+ port = atoi(argv[2]);
+ if (port <= 0) {
+ printf("%s: bad port number-- %s\n", argv[1], argv[2]);
+ printf ("usage: %s host-name [port]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ port = htons(port);
+ }
+ host = hookup(argv[1], port);
+ if (host) {
+ int overbose;
+
+ connected = 1;
+ /*
+ * Set up defaults for FTP.
+ */
+ (void) strcpy(typename, "ascii"), type = TYPE_A;
+ curtype = TYPE_A;
+ (void) strcpy(formname, "non-print"), form = FORM_N;
+ (void) strcpy(modename, "stream"), mode = MODE_S;
+ (void) strcpy(structname, "file"), stru = STRU_F;
+ (void) strcpy(bytename, "8"), bytesize = 8;
+ if (autologin)
+ (void) login(argv[1]);
+
+#if defined(unix) && NBBY == 8
+/*
+ * this ifdef is to keep someone form "porting" this to an incompatible
+ * system and not checking this out. This way they have to think about it.
+ */
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("SYST") == COMPLETE && overbose) {
+ char *cp, c;
+ cp = strchr(reply_string+4, ' ');
+ if (cp == NULL)
+ cp = strchr(reply_string+4, '\r');
+ if (cp) {
+ if (cp[-1] == '.')
+ cp--;
+ c = *cp;
+ *cp = '\0';
+ }
+
+ printf("Remote system type is %s.\n",
+ reply_string+4);
+ if (cp)
+ *cp = c;
+ }
+ if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
+ if (proxy)
+ unix_proxy = 1;
+ else
+ unix_server = 1;
+ /*
+ * Set type to 0 (not specified by user),
+ * meaning binary by default, but don't bother
+ * telling server. We can use binary
+ * for text files unless changed by the user.
+ */
+ type = 0;
+ (void) strcpy(typename, "binary");
+ if (overbose)
+ printf("Using %s mode to transfer files.\n",
+ typename);
+ } else {
+ if (proxy)
+ unix_proxy = 0;
+ else
+ unix_server = 0;
+ if (overbose &&
+ !strncmp(reply_string, "215 TOPS20", 10))
+ printf(
+"Remember to set tenex mode when transfering binary files from this machine.\n");
+ }
+ verbose = overbose;
+#endif /* unix */
+ }
+}
+
+struct types {
+ char *t_name;
+ char *t_mode;
+ int t_type;
+ char *t_arg;
+} types[] = {
+ { "ascii", "A", TYPE_A, 0 },
+ { "binary", "I", TYPE_I, 0 },
+ { "image", "I", TYPE_I, 0 },
+ { "ebcdic", "E", TYPE_E, 0 },
+ { "tenex", "L", TYPE_L, bytename },
+ { NULL }
+};
+
+/*
+ * Set transfer type.
+ */
+void
+settype(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct types *p;
+ int comret;
+
+ if (argc > 2) {
+ char *sep;
+
+ printf("usage: %s [", argv[0]);
+ sep = " ";
+ for (p = types; p->t_name; p++) {
+ printf("%s%s", sep, p->t_name);
+ sep = " | ";
+ }
+ printf(" ]\n");
+ code = -1;
+ return;
+ }
+ if (argc < 2) {
+ printf("Using %s mode to transfer files.\n", typename);
+ code = 0;
+ return;
+ }
+ for (p = types; p->t_name; p++)
+ if (strcmp(argv[1], p->t_name) == 0)
+ break;
+ if (p->t_name == 0) {
+ printf("%s: unknown mode\n", argv[1]);
+ code = -1;
+ return;
+ }
+ if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
+ comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
+ else
+ comret = command("TYPE %s", p->t_mode);
+ if (comret == COMPLETE) {
+ (void) strcpy(typename, p->t_name);
+ curtype = type = p->t_type;
+ }
+}
+
+/*
+ * Internal form of settype; changes current type in use with server
+ * without changing our notion of the type for data transfers.
+ * Used to change to and from ascii for listings.
+ */
+void
+changetype(newtype, show)
+ int newtype, show;
+{
+ struct types *p;
+ int comret, oldverbose = verbose;
+
+ if (newtype == 0)
+ newtype = TYPE_I;
+ if (newtype == curtype)
+ return;
+ if (debug == 0 && show == 0)
+ verbose = 0;
+ for (p = types; p->t_name; p++)
+ if (newtype == p->t_type)
+ break;
+ if (p->t_name == 0) {
+ printf("ftp: internal error: unknown type %d\n", newtype);
+ return;
+ }
+ if (newtype == TYPE_L && bytename[0] != '\0')
+ comret = command("TYPE %s %s", p->t_mode, bytename);
+ else
+ comret = command("TYPE %s", p->t_mode);
+ if (comret == COMPLETE)
+ curtype = newtype;
+ verbose = oldverbose;
+}
+
+char *stype[] = {
+ "type",
+ "",
+ 0
+};
+
+/*
+ * Set binary transfer type.
+ */
+/*VARARGS*/
+void
+setbinary(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ stype[1] = "binary";
+ settype(2, stype);
+}
+
+/*
+ * Set ascii transfer type.
+ */
+/*VARARGS*/
+void
+setascii(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ stype[1] = "ascii";
+ settype(2, stype);
+}
+
+/*
+ * Set tenex transfer type.
+ */
+/*VARARGS*/
+void
+settenex(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ stype[1] = "tenex";
+ settype(2, stype);
+}
+
+/*
+ * Set file transfer mode.
+ */
+/*ARGSUSED*/
+void
+setftmode(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ printf("We only support %s mode, sorry.\n", modename);
+ code = -1;
+}
+
+/*
+ * Set file transfer format.
+ */
+/*ARGSUSED*/
+void
+setform(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ printf("We only support %s format, sorry.\n", formname);
+ code = -1;
+}
+
+/*
+ * Set file transfer structure.
+ */
+/*ARGSUSED*/
+void
+setstruct(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ printf("We only support %s structure, sorry.\n", structname);
+ code = -1;
+}
+
+/*
+ * Send a single file.
+ */
+void
+put(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cmd;
+ int loc = 0;
+ char *oldargv1, *oldargv2;
+
+ if (argc == 2) {
+ argc++;
+ argv[2] = argv[1];
+ loc++;
+ }
+ if (argc < 2 && !another(&argc, &argv, "local-file"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "remote-file")) {
+usage:
+ printf("usage: %s local-file remote-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ oldargv1 = argv[1];
+ oldargv2 = argv[2];
+ if (!globulize(&argv[1])) {
+ code = -1;
+ return;
+ }
+ /*
+ * If "globulize" modifies argv[1], and argv[2] is a copy of
+ * the old argv[1], make it a copy of the new argv[1].
+ */
+ if (argv[1] != oldargv1 && argv[2] == oldargv1) {
+ argv[2] = argv[1];
+ }
+ cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
+ if (loc && ntflag) {
+ argv[2] = dotrans(argv[2]);
+ }
+ if (loc && mapflag) {
+ argv[2] = domap(argv[2]);
+ }
+ sendrequest(cmd, argv[1], argv[2],
+ argv[1] != oldargv1 || argv[2] != oldargv2);
+}
+
+/*
+ * Send multiple files.
+ */
+void
+mput(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ sig_t oldintr;
+ int ointer;
+ char *tp;
+
+ if (argc < 2 && !another(&argc, &argv, "local-files")) {
+ printf("usage: %s local-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ if (proxy) {
+ char *cp, *tp2, tmpbuf[MAXPATHLEN];
+
+ while ((cp = remglob(argv,0)) != NULL) {
+ if (*cp == 0) {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ tp = cp;
+ if (mcase) {
+ while (*tp && !islower(*tp)) {
+ tp++;
+ }
+ if (!*tp) {
+ tp = cp;
+ tp2 = tmpbuf;
+ while ((*tp2 = *tp) != NULL) {
+ if (isupper(*tp2)) {
+ *tp2 = 'a' + *tp2 - 'A';
+ }
+ tp++;
+ tp2++;
+ }
+ }
+ tp = tmpbuf;
+ }
+ if (ntflag) {
+ tp = dotrans(tp);
+ }
+ if (mapflag) {
+ tp = domap(tp);
+ }
+ sendrequest((sunique) ? "STOU" : "STOR",
+ cp, tp, cp != tp || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+ return;
+ }
+ for (i = 1; i < argc; i++) {
+ char **cpp, **gargs;
+ glob_t gl;
+ int flags;
+
+ if (!doglob) {
+ if (mflag && confirm(argv[0], argv[i])) {
+ tp = (ntflag) ? dotrans(argv[i]) : argv[i];
+ tp = (mapflag) ? domap(tp) : tp;
+ sendrequest((sunique) ? "STOU" : "STOR",
+ argv[i], tp, tp != argv[i] || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ continue;
+ }
+
+ memset(&gl, 0, sizeof(gl));
+ flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
+ if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
+ warnx("%s: not found", argv[i]);
+ globfree(&gl);
+ continue;
+ }
+ for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) {
+ if (mflag && confirm(argv[0], *cpp)) {
+ tp = (ntflag) ? dotrans(*cpp) : *cpp;
+ tp = (mapflag) ? domap(tp) : tp;
+ sendrequest((sunique) ? "STOU" : "STOR",
+ *cpp, tp, *cpp != tp || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ globfree(&gl);
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+void
+reget(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ (void) getit(argc, argv, 1, "r+w");
+}
+
+void
+get(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ (void) getit(argc, argv, 0, restart_point ? "r+w" : "w" );
+}
+
+/*
+ * Receive one file.
+ */
+int
+getit(argc, argv, restartit, mode)
+ int argc;
+ char *argv[];
+ char *mode;
+ int restartit;
+{
+ int loc = 0;
+ char *oldargv1, *oldargv2;
+
+ if (argc == 2) {
+ argc++;
+ argv[2] = argv[1];
+ loc++;
+ }
+ if (argc < 2 && !another(&argc, &argv, "remote-file"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "local-file")) {
+usage:
+ printf("usage: %s remote-file [ local-file ]\n", argv[0]);
+ code = -1;
+ return (0);
+ }
+ oldargv1 = argv[1];
+ oldargv2 = argv[2];
+ if (!globulize(&argv[2])) {
+ code = -1;
+ return (0);
+ }
+ if (loc && mcase) {
+ char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
+
+ while (*tp && !islower(*tp)) {
+ tp++;
+ }
+ if (!*tp) {
+ tp = argv[2];
+ tp2 = tmpbuf;
+ while ((*tp2 = *tp) != NULL) {
+ if (isupper(*tp2)) {
+ *tp2 = 'a' + *tp2 - 'A';
+ }
+ tp++;
+ tp2++;
+ }
+ argv[2] = tmpbuf;
+ }
+ }
+ if (loc && ntflag)
+ argv[2] = dotrans(argv[2]);
+ if (loc && mapflag)
+ argv[2] = domap(argv[2]);
+ if (restartit) {
+ struct stat stbuf;
+ int ret;
+
+ ret = stat(argv[2], &stbuf);
+ if (restartit == 1) {
+ if (ret < 0) {
+ warn("local: %s", argv[2]);
+ return (0);
+ }
+ restart_point = stbuf.st_size;
+ } else {
+ if (ret == 0) {
+ int overbose;
+
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("MDTM %s", argv[1]) == COMPLETE) {
+ int yy, mo, day, hour, min, sec;
+ struct tm *tm;
+ verbose = overbose;
+ sscanf(reply_string,
+ "%*s %04d%02d%02d%02d%02d%02d",
+ &yy, &mo, &day, &hour, &min, &sec);
+ tm = gmtime(&stbuf.st_mtime);
+ tm->tm_mon++;
+ if (tm->tm_year > yy%100)
+ return (1);
+ if ((tm->tm_year == yy%100 &&
+ tm->tm_mon > mo) ||
+ (tm->tm_mon == mo &&
+ tm->tm_mday > day) ||
+ (tm->tm_mday == day &&
+ tm->tm_hour > hour) ||
+ (tm->tm_hour == hour &&
+ tm->tm_min > min) ||
+ (tm->tm_min == min &&
+ tm->tm_sec > sec))
+ return (1);
+ } else {
+ printf("%s\n", reply_string);
+ verbose = overbose;
+ return (0);
+ }
+ }
+ }
+ }
+
+ recvrequest("RETR", argv[2], argv[1], mode,
+ argv[1] != oldargv1 || argv[2] != oldargv2);
+ restart_point = 0;
+ return (0);
+}
+
+/* ARGSUSED */
+void
+mabort(signo)
+ int signo;
+{
+ int ointer;
+
+ printf("\n");
+ (void) fflush(stdout);
+ if (mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", mname)) {
+ interactive = ointer;
+ longjmp(jabort,0);
+ }
+ interactive = ointer;
+ }
+ mflag = 0;
+ longjmp(jabort,0);
+}
+
+/*
+ * Get multiple files.
+ */
+void
+mget(argc, argv)
+ int argc;
+ char **argv;
+{
+ sig_t oldintr;
+ int ch, ointer;
+ char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
+
+ if (argc < 2 && !another(&argc, &argv, "remote-files")) {
+ printf("usage: %s remote-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ while ((cp = remglob(argv,proxy)) != NULL) {
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ tp = cp;
+ if (mcase) {
+ for (tp2 = tmpbuf; ch = *tp++;)
+ *tp2++ = isupper(ch) ? tolower(ch) : ch;
+ tp = tmpbuf;
+ }
+ if (ntflag) {
+ tp = dotrans(tp);
+ }
+ if (mapflag) {
+ tp = domap(tp);
+ }
+ recvrequest("RETR", tp, cp, "w",
+ tp != cp || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mget")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT,oldintr);
+ mflag = 0;
+}
+
+char *
+remglob(argv,doswitch)
+ char *argv[];
+ int doswitch;
+{
+ char temp[16];
+ static char buf[MAXPATHLEN];
+ static FILE *ftemp = NULL;
+ static char **args;
+ int oldverbose, oldhash;
+ char *cp, *mode;
+
+ if (!mflag) {
+ if (!doglob) {
+ args = NULL;
+ }
+ else {
+ if (ftemp) {
+ (void) fclose(ftemp);
+ ftemp = NULL;
+ }
+ }
+ return (NULL);
+ }
+ if (!doglob) {
+ if (args == NULL)
+ args = argv;
+ if ((cp = *++args) == NULL)
+ args = NULL;
+ return (cp);
+ }
+ if (ftemp == NULL) {
+ (void) strcpy(temp, _PATH_TMP);
+ (void) mktemp(temp);
+ oldverbose = verbose, verbose = 0;
+ oldhash = hash, hash = 0;
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+ for (mode = "w"; *++argv != NULL; mode = "a")
+ recvrequest ("NLST", temp, *argv, mode, 0);
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+ verbose = oldverbose; hash = oldhash;
+ ftemp = fopen(temp, "r");
+ (void) unlink(temp);
+ if (ftemp == NULL) {
+ printf("can't find list of remote files, oops\n");
+ return (NULL);
+ }
+ }
+ if (fgets(buf, sizeof (buf), ftemp) == NULL) {
+ (void) fclose(ftemp), ftemp = NULL;
+ return (NULL);
+ }
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ return (buf);
+}
+
+char *
+onoff(bool)
+ int bool;
+{
+
+ return (bool ? "on" : "off");
+}
+
+/*
+ * Show status.
+ */
+/*ARGSUSED*/
+void
+status(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+
+ if (connected)
+ printf("Connected to %s.\n", hostname);
+ else
+ printf("Not connected.\n");
+ if (!proxy) {
+ pswitch(1);
+ if (connected) {
+ printf("Connected for proxy commands to %s.\n", hostname);
+ }
+ else {
+ printf("No proxy connection.\n");
+ }
+ pswitch(0);
+ }
+ printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
+ modename, typename, formname, structname);
+ printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
+ onoff(verbose), onoff(bell), onoff(interactive),
+ onoff(doglob));
+ printf("Store unique: %s; Receive unique: %s\n", onoff(sunique),
+ onoff(runique));
+ printf("Case: %s; CR stripping: %s\n",onoff(mcase),onoff(crflag));
+ if (ntflag) {
+ printf("Ntrans: (in) %s (out) %s\n", ntin,ntout);
+ }
+ else {
+ printf("Ntrans: off\n");
+ }
+ if (mapflag) {
+ printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
+ }
+ else {
+ printf("Nmap: off\n");
+ }
+ printf("Hash mark printing: %s; Use of PORT cmds: %s\n",
+ onoff(hash), onoff(sendport));
+ if (macnum > 0) {
+ printf("Macros:\n");
+ for (i=0; i<macnum; i++) {
+ printf("\t%s\n",macros[i].mac_name);
+ }
+ }
+ code = 0;
+}
+
+/*
+ * Set beep on cmd completed mode.
+ */
+/*VARARGS*/
+void
+setbell(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ bell = !bell;
+ printf("Bell mode %s.\n", onoff(bell));
+ code = bell;
+}
+
+/*
+ * Turn on packet tracing.
+ */
+/*VARARGS*/
+void
+settrace(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ trace = !trace;
+ printf("Packet tracing %s.\n", onoff(trace));
+ code = trace;
+}
+
+/*
+ * Toggle hash mark printing during transfers.
+ */
+/*VARARGS*/
+void
+sethash(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ hash = !hash;
+ printf("Hash mark printing %s", onoff(hash));
+ code = hash;
+ if (hash)
+ printf(" (%d bytes/hash mark)", 1024);
+ printf(".\n");
+}
+
+/*
+ * Turn on printing of server echo's.
+ */
+/*VARARGS*/
+void
+setverbose(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ verbose = !verbose;
+ printf("Verbose mode %s.\n", onoff(verbose));
+ code = verbose;
+}
+
+/*
+ * Toggle PORT cmd use before each data connection.
+ */
+/*VARARGS*/
+void
+setport(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ sendport = !sendport;
+ printf("Use of PORT cmds %s.\n", onoff(sendport));
+ code = sendport;
+}
+
+/*
+ * Turn on interactive prompting
+ * during mget, mput, and mdelete.
+ */
+/*VARARGS*/
+void
+setprompt(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ interactive = !interactive;
+ printf("Interactive mode %s.\n", onoff(interactive));
+ code = interactive;
+}
+
+/*
+ * Toggle metacharacter interpretation
+ * on local file names.
+ */
+/*VARARGS*/
+void
+setglob(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ doglob = !doglob;
+ printf("Globbing %s.\n", onoff(doglob));
+ code = doglob;
+}
+
+/*
+ * Set debugging mode on/off and/or
+ * set level of debugging.
+ */
+/*VARARGS*/
+void
+setdebug(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int val;
+
+ if (argc > 1) {
+ val = atoi(argv[1]);
+ if (val < 0) {
+ printf("%s: bad debugging value.\n", argv[1]);
+ code = -1;
+ return;
+ }
+ } else
+ val = !debug;
+ debug = val;
+ if (debug)
+ options |= SO_DEBUG;
+ else
+ options &= ~SO_DEBUG;
+ printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
+ code = debug > 0;
+}
+
+/*
+ * Set current working directory
+ * on remote machine.
+ */
+void
+cd(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "remote-directory")) {
+ printf("usage: %s remote-directory\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("CWD %s", argv[1]) == ERROR && code == 500) {
+ if (verbose)
+ printf("CWD command not recognized, trying XCWD\n");
+ (void) command("XCWD %s", argv[1]);
+ }
+}
+
+/*
+ * Set current working directory
+ * on local machine.
+ */
+void
+lcd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char buf[MAXPATHLEN];
+
+ if (argc < 2)
+ argc++, argv[1] = home;
+ if (argc != 2) {
+ printf("usage: %s local-directory\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (!globulize(&argv[1])) {
+ code = -1;
+ return;
+ }
+ if (chdir(argv[1]) < 0) {
+ warn("local: %s", argv[1]);
+ code = -1;
+ return;
+ }
+ if (getwd(buf) != NULL)
+ printf("Local directory now %s\n", buf);
+ else
+ warnx("getwd: %s", buf);
+ code = 0;
+}
+
+/*
+ * Delete a single file.
+ */
+void
+delete(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "remote-file")) {
+ printf("usage: %s remote-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("DELE %s", argv[1]);
+}
+
+/*
+ * Delete multiple files.
+ */
+void
+mdelete(argc, argv)
+ int argc;
+ char **argv;
+{
+ sig_t oldintr;
+ int ointer;
+ char *cp;
+
+ if (argc < 2 && !another(&argc, &argv, "remote-files")) {
+ printf("usage: %s remote-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ while ((cp = remglob(argv,0)) != NULL) {
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ (void) command("DELE %s", cp);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", "mdelete")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+/*
+ * Rename a remote file.
+ */
+void
+renamefile(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "from-name"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "to-name")) {
+usage:
+ printf("%s from-name to-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("RNFR %s", argv[1]) == CONTINUE)
+ (void) command("RNTO %s", argv[2]);
+}
+
+/*
+ * Get a directory listing
+ * of remote files.
+ */
+void
+ls(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cmd;
+
+ if (argc < 2)
+ argc++, argv[1] = NULL;
+ if (argc < 3)
+ argc++, argv[2] = "-";
+ if (argc > 3) {
+ printf("usage: %s remote-directory local-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ cmd = argv[0][0] == 'n' ? "NLST" : "LIST";
+ if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
+ code = -1;
+ return;
+ }
+ if (strcmp(argv[2], "-") && *argv[2] != '|')
+ if (!globulize(&argv[2]) || !confirm("output to local-file:", argv[2])) {
+ code = -1;
+ return;
+ }
+ recvrequest(cmd, argv[2], argv[1], "w", 0);
+}
+
+/*
+ * Get a directory listing
+ * of multiple remote files.
+ */
+void
+mls(argc, argv)
+ int argc;
+ char **argv;
+{
+ sig_t oldintr;
+ int ointer, i;
+ char *cmd, mode[1], *dest;
+
+ if (argc < 2 && !another(&argc, &argv, "remote-files"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "local-file")) {
+usage:
+ printf("usage: %s remote-files local-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ dest = argv[argc - 1];
+ argv[argc - 1] = NULL;
+ if (strcmp(dest, "-") && *dest != '|')
+ if (!globulize(&dest) ||
+ !confirm("output to local-file:", dest)) {
+ code = -1;
+ return;
+ }
+ cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ for (i = 1; mflag && i < argc-1; ++i) {
+ *mode = (i == 1) ? 'w' : 'a';
+ recvrequest(cmd, dest, argv[i], mode, 0);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", argv[0])) {
+ mflag ++;
+ }
+ interactive = ointer;
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+/*
+ * Do a shell escape
+ */
+/*ARGSUSED*/
+void
+shell(argc, argv)
+ int argc;
+ char **argv;
+{
+ pid_t pid;
+ sig_t old1, old2;
+ char shellnam[40], *shell, *namep;
+ union wait status;
+
+ old1 = signal (SIGINT, SIG_IGN);
+ old2 = signal (SIGQUIT, SIG_IGN);
+ if ((pid = fork()) == 0) {
+ for (pid = 3; pid < 20; pid++)
+ (void) close(pid);
+ (void) signal(SIGINT, SIG_DFL);
+ (void) signal(SIGQUIT, SIG_DFL);
+ shell = getenv("SHELL");
+ if (shell == NULL)
+ shell = _PATH_BSHELL;
+ namep = strrchr(shell,'/');
+ if (namep == NULL)
+ namep = shell;
+ (void) strcpy(shellnam,"-");
+ (void) strcat(shellnam, ++namep);
+ if (strcmp(namep, "sh") != 0)
+ shellnam[0] = '+';
+ if (debug) {
+ printf ("%s\n", shell);
+ (void) fflush (stdout);
+ }
+ if (argc > 1) {
+ execl(shell,shellnam,"-c",altarg,(char *)0);
+ }
+ else {
+ execl(shell,shellnam,(char *)0);
+ }
+ warn("%s", shell);
+ code = -1;
+ exit(1);
+ }
+ if (pid > 0)
+ while (wait((int *)&status) != pid)
+ ;
+ (void) signal(SIGINT, old1);
+ (void) signal(SIGQUIT, old2);
+ if (pid == -1) {
+ warn("%s", "Try again later");
+ code = -1;
+ }
+ else {
+ code = 0;
+ }
+}
+
+/*
+ * Send new user information (re-login)
+ */
+void
+user(argc, argv)
+ int argc;
+ char **argv;
+{
+ char acct[80];
+ int n, aflag = 0;
+
+ if (argc < 2)
+ (void) another(&argc, &argv, "username");
+ if (argc < 2 || argc > 4) {
+ printf("usage: %s username [password] [account]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ n = command("USER %s", argv[1]);
+ if (n == CONTINUE) {
+ if (argc < 3 )
+ argv[2] = getpass("Password: "), argc++;
+ n = command("PASS %s", argv[2]);
+ }
+ if (n == CONTINUE) {
+ if (argc < 4) {
+ printf("Account: "); (void) fflush(stdout);
+ (void) fgets(acct, sizeof(acct) - 1, stdin);
+ acct[strlen(acct) - 1] = '\0';
+ argv[3] = acct; argc++;
+ }
+ n = command("ACCT %s", argv[3]);
+ aflag++;
+ }
+ if (n != COMPLETE) {
+ fprintf(stdout, "Login failed.\n");
+ return;
+ }
+ if (!aflag && argc == 4) {
+ (void) command("ACCT %s", argv[3]);
+ }
+}
+
+/*
+ * Print working directory.
+ */
+/*VARARGS*/
+void
+pwd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int oldverbose = verbose;
+
+ /*
+ * If we aren't verbose, this doesn't do anything!
+ */
+ verbose = 1;
+ if (command("PWD") == ERROR && code == 500) {
+ printf("PWD command not recognized, trying XPWD\n");
+ (void) command("XPWD");
+ }
+ verbose = oldverbose;
+}
+
+/*
+ * Make a directory.
+ */
+void
+makedir(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "directory-name")) {
+ printf("usage: %s directory-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("MKD %s", argv[1]) == ERROR && code == 500) {
+ if (verbose)
+ printf("MKD command not recognized, trying XMKD\n");
+ (void) command("XMKD %s", argv[1]);
+ }
+}
+
+/*
+ * Remove a directory.
+ */
+void
+removedir(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "directory-name")) {
+ printf("usage: %s directory-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("RMD %s", argv[1]) == ERROR && code == 500) {
+ if (verbose)
+ printf("RMD command not recognized, trying XRMD\n");
+ (void) command("XRMD %s", argv[1]);
+ }
+}
+
+/*
+ * Send a line, verbatim, to the remote machine.
+ */
+void
+quote(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "command line to send")) {
+ printf("usage: %s line-to-send\n", argv[0]);
+ code = -1;
+ return;
+ }
+ quote1("", argc, argv);
+}
+
+/*
+ * Send a SITE command to the remote machine. The line
+ * is sent verbatim to the remote machine, except that the
+ * word "SITE" is added at the front.
+ */
+void
+site(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
+ printf("usage: %s line-to-send\n", argv[0]);
+ code = -1;
+ return;
+ }
+ quote1("SITE ", argc, argv);
+}
+
+/*
+ * Turn argv[1..argc) into a space-separated string, then prepend initial text.
+ * Send the result as a one-line command and get response.
+ */
+void
+quote1(initial, argc, argv)
+ char *initial;
+ int argc;
+ char **argv;
+{
+ int i, len;
+ char buf[BUFSIZ]; /* must be >= sizeof(line) */
+
+ (void) strcpy(buf, initial);
+ if (argc > 1) {
+ len = strlen(buf);
+ len += strlen(strcpy(&buf[len], argv[1]));
+ for (i = 2; i < argc; i++) {
+ buf[len++] = ' ';
+ len += strlen(strcpy(&buf[len], argv[i]));
+ }
+ }
+ if (command(buf) == PRELIM) {
+ while (getreply(0) == PRELIM)
+ continue;
+ }
+}
+
+void
+do_chmod(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "mode"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "file-name")) {
+usage:
+ printf("usage: %s mode file-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("SITE CHMOD %s %s", argv[1], argv[2]);
+}
+
+void
+do_umask(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int oldverbose = verbose;
+
+ verbose = 1;
+ (void) command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
+ verbose = oldverbose;
+}
+
+void
+idle(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int oldverbose = verbose;
+
+ verbose = 1;
+ (void) command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
+ verbose = oldverbose;
+}
+
+/*
+ * Ask the other side for help.
+ */
+void
+rmthelp(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int oldverbose = verbose;
+
+ verbose = 1;
+ (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
+ verbose = oldverbose;
+}
+
+/*
+ * Terminate session and exit.
+ */
+/*VARARGS*/
+void
+quit(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (connected)
+ disconnect(0, 0);
+ pswitch(1);
+ if (connected) {
+ disconnect(0, 0);
+ }
+ exit(0);
+}
+
+/*
+ * Terminate session, but don't exit.
+ */
+void
+disconnect(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (!connected)
+ return;
+ (void) command("QUIT");
+ if (cout) {
+ (void) fclose(cout);
+ }
+ cout = NULL;
+ connected = 0;
+ data = -1;
+ if (!proxy) {
+ macnum = 0;
+ }
+}
+
+int
+confirm(cmd, file)
+ char *cmd, *file;
+{
+ char line[BUFSIZ];
+
+ if (!interactive)
+ return (1);
+ printf("%s %s? ", cmd, file);
+ (void) fflush(stdout);
+ if (fgets(line, sizeof line, stdin) == NULL)
+ return (0);
+ return (*line != 'n' && *line != 'N');
+}
+
+void
+fatal(msg)
+ char *msg;
+{
+
+ errx(1, "%s", msg);
+}
+
+/*
+ * Glob a local file name specification with
+ * the expectation of a single return value.
+ * Can't control multiple values being expanded
+ * from the expression, we return only the first.
+ */
+int
+globulize(cpp)
+ char **cpp;
+{
+ glob_t gl;
+ int flags;
+
+ if (!doglob)
+ return (1);
+
+ flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
+ memset(&gl, 0, sizeof(gl));
+ if (glob(*cpp, flags, NULL, &gl) ||
+ gl.gl_pathc == 0) {
+ warnx("%s: not found", *cpp);
+ globfree(&gl);
+ return (0);
+ }
+ *cpp = strdup(gl.gl_pathv[0]); /* XXX - wasted memory */
+ globfree(&gl);
+ return (1);
+}
+
+void
+account(argc,argv)
+ int argc;
+ char **argv;
+{
+ char acct[50], *ap;
+
+ if (argc > 1) {
+ ++argv;
+ --argc;
+ (void) strncpy(acct,*argv,49);
+ acct[49] = '\0';
+ while (argc > 1) {
+ --argc;
+ ++argv;
+ (void) strncat(acct,*argv, 49-strlen(acct));
+ }
+ ap = acct;
+ }
+ else {
+ ap = getpass("Account:");
+ }
+ (void) command("ACCT %s", ap);
+}
+
+jmp_buf abortprox;
+
+void
+proxabort()
+{
+
+ if (!proxy) {
+ pswitch(1);
+ }
+ if (connected) {
+ proxflag = 1;
+ }
+ else {
+ proxflag = 0;
+ }
+ pswitch(0);
+ longjmp(abortprox,1);
+}
+
+void
+doproxy(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct cmd *c;
+ sig_t oldintr;
+
+ if (argc < 2 && !another(&argc, &argv, "command")) {
+ printf("usage: %s command\n", argv[0]);
+ code = -1;
+ return;
+ }
+ c = getcmd(argv[1]);
+ if (c == (struct cmd *) -1) {
+ printf("?Ambiguous command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (!c->c_proxy) {
+ printf("?Invalid proxy command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (setjmp(abortprox)) {
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, proxabort);
+ pswitch(1);
+ if (c->c_conn && !connected) {
+ printf("Not connected\n");
+ (void) fflush(stdout);
+ pswitch(0);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ (*c->c_handler)(argc-1, argv+1);
+ if (connected) {
+ proxflag = 1;
+ }
+ else {
+ proxflag = 0;
+ }
+ pswitch(0);
+ (void) signal(SIGINT, oldintr);
+}
+
+void
+setcase(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ mcase = !mcase;
+ printf("Case mapping %s.\n", onoff(mcase));
+ code = mcase;
+}
+
+void
+setcr(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ crflag = !crflag;
+ printf("Carriage Return stripping %s.\n", onoff(crflag));
+ code = crflag;
+}
+
+void
+setntrans(argc,argv)
+ int argc;
+ char *argv[];
+{
+ if (argc == 1) {
+ ntflag = 0;
+ printf("Ntrans off.\n");
+ code = ntflag;
+ return;
+ }
+ ntflag++;
+ code = ntflag;
+ (void) strncpy(ntin, argv[1], 16);
+ ntin[16] = '\0';
+ if (argc == 2) {
+ ntout[0] = '\0';
+ return;
+ }
+ (void) strncpy(ntout, argv[2], 16);
+ ntout[16] = '\0';
+}
+
+char *
+dotrans(name)
+ char *name;
+{
+ static char new[MAXPATHLEN];
+ char *cp1, *cp2 = new;
+ int i, ostop, found;
+
+ for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
+ continue;
+ for (cp1 = name; *cp1; cp1++) {
+ found = 0;
+ for (i = 0; *(ntin + i) && i < 16; i++) {
+ if (*cp1 == *(ntin + i)) {
+ found++;
+ if (i < ostop) {
+ *cp2++ = *(ntout + i);
+ }
+ break;
+ }
+ }
+ if (!found) {
+ *cp2++ = *cp1;
+ }
+ }
+ *cp2 = '\0';
+ return (new);
+}
+
+void
+setnmap(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cp;
+
+ if (argc == 1) {
+ mapflag = 0;
+ printf("Nmap off.\n");
+ code = mapflag;
+ return;
+ }
+ if (argc < 3 && !another(&argc, &argv, "mapout")) {
+ printf("Usage: %s [mapin mapout]\n",argv[0]);
+ code = -1;
+ return;
+ }
+ mapflag = 1;
+ code = 1;
+ cp = strchr(altarg, ' ');
+ if (proxy) {
+ while(*++cp == ' ')
+ continue;
+ altarg = cp;
+ cp = strchr(altarg, ' ');
+ }
+ *cp = '\0';
+ (void) strncpy(mapin, altarg, MAXPATHLEN - 1);
+ while (*++cp == ' ')
+ continue;
+ (void) strncpy(mapout, cp, MAXPATHLEN - 1);
+}
+
+char *
+domap(name)
+ char *name;
+{
+ static char new[MAXPATHLEN];
+ char *cp1 = name, *cp2 = mapin;
+ char *tp[9], *te[9];
+ int i, toks[9], toknum = 0, match = 1;
+
+ for (i=0; i < 9; ++i) {
+ toks[i] = 0;
+ }
+ while (match && *cp1 && *cp2) {
+ switch (*cp2) {
+ case '\\':
+ if (*++cp2 != *cp1) {
+ match = 0;
+ }
+ break;
+ case '$':
+ if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
+ if (*cp1 != *(++cp2+1)) {
+ toks[toknum = *cp2 - '1']++;
+ tp[toknum] = cp1;
+ while (*++cp1 && *(cp2+1)
+ != *cp1);
+ te[toknum] = cp1;
+ }
+ cp2++;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ if (*cp2 != *cp1) {
+ match = 0;
+ }
+ break;
+ }
+ if (match && *cp1) {
+ cp1++;
+ }
+ if (match && *cp2) {
+ cp2++;
+ }
+ }
+ if (!match && *cp1) /* last token mismatch */
+ {
+ toks[toknum] = 0;
+ }
+ cp1 = new;
+ *cp1 = '\0';
+ cp2 = mapout;
+ while (*cp2) {
+ match = 0;
+ switch (*cp2) {
+ case '\\':
+ if (*(cp2 + 1)) {
+ *cp1++ = *++cp2;
+ }
+ break;
+ case '[':
+LOOP:
+ if (*++cp2 == '$' && isdigit(*(cp2+1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ match = 1;
+ }
+ else if (toks[toknum = *cp2 - '1']) {
+ char *cp3 = tp[toknum];
+
+ while (cp3 != te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ match = 1;
+ }
+ }
+ else {
+ while (*cp2 && *cp2 != ',' &&
+ *cp2 != ']') {
+ if (*cp2 == '\\') {
+ cp2++;
+ }
+ else if (*cp2 == '$' &&
+ isdigit(*(cp2+1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ }
+ else if (toks[toknum =
+ *cp2 - '1']) {
+ char *cp3=tp[toknum];
+
+ while (cp3 !=
+ te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ }
+ }
+ else if (*cp2) {
+ *cp1++ = *cp2++;
+ }
+ }
+ if (!*cp2) {
+ printf("nmap: unbalanced brackets\n");
+ return (name);
+ }
+ match = 1;
+ cp2--;
+ }
+ if (match) {
+ while (*++cp2 && *cp2 != ']') {
+ if (*cp2 == '\\' && *(cp2 + 1)) {
+ cp2++;
+ }
+ }
+ if (!*cp2) {
+ printf("nmap: unbalanced brackets\n");
+ return (name);
+ }
+ break;
+ }
+ switch (*++cp2) {
+ case ',':
+ goto LOOP;
+ case ']':
+ break;
+ default:
+ cp2--;
+ goto LOOP;
+ }
+ break;
+ case '$':
+ if (isdigit(*(cp2 + 1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ }
+ else if (toks[toknum = *cp2 - '1']) {
+ char *cp3 = tp[toknum];
+
+ while (cp3 != te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ }
+ break;
+ }
+ /* intentional drop through */
+ default:
+ *cp1++ = *cp2;
+ break;
+ }
+ cp2++;
+ }
+ *cp1 = '\0';
+ if (!*new) {
+ return (name);
+ }
+ return (new);
+}
+
+void
+setsunique(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ sunique = !sunique;
+ printf("Store unique %s.\n", onoff(sunique));
+ code = sunique;
+}
+
+void
+setrunique(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ runique = !runique;
+ printf("Receive unique %s.\n", onoff(runique));
+ code = runique;
+}
+
+/* change directory to perent directory */
+void
+cdup(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (command("CDUP") == ERROR && code == 500) {
+ if (verbose)
+ printf("CDUP command not recognized, trying XCUP\n");
+ (void) command("XCUP");
+ }
+}
+
+/* restart transfer at specific point */
+void
+restart(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc != 2)
+ printf("restart: offset not specified\n");
+ else {
+ restart_point = atol(argv[1]);
+ printf("restarting at %qd. %s\n", restart_point,
+ "execute get, put or append to initiate transfer");
+ }
+}
+
+/* show remote system type */
+void
+syst(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ (void) command("SYST");
+}
+
+void
+macdef(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *tmp;
+ int c;
+
+ if (macnum == 16) {
+ printf("Limit of 16 macros have already been defined\n");
+ code = -1;
+ return;
+ }
+ if (argc < 2 && !another(&argc, &argv, "macro name")) {
+ printf("Usage: %s macro_name\n",argv[0]);
+ code = -1;
+ return;
+ }
+ if (interactive) {
+ printf("Enter macro line by line, terminating it with a null line\n");
+ }
+ (void) strncpy(macros[macnum].mac_name, argv[1], 8);
+ if (macnum == 0) {
+ macros[macnum].mac_start = macbuf;
+ }
+ else {
+ macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
+ }
+ tmp = macros[macnum].mac_start;
+ while (tmp != macbuf+4096) {
+ if ((c = getchar()) == EOF) {
+ printf("macdef:end of file encountered\n");
+ code = -1;
+ return;
+ }
+ if ((*tmp = c) == '\n') {
+ if (tmp == macros[macnum].mac_start) {
+ macros[macnum++].mac_end = tmp;
+ code = 0;
+ return;
+ }
+ if (*(tmp-1) == '\0') {
+ macros[macnum++].mac_end = tmp - 1;
+ code = 0;
+ return;
+ }
+ *tmp = '\0';
+ }
+ tmp++;
+ }
+ while (1) {
+ while ((c = getchar()) != '\n' && c != EOF)
+ /* LOOP */;
+ if (c == EOF || getchar() == '\n') {
+ printf("Macro not defined - 4k buffer exceeded\n");
+ code = -1;
+ return;
+ }
+ }
+}
+
+/*
+ * get size of file on remote machine
+ */
+void
+sizecmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "filename")) {
+ printf("usage: %s filename\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("SIZE %s", argv[1]);
+}
+
+/*
+ * get last modification time of file on remote machine
+ */
+void
+modtime(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int overbose;
+
+ if (argc < 2 && !another(&argc, &argv, "filename")) {
+ printf("usage: %s filename\n", argv[0]);
+ code = -1;
+ return;
+ }
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("MDTM %s", argv[1]) == COMPLETE) {
+ int yy, mo, day, hour, min, sec;
+ sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
+ &day, &hour, &min, &sec);
+ /* might want to print this in local time */
+ printf("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1],
+ mo, day, yy, hour, min, sec);
+ } else
+ printf("%s\n", reply_string);
+ verbose = overbose;
+}
+
+/*
+ * show status on reomte machine
+ */
+void
+rmtstatus(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ (void) command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
+}
+
+/*
+ * get file if modtime is more recent than current file
+ */
+void
+newer(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (getit(argc, argv, -1, "w"))
+ printf("Local file \"%s\" is newer than remote file \"%s\"\n",
+ argv[2], argv[1]);
+}
diff --git a/usr.bin/ftp/cmdtab.c b/usr.bin/ftp/cmdtab.c
new file mode 100644
index 0000000..db3b755
--- /dev/null
+++ b/usr.bin/ftp/cmdtab.c
@@ -0,0 +1,186 @@
+/*
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmdtab.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <stdio.h>
+#include "ftp_var.h"
+
+/*
+ * User FTP -- Command Tables.
+ */
+
+char accounthelp[] = "send account command to remote server";
+char appendhelp[] = "append to a file";
+char asciihelp[] = "set ascii transfer type";
+char beephelp[] = "beep when command completed";
+char binaryhelp[] = "set binary transfer type";
+char casehelp[] = "toggle mget upper/lower case id mapping";
+char cdhelp[] = "change remote working directory";
+char cduphelp[] = "change remote working directory to parent directory";
+char chmodhelp[] = "change file permissions of remote file";
+char connecthelp[] = "connect to remote tftp";
+char crhelp[] = "toggle carriage return stripping on ascii gets";
+char deletehelp[] = "delete remote file";
+char debughelp[] = "toggle/set debugging mode";
+char dirhelp[] = "list contents of remote directory";
+char disconhelp[] = "terminate ftp session";
+char domachelp[] = "execute macro";
+char formhelp[] = "set file transfer format";
+char globhelp[] = "toggle metacharacter expansion of local file names";
+char hashhelp[] = "toggle printing `#' for each buffer transferred";
+char helphelp[] = "print local help information";
+char idlehelp[] = "get (set) idle timer on remote side";
+char lcdhelp[] = "change local working directory";
+char lshelp[] = "list contents of remote directory";
+char macdefhelp[] = "define a macro";
+char mdeletehelp[] = "delete multiple files";
+char mdirhelp[] = "list contents of multiple remote directories";
+char mgethelp[] = "get multiple files";
+char mkdirhelp[] = "make directory on the remote machine";
+char mlshelp[] = "list contents of multiple remote directories";
+char modtimehelp[] = "show last modification time of remote file";
+char modehelp[] = "set file transfer mode";
+char mputhelp[] = "send multiple files";
+char newerhelp[] = "get file if remote file is newer than local file ";
+char nlisthelp[] = "nlist contents of remote directory";
+char nmaphelp[] = "set templates for default file name mapping";
+char ntranshelp[] = "set translation table for default file name mapping";
+char porthelp[] = "toggle use of PORT cmd for each data connection";
+char prompthelp[] = "force interactive prompting on multiple commands";
+char proxyhelp[] = "issue command on alternate connection";
+char pwdhelp[] = "print working directory on remote machine";
+char quithelp[] = "terminate ftp session and exit";
+char quotehelp[] = "send arbitrary ftp command";
+char receivehelp[] = "receive file";
+char regethelp[] = "get file restarting at end of local file";
+char remotehelp[] = "get help from remote server";
+char renamehelp[] = "rename file";
+char restarthelp[]= "restart file transfer at bytecount";
+char rmdirhelp[] = "remove directory on the remote machine";
+char rmtstatushelp[]="show status of remote machine";
+char runiquehelp[] = "toggle store unique for local files";
+char resethelp[] = "clear queued command replies";
+char sendhelp[] = "send one file";
+char sitehelp[] = "send site specific command to remote server\n\t\tTry \"rhelp site\" or \"site help\" for more information";
+char shellhelp[] = "escape to the shell";
+char sizecmdhelp[] = "show size of remote file";
+char statushelp[] = "show current status";
+char structhelp[] = "set file transfer structure";
+char suniquehelp[] = "toggle store unique on remote machine";
+char systemhelp[] = "show remote system type";
+char tenexhelp[] = "set tenex file transfer type";
+char tracehelp[] = "toggle packet tracing";
+char typehelp[] = "set file transfer type";
+char umaskhelp[] = "get (set) umask on remote side";
+char userhelp[] = "send new user information";
+char verbosehelp[] = "toggle verbose mode";
+
+struct cmd cmdtab[] = {
+ { "!", shellhelp, 0, 0, 0, shell },
+ { "$", domachelp, 1, 0, 0, domacro },
+ { "account", accounthelp, 0, 1, 1, account},
+ { "append", appendhelp, 1, 1, 1, put },
+ { "ascii", asciihelp, 0, 1, 1, setascii },
+ { "bell", beephelp, 0, 0, 0, setbell },
+ { "binary", binaryhelp, 0, 1, 1, setbinary },
+ { "bye", quithelp, 0, 0, 0, quit },
+ { "case", casehelp, 0, 0, 1, setcase },
+ { "cd", cdhelp, 0, 1, 1, cd },
+ { "cdup", cduphelp, 0, 1, 1, cdup },
+ { "chmod", chmodhelp, 0, 1, 1, do_chmod },
+ { "close", disconhelp, 0, 1, 1, disconnect },
+ { "cr", crhelp, 0, 0, 0, setcr },
+ { "delete", deletehelp, 0, 1, 1, delete },
+ { "debug", debughelp, 0, 0, 0, setdebug },
+ { "dir", dirhelp, 1, 1, 1, ls },
+ { "disconnect", disconhelp, 0, 1, 1, disconnect },
+ { "form", formhelp, 0, 1, 1, setform },
+ { "get", receivehelp, 1, 1, 1, get },
+ { "glob", globhelp, 0, 0, 0, setglob },
+ { "hash", hashhelp, 0, 0, 0, sethash },
+ { "help", helphelp, 0, 0, 1, help },
+ { "idle", idlehelp, 0, 1, 1, idle },
+ { "image", binaryhelp, 0, 1, 1, setbinary },
+ { "lcd", lcdhelp, 0, 0, 0, lcd },
+ { "ls", lshelp, 1, 1, 1, ls },
+ { "macdef", macdefhelp, 0, 0, 0, macdef },
+ { "mdelete", mdeletehelp, 1, 1, 1, mdelete },
+ { "mdir", mdirhelp, 1, 1, 1, mls },
+ { "mget", mgethelp, 1, 1, 1, mget },
+ { "mkdir", mkdirhelp, 0, 1, 1, makedir },
+ { "mls", mlshelp, 1, 1, 1, mls },
+ { "mode", modehelp, 0, 1, 1, setftmode },
+ { "modtime", modtimehelp, 0, 1, 1, modtime },
+ { "mput", mputhelp, 1, 1, 1, mput },
+ { "newer", newerhelp, 1, 1, 1, newer },
+ { "nmap", nmaphelp, 0, 0, 1, setnmap },
+ { "nlist", nlisthelp, 1, 1, 1, ls },
+ { "ntrans", ntranshelp, 0, 0, 1, setntrans },
+ { "open", connecthelp, 0, 0, 1, setpeer },
+ { "prompt", prompthelp, 0, 0, 0, setprompt },
+ { "proxy", proxyhelp, 0, 0, 1, doproxy },
+ { "sendport", porthelp, 0, 0, 0, setport },
+ { "put", sendhelp, 1, 1, 1, put },
+ { "pwd", pwdhelp, 0, 1, 1, pwd },
+ { "quit", quithelp, 0, 0, 0, quit },
+ { "quote", quotehelp, 1, 1, 1, quote },
+ { "recv", receivehelp, 1, 1, 1, get },
+ { "reget", regethelp, 1, 1, 1, reget },
+ { "rstatus", rmtstatushelp, 0, 1, 1, rmtstatus },
+ { "rhelp", remotehelp, 0, 1, 1, rmthelp },
+ { "rename", renamehelp, 0, 1, 1, renamefile },
+ { "reset", resethelp, 0, 1, 1, reset },
+ { "restart", restarthelp, 1, 1, 1, restart },
+ { "rmdir", rmdirhelp, 0, 1, 1, removedir },
+ { "runique", runiquehelp, 0, 0, 1, setrunique },
+ { "send", sendhelp, 1, 1, 1, put },
+ { "site", sitehelp, 0, 1, 1, site },
+ { "size", sizecmdhelp, 1, 1, 1, sizecmd },
+ { "status", statushelp, 0, 0, 1, status },
+ { "struct", structhelp, 0, 1, 1, setstruct },
+ { "system", systemhelp, 0, 1, 1, syst },
+ { "sunique", suniquehelp, 0, 0, 1, setsunique },
+ { "tenex", tenexhelp, 0, 1, 1, settenex },
+ { "trace", tracehelp, 0, 0, 0, settrace },
+ { "type", typehelp, 0, 1, 1, settype },
+ { "user", userhelp, 0, 1, 1, user },
+ { "umask", umaskhelp, 0, 1, 1, do_umask },
+ { "verbose", verbosehelp, 0, 0, 0, setverbose },
+ { "?", helphelp, 0, 0, 1, help },
+ { 0 },
+};
+
+int NCMDS = (sizeof (cmdtab) / sizeof (cmdtab[0])) - 1;
diff --git a/usr.bin/ftp/domacro.c b/usr.bin/ftp/domacro.c
new file mode 100644
index 0000000..9644487
--- /dev/null
+++ b/usr.bin/ftp/domacro.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 1985, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)domacro.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+#include <strings.h>
+
+#include "ftp_var.h"
+
+void
+domacro(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, j, count = 2, loopflg = 0;
+ char *cp1, *cp2, line2[200];
+ struct cmd *c;
+
+ if (argc < 2 && !another(&argc, &argv, "macro name")) {
+ printf("Usage: %s macro_name.\n", argv[0]);
+ code = -1;
+ return;
+ }
+ for (i = 0; i < macnum; ++i) {
+ if (!strncmp(argv[1], macros[i].mac_name, 9)) {
+ break;
+ }
+ }
+ if (i == macnum) {
+ printf("'%s' macro not found.\n", argv[1]);
+ code = -1;
+ return;
+ }
+ (void) strcpy(line2, line);
+TOP:
+ cp1 = macros[i].mac_start;
+ while (cp1 != macros[i].mac_end) {
+ while (isspace(*cp1)) {
+ cp1++;
+ }
+ cp2 = line;
+ while (*cp1 != '\0') {
+ switch(*cp1) {
+ case '\\':
+ *cp2++ = *++cp1;
+ break;
+ case '$':
+ if (isdigit(*(cp1+1))) {
+ j = 0;
+ while (isdigit(*++cp1)) {
+ j = 10*j + *cp1 - '0';
+ }
+ cp1--;
+ if (argc - 2 >= j) {
+ (void) strcpy(cp2, argv[j+1]);
+ cp2 += strlen(argv[j+1]);
+ }
+ break;
+ }
+ if (*(cp1+1) == 'i') {
+ loopflg = 1;
+ cp1++;
+ if (count < argc) {
+ (void) strcpy(cp2, argv[count]);
+ cp2 += strlen(argv[count]);
+ }
+ break;
+ }
+ /* intentional drop through */
+ default:
+ *cp2++ = *cp1;
+ break;
+ }
+ if (*cp1 != '\0') {
+ cp1++;
+ }
+ }
+ *cp2 = '\0';
+ makeargv();
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ code = -1;
+ }
+ else if (c == 0) {
+ printf("?Invalid command\n");
+ code = -1;
+ }
+ else if (c->c_conn && !connected) {
+ printf("Not connected.\n");
+ code = -1;
+ }
+ else {
+ if (verbose) {
+ printf("%s\n",line);
+ }
+ (*c->c_handler)(margc, margv);
+ if (bell && c->c_bell) {
+ (void) putchar('\007');
+ }
+ (void) strcpy(line, line2);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (cp1 != macros[i].mac_end) {
+ cp1++;
+ }
+ }
+ if (loopflg && ++count < argc) {
+ goto TOP;
+ }
+}
diff --git a/usr.bin/ftp/extern.h b/usr.bin/ftp/extern.h
new file mode 100644
index 0000000..349aea3
--- /dev/null
+++ b/usr.bin/ftp/extern.h
@@ -0,0 +1,152 @@
+/*-
+ * Copyright (c) 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ * @(#)extern.h 8.2 (Berkeley) 4/3/94
+ */
+
+struct timeval;
+struct fd_set;
+
+void abort_remote __P((FILE *));
+void abortpt __P(());
+void abortrecv __P(());
+void abortsend __P(());
+void account __P((int, char **));
+int another __P((int *, char ***, char *));
+void blkfree __P((char **));
+void cd __P((int, char **));
+void cdup __P((int, char **));
+void changetype __P((int, int));
+void cmdabort __P(());
+void cmdscanner __P((int));
+int command __P(());
+int confirm __P((char *, char *));
+FILE *dataconn __P((char *));
+void delete __P((int, char **));
+void disconnect __P((int, char **));
+void do_chmod __P((int, char **));
+void do_umask __P((int, char **));
+void domacro __P((int, char **));
+char *domap __P((char *));
+void doproxy __P((int, char **));
+char *dotrans __P((char *));
+int empty __P((struct fd_set *, int));
+void fatal __P((char *));
+void get __P((int, char **));
+struct cmd *getcmd __P((char *));
+int getit __P((int, char **, int, char *));
+int getreply __P((int));
+int globulize __P((char **));
+char *gunique __P((char *));
+void help __P((int, char **));
+char *hookup __P((char *, int));
+void idle __P((int, char **));
+int initconn __P((void));
+void intr __P(());
+void lcd __P((int, char **));
+int login __P((char *));
+void lostpeer __P(());
+void ls __P((int, char **));
+void mabort __P((int));
+void macdef __P((int, char **));
+void makeargv __P((void));
+void makedir __P((int, char **));
+void mdelete __P((int, char **));
+void mget __P((int, char **));
+void mls __P((int, char **));
+void modtime __P((int, char **));
+void mput __P((int, char **));
+char *onoff __P((int));
+void newer __P((int, char **));
+void proxabort __P(());
+void proxtrans __P((char *, char *, char *));
+void psabort __P(());
+void pswitch __P((int));
+void ptransfer __P((char *, long, struct timeval *, struct timeval *));
+void put __P((int, char **));
+void pwd __P((int, char **));
+void quit __P((int, char **));
+void quote __P((int, char **));
+void quote1 __P((char *, int, char **));
+void recvrequest __P((char *, char *, char *, char *, int));
+void reget __P((int, char **));
+char *remglob __P((char **, int));
+void removedir __P((int, char **));
+void renamefile __P((int, char **));
+void reset __P((int, char **));
+void restart __P((int, char **));
+void rmthelp __P((int, char **));
+void rmtstatus __P((int, char **));
+int ruserpass __P((char *, char **, char **, char **));
+void sendrequest __P((char *, char *, char *, int));
+void setascii __P((int, char **));
+void setbell __P((int, char **));
+void setbinary __P((int, char **));
+void setcase __P((int, char **));
+void setcr __P((int, char **));
+void setdebug __P((int, char **));
+void setform __P((int, char **));
+void setftmode __P((int, char **));
+void setglob __P((int, char **));
+void sethash __P((int, char **));
+void setnmap __P((int, char **));
+void setntrans __P((int, char **));
+void setpeer __P((int, char **));
+void setport __P((int, char **));
+void setprompt __P((int, char **));
+void setrunique __P((int, char **));
+void setstruct __P((int, char **));
+void setsunique __P((int, char **));
+void settenex __P((int, char **));
+void settrace __P((int, char **));
+void settype __P((int, char **));
+void setverbose __P((int, char **));
+void shell __P((int, char **));
+void site __P((int, char **));
+void sizecmd __P((int, char **));
+char *slurpstring __P((void));
+void status __P((int, char **));
+void syst __P((int, char **));
+void tvsub __P((struct timeval *, struct timeval *, struct timeval *));
+void user __P((int, char **));
+
+extern jmp_buf abortprox;
+extern int abrtflag;
+extern struct cmd cmdtab[];
+extern FILE *cout;
+extern int data;
+extern char *home;
+extern jmp_buf jabort;
+extern int proxy;
+extern char reply_string[];
+extern off_t restart_point;
+extern int NCMDS;
diff --git a/usr.bin/ftp/ftp.1 b/usr.bin/ftp/ftp.1
new file mode 100644
index 0000000..417da08
--- /dev/null
+++ b/usr.bin/ftp/ftp.1
@@ -0,0 +1,1136 @@
+.\" Copyright (c) 1985, 1989, 1990, 1993
+.\" 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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. 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.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt FTP 1
+.Os BSD 4.2
+.Sh NAME
+.Nm ftp
+.Nd
+.Tn ARPANET
+file transfer program
+.Sh SYNOPSIS
+.Nm ftp
+.Op Fl v
+.Op Fl d
+.Op Fl i
+.Op Fl n
+.Op Fl g
+.Op Ar host
+.Sh DESCRIPTION
+.Nm Ftp
+is the user interface to the
+.Tn ARPANET
+standard File Transfer Protocol.
+The program allows a user to transfer files to and from a
+remote network site.
+.Pp
+Options may be specified at the command line, or to the
+command interpreter.
+.Bl -tag -width flag
+.It Fl v
+Verbose option forces
+.Nm ftp
+to show all responses from the remote server, as well
+as report on data transfer statistics.
+.It Fl n
+Restrains
+.Nm ftp
+from attempting \*(Lqauto-login\*(Rq upon initial connection.
+If auto-login is enabled,
+.Nm ftp
+will check the
+.Pa .netrc
+(see below) file in the user's home directory for an entry describing
+an account on the remote machine.
+If no entry exists,
+.Nm ftp
+will prompt for the remote machine login name (default is the user
+identity on the local machine), and, if necessary, prompt for a password
+and an account with which to login.
+.It Fl i
+Turns off interactive prompting during
+multiple file transfers.
+.It Fl d
+Enables debugging.
+.It Fl g
+Disables file name globbing.
+.El
+.Pp
+The client host with which
+.Nm ftp
+is to communicate may be specified on the command line.
+If this is done,
+.Nm ftp
+will immediately attempt to establish a connection to an
+.Tn FTP
+server on that host; otherwise,
+.Nm ftp
+will enter its command interpreter and await instructions
+from the user.
+When
+.Nm ftp
+is awaiting commands from the user the prompt
+.Ql ftp>
+is provided to the user.
+The following commands are recognized
+by
+.Nm ftp :
+.Bl -tag -width Fl
+.It Ic \&! Op Ar command Op Ar args
+Invoke an interactive shell on the local machine.
+If there are arguments, the first is taken to be a command to execute
+directly, with the rest of the arguments as its arguments.
+.It Ic \&$ Ar macro-name Op Ar args
+Execute the macro
+.Ar macro-name
+that was defined with the
+.Ic macdef
+command.
+Arguments are passed to the macro unglobbed.
+.It Ic account Op Ar passwd
+Supply a supplemental password required by a remote system for access
+to resources once a login has been successfully completed.
+If no argument is included, the user will be prompted for an account
+password in a non-echoing input mode.
+.It Ic append Ar local-file Op Ar remote-file
+Append a local file to a file on the remote machine.
+If
+.Ar remote-file
+is left unspecified, the local file name is used in naming the
+remote file after being altered by any
+.Ic ntrans
+or
+.Ic nmap
+setting.
+File transfer uses the current settings for
+.Ic type ,
+.Ic format ,
+.Ic mode ,
+and
+.Ic structure .
+.It Ic ascii
+Set the file transfer
+.Ic type
+to network
+.Tn ASCII .
+This is the default type.
+.It Ic bell
+Arrange that a bell be sounded after each file transfer
+command is completed.
+.It Ic binary
+Set the file transfer
+.Ic type
+to support binary image transfer.
+.It Ic bye
+Terminate the
+.Tn FTP
+session with the remote server
+and exit
+.Nm ftp .
+An end of file will also terminate the session and exit.
+.It Ic case
+Toggle remote computer file name case mapping during
+.Ic mget
+commands.
+When
+.Ic case
+is on (default is off), remote computer file names with all letters in
+upper case are written in the local directory with the letters mapped
+to lower case.
+.It Ic \&cd Ar remote-directory
+Change the working directory on the remote machine
+to
+.Ar remote-directory .
+.It Ic cdup
+Change the remote machine working directory to the parent of the
+current remote machine working directory.
+.It Ic chmod Ar mode file-name
+Change the permission modes of the file
+.Ar file-name
+on the remote
+sytem to
+.Ar mode .
+.It Ic close
+Terminate the
+.Tn FTP
+session with the remote server, and
+return to the command interpreter.
+Any defined macros are erased.
+.It Ic \&cr
+Toggle carriage return stripping during
+ascii type file retrieval.
+Records are denoted by a carriage return/linefeed sequence
+during ascii type file transfer.
+When
+.Ic \&cr
+is on (the default), carriage returns are stripped from this
+sequence to conform with the
+.Ux
+single linefeed record
+delimiter.
+Records on
+.Pf non\- Ns Ux
+remote systems may contain single linefeeds;
+when an ascii type transfer is made, these linefeeds may be
+distinguished from a record delimiter only when
+.Ic \&cr
+is off.
+.It Ic delete Ar remote-file
+Delete the file
+.Ar remote-file
+on the remote machine.
+.It Ic debug Op Ar debug-value
+Toggle debugging mode.
+If an optional
+.Ar debug-value
+is specified it is used to set the debugging level.
+When debugging is on,
+.Nm ftp
+prints each command sent to the remote machine, preceded
+by the string
+.Ql \-\->
+.It Xo
+.Ic dir
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a listing of the directory contents in the
+directory,
+.Ar remote-directory ,
+and, optionally, placing the output in
+.Ar local-file .
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic dir
+output.
+If no directory is specified, the current working
+directory on the remote machine is used.
+If no local
+file is specified, or
+.Ar local-file
+is
+.Fl ,
+output comes to the terminal.
+.It Ic disconnect
+A synonym for
+.Ar close .
+.It Ic form Ar format
+Set the file transfer
+.Ic form
+to
+.Ar format .
+The default format is \*(Lqfile\*(Rq.
+.It Ic get Ar remote-file Op Ar local-file
+Retrieve the
+.Ar remote-file
+and store it on the local machine.
+If the local
+file name is not specified, it is given the same
+name it has on the remote machine, subject to
+alteration by the current
+.Ic case ,
+.Ic ntrans ,
+and
+.Ic nmap
+settings.
+The current settings for
+.Ic type ,
+.Ic form ,
+.Ic mode ,
+and
+.Ic structure
+are used while transferring the file.
+.It Ic glob
+Toggle filename expansion for
+.Ic mdelete ,
+.Ic mget
+and
+.Ic mput .
+If globbing is turned off with
+.Ic glob ,
+the file name arguments
+are taken literally and not expanded.
+Globbing for
+.Ic mput
+is done as in
+.Xr csh 1 .
+For
+.Ic mdelete
+and
+.Ic mget ,
+each remote file name is expanded
+separately on the remote machine and the lists are not merged.
+Expansion of a directory name is likely to be
+different from expansion of the name of an ordinary file:
+the exact result depends on the foreign operating system and ftp server,
+and can be previewed by doing
+.Ql mls remote-files \-
+Note:
+.Ic mget
+and
+.Ic mput
+are not meant to transfer
+entire directory subtrees of files.
+That can be done by
+transferring a
+.Xr tar 1
+archive of the subtree (in binary mode).
+.It Ic hash
+Toggle hash-sign (``#'') printing for each data block
+transferred.
+The size of a data block is 1024 bytes.
+.It Ic help Op Ar command
+Print an informative message about the meaning of
+.Ar command .
+If no argument is given,
+.Nm ftp
+prints a list of the known commands.
+.It Ic idle Op Ar seconds
+Set the inactivity timer on the remote server to
+.Ar seconds
+seconds.
+If
+.Ar seconds
+is omitted, the current inactivity timer is printed.
+.It Ic lcd Op Ar directory
+Change the working directory on the local machine.
+If
+no
+.Ar directory
+is specified, the user's home directory is used.
+.It Xo
+.Ic \&ls
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a listing of the contents of a
+directory on the remote machine.
+The listing includes any system-dependent information that the server
+chooses to include; for example, most
+.Ux
+systems will produce
+output from the command
+.Ql ls \-l .
+(See also
+.Ic nlist . )
+If
+.Ar remote-directory
+is left unspecified, the current working directory is used.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic \&ls
+output.
+If no local file is specified, or if
+.Ar local-file
+is
+.Sq Fl ,
+the output is sent to the terminal.
+.It Ic macdefNs Ar macro-name
+Define a macro.
+Subsequent lines are stored as the macro
+.Ar macro-name ;
+a null line (consecutive newline characters
+in a file or
+carriage returns from the terminal) terminates macro input mode.
+There is a limit of 16 macros and 4096 total characters in all
+defined macros.
+Macros remain defined until a
+.Ic close
+command is executed.
+The macro processor interprets `$' and `\e' as special characters.
+A `$' followed by a number (or numbers) is replaced by the
+corresponding argument on the macro invocation command line.
+A `$' followed by an `i' signals that macro processor that the
+executing macro is to be looped.
+On the first pass `$i' is
+replaced by the first argument on the macro invocation command line,
+on the second pass it is replaced by the second argument, and so on.
+A `\e' followed by any character is replaced by that character.
+Use the `\e' to prevent special treatment of the `$'.
+.It Ic mdelete Op Ar remote-files
+Delete the
+.Ar remote-files
+on the remote machine.
+.It Ic mdir Ar remote-files local-file
+Like
+.Ic dir ,
+except multiple remote files may be specified.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic mdir
+output.
+.It Ic mget Ar remote-files
+Expand the
+.Ar remote-files
+on the remote machine
+and do a
+.Ic get
+for each file name thus produced.
+See
+.Ic glob
+for details on the filename expansion.
+Resulting file names will then be processed according to
+.Ic case ,
+.Ic ntrans ,
+and
+.Ic nmap
+settings.
+Files are transferred into the local working directory,
+which can be changed with
+.Ql lcd directory ;
+new local directories can be created with
+.Ql "\&! mkdir directory" .
+.It Ic mkdir Ar directory-name
+Make a directory on the remote machine.
+.It Ic mls Ar remote-files local-file
+Like
+.Ic nlist ,
+except multiple remote files may be specified,
+and the
+.Ar local-file
+must be specified.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic mls
+output.
+.It Ic mode Op Ar mode-name
+Set the file transfer
+.Ic mode
+to
+.Ar mode-name .
+The default mode is \*(Lqstream\*(Rq mode.
+.It Ic modtime Ar file-name
+Show the last modification time of the file on the remote machine.
+.It Ic mput Ar local-files
+Expand wild cards in the list of local files given as arguments
+and do a
+.Ic put
+for each file in the resulting list.
+See
+.Ic glob
+for details of filename expansion.
+Resulting file names will then be processed according to
+.Ic ntrans
+and
+.Ic nmap
+settings.
+.It Ic newer Ar file-name
+Get the file only if the modification time of the remote file is more
+recent that the file on the current system.
+If the file does not
+exist on the current system, the remote file is considered
+.Ic newer .
+Otherwise, this command is identical to
+.Ar get .
+.It Xo
+.Ic nlist
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a list of the files in a
+directory on the remote machine.
+If
+.Ar remote-directory
+is left unspecified, the current working directory is used.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic nlist
+output.
+If no local file is specified, or if
+.Ar local-file
+is
+.Fl ,
+the output is sent to the terminal.
+.It Ic nmap Op Ar inpattern outpattern
+Set or unset the filename mapping mechanism.
+If no arguments are specified, the filename mapping mechanism is unset.
+If arguments are specified, remote filenames are mapped during
+.Ic mput
+commands and
+.Ic put
+commands issued without a specified remote target filename.
+If arguments are specified, local filenames are mapped during
+.Ic mget
+commands and
+.Ic get
+commands issued without a specified local target filename.
+This command is useful when connecting to a
+.No non\- Ns Ux
+remote computer
+with different file naming conventions or practices.
+The mapping follows the pattern set by
+.Ar inpattern
+and
+.Ar outpattern .
+.Op Ar Inpattern
+is a template for incoming filenames (which may have already been
+processed according to the
+.Ic ntrans
+and
+.Ic case
+settings).
+Variable templating is accomplished by including the
+sequences `$1', `$2', ..., `$9' in
+.Ar inpattern .
+Use `\\' to prevent this special treatment of the `$' character.
+All other characters are treated literally, and are used to determine the
+.Ic nmap
+.Op Ar inpattern
+variable values.
+For example, given
+.Ar inpattern
+$1.$2 and the remote file name "mydata.data", $1 would have the value
+"mydata", and $2 would have the value "data".
+The
+.Ar outpattern
+determines the resulting mapped filename.
+The sequences `$1', `$2', ...., `$9' are replaced by any value resulting
+from the
+.Ar inpattern
+template.
+The sequence `$0' is replace by the original filename.
+Additionally, the sequence
+.Ql Op Ar seq1 , Ar seq2
+is replaced by
+.Op Ar seq1
+if
+.Ar seq1
+is not a null string; otherwise it is replaced by
+.Ar seq2 .
+For example, the command
+.Pp
+.Bd -literal -offset indent -compact
+nmap $1.$2.$3 [$1,$2].[$2,file]
+.Ed
+.Pp
+would yield
+the output filename "myfile.data" for input filenames "myfile.data" and
+"myfile.data.old", "myfile.file" for the input filename "myfile", and
+"myfile.myfile" for the input filename ".myfile".
+Spaces may be included in
+.Ar outpattern ,
+as in the example: `nmap $1 sed "s/ *$//" > $1' .
+Use the `\e' character to prevent special treatment
+of the `$','[','[', and `,' characters.
+.It Ic ntrans Op Ar inchars Op Ar outchars
+Set or unset the filename character translation mechanism.
+If no arguments are specified, the filename character
+translation mechanism is unset.
+If arguments are specified, characters in
+remote filenames are translated during
+.Ic mput
+commands and
+.Ic put
+commands issued without a specified remote target filename.
+If arguments are specified, characters in
+local filenames are translated during
+.Ic mget
+commands and
+.Ic get
+commands issued without a specified local target filename.
+This command is useful when connecting to a
+.No non\- Ns Ux
+remote computer
+with different file naming conventions or practices.
+Characters in a filename matching a character in
+.Ar inchars
+are replaced with the corresponding character in
+.Ar outchars .
+If the character's position in
+.Ar inchars
+is longer than the length of
+.Ar outchars ,
+the character is deleted from the file name.
+.It Ic open Ar host Op Ar port
+Establish a connection to the specified
+.Ar host
+.Tn FTP
+server.
+An optional port number may be supplied,
+in which case,
+.Nm ftp
+will attempt to contact an
+.Tn FTP
+server at that port.
+If the
+.Ic auto-login
+option is on (default),
+.Nm ftp
+will also attempt to automatically log the user in to
+the
+.Tn FTP
+server (see below).
+.It Ic prompt
+Toggle interactive prompting.
+Interactive prompting
+occurs during multiple file transfers to allow the
+user to selectively retrieve or store files.
+If prompting is turned off (default is on), any
+.Ic mget
+or
+.Ic mput
+will transfer all files, and any
+.Ic mdelete
+will delete all files.
+.It Ic proxy Ar ftp-command
+Execute an ftp command on a secondary control connection.
+This command allows simultaneous connection to two remote ftp
+servers for transferring files between the two servers.
+The first
+.Ic proxy
+command should be an
+.Ic open ,
+to establish the secondary control connection.
+Enter the command "proxy ?" to see other ftp commands executable on the
+secondary connection.
+The following commands behave differently when prefaced by
+.Ic proxy :
+.Ic open
+will not define new macros during the auto-login process,
+.Ic close
+will not erase existing macro definitions,
+.Ic get
+and
+.Ic mget
+transfer files from the host on the primary control connection
+to the host on the secondary control connection, and
+.Ic put ,
+.Ic mput ,
+and
+.Ic append
+transfer files from the host on the secondary control connection
+to the host on the primary control connection.
+Third party file transfers depend upon support of the ftp protocol
+.Dv PASV
+command by the server on the secondary control connection.
+.It Ic put Ar local-file Op Ar remote-file
+Store a local file on the remote machine.
+If
+.Ar remote-file
+is left unspecified, the local file name is used
+after processing according to any
+.Ic ntrans
+or
+.Ic nmap
+settings
+in naming the remote file.
+File transfer uses the
+current settings for
+.Ic type ,
+.Ic format ,
+.Ic mode ,
+and
+.Ic structure .
+.It Ic pwd
+Print the name of the current working directory on the remote
+machine.
+.It Ic quit
+A synonym for
+.Ic bye .
+.It Ic quote Ar arg1 arg2 ...
+The arguments specified are sent, verbatim, to the remote
+.Tn FTP
+server.
+.It Ic recv Ar remote-file Op Ar local-file
+A synonym for get.
+.It Ic reget Ar remote-file Op Ar local-file
+Reget acts like get, except that if
+.Ar local-file
+exists and is
+smaller than
+.Ar remote-file ,
+.Ar local-file
+is presumed to be
+a partially transferred copy of
+.Ar remote-file
+and the transfer
+is continued from the apparent point of failure.
+This command
+is useful when transferring very large files over networks that
+are prone to dropping connections.
+.It Ic remotehelp Op Ar command-name
+Request help from the remote
+.Tn FTP
+server.
+If a
+.Ar command-name
+is specified it is supplied to the server as well.
+.It Ic remotestatus Op Ar file-name
+With no arguments, show status of remote machine.
+If
+.Ar file-name
+is specified, show status of
+.Ar file-name
+on remote machine.
+.It Xo
+.Ic rename
+.Op Ar from
+.Op Ar to
+.Xc
+Rename the file
+.Ar from
+on the remote machine, to the file
+.Ar to .
+.It Ic reset
+Clear reply queue.
+This command re-synchronizes command/reply sequencing with the remote
+ftp server.
+Resynchronization may be necessary following a violation of the ftp protocol
+by the remote server.
+.It Ic restart Ar marker
+Restart the immediately following
+.Ic get
+or
+.Ic put
+at the
+indicated
+.Ar marker .
+On
+.Ux
+systems, marker is usually a byte
+offset into the file.
+.It Ic rmdir Ar directory-name
+Delete a directory on the remote machine.
+.It Ic runique
+Toggle storing of files on the local system with unique filenames.
+If a file already exists with a name equal to the target
+local filename for a
+.Ic get
+or
+.Ic mget
+command, a ".1" is appended to the name.
+If the resulting name matches another existing file,
+a ".2" is appended to the original name.
+If this process continues up to ".99", an error
+message is printed, and the transfer does not take place.
+The generated unique filename will be reported.
+Note that
+.Ic runique
+will not affect local files generated from a shell command
+(see below).
+The default value is off.
+.It Ic send Ar local-file Op Ar remote-file
+A synonym for put.
+.It Ic sendport
+Toggle the use of
+.Dv PORT
+commands.
+By default,
+.Nm ftp
+will attempt to use a
+.Dv PORT
+command when establishing
+a connection for each data transfer.
+The use of
+.Dv PORT
+commands can prevent delays
+when performing multiple file transfers.
+If the
+.Dv PORT
+command fails,
+.Nm ftp
+will use the default data port.
+When the use of
+.Dv PORT
+commands is disabled, no attempt will be made to use
+.Dv PORT
+commands for each data transfer.
+This is useful
+for certain
+.Tn FTP
+implementations which do ignore
+.Dv PORT
+commands but, incorrectly, indicate they've been accepted.
+.It Ic site Ar arg1 arg2 ...
+The arguments specified are sent, verbatim, to the remote
+.Tn FTP
+server as a
+.Dv SITE
+command.
+.It Ic size Ar file-name
+Return size of
+.Ar file-name
+on remote machine.
+.It Ic status
+Show the current status of
+.Nm ftp .
+.It Ic struct Op Ar struct-name
+Set the file transfer
+.Ar structure
+to
+.Ar struct-name .
+By default \*(Lqstream\*(Rq structure is used.
+.It Ic sunique
+Toggle storing of files on remote machine under unique file names.
+Remote ftp server must support ftp protocol
+.Dv STOU
+command for
+successful completion.
+The remote server will report unique name.
+Default value is off.
+.It Ic system
+Show the type of operating system running on the remote machine.
+.It Ic tenex
+Set the file transfer type to that needed to
+talk to
+.Tn TENEX
+machines.
+.It Ic trace
+Toggle packet tracing.
+.It Ic type Op Ar type-name
+Set the file transfer
+.Ic type
+to
+.Ar type-name .
+If no type is specified, the current type
+is printed.
+The default type is network
+.Tn ASCII .
+.It Ic umask Op Ar newmask
+Set the default umask on the remote server to
+.Ar newmask .
+If
+.Ar newmask
+is omitted, the current umask is printed.
+.It Xo
+.Ic user Ar user-name
+.Op Ar password
+.Op Ar account
+.Xc
+Identify yourself to the remote
+.Tn FTP
+server.
+If the
+.Ar password
+is not specified and the server requires it,
+.Nm ftp
+will prompt the user for it (after disabling local echo).
+If an
+.Ar account
+field is not specified, and the
+.Tn FTP
+server
+requires it, the user will be prompted for it.
+If an
+.Ar account
+field is specified, an account command will
+be relayed to the remote server after the login sequence
+is completed if the remote server did not require it
+for logging in.
+Unless
+.Nm ftp
+is invoked with \*(Lqauto-login\*(Rq disabled, this
+process is done automatically on initial connection to
+the
+.Tn FTP
+server.
+.It Ic verbose
+Toggle verbose mode.
+In verbose mode, all responses from
+the
+.Tn FTP
+server are displayed to the user.
+In addition,
+if verbose is on, when a file transfer completes, statistics
+regarding the efficiency of the transfer are reported.
+By default,
+verbose is on.
+.It Ic ? Op Ar command
+A synonym for help.
+.El
+.Pp
+Command arguments which have embedded spaces may be quoted with
+quote `"' marks.
+.Sh ABORTING A FILE TRANSFER
+To abort a file transfer, use the terminal interrupt key
+(usually Ctrl-C).
+Sending transfers will be immediately halted.
+Receiving transfers will be halted by sending a ftp protocol
+.Dv ABOR
+command to the remote server, and discarding any further data received.
+The speed at which this is accomplished depends upon the remote
+server's support for
+.Dv ABOR
+processing.
+If the remote server does not support the
+.Dv ABOR
+command, an
+.Ql ftp>
+prompt will not appear until the remote server has completed
+sending the requested file.
+.Pp
+The terminal interrupt key sequence will be ignored when
+.Nm ftp
+has completed any local processing and is awaiting a reply
+from the remote server.
+A long delay in this mode may result from the ABOR processing described
+above, or from unexpected behavior by the remote server, including
+violations of the ftp protocol.
+If the delay results from unexpected remote server behavior, the local
+.Nm ftp
+program must be killed by hand.
+.Sh FILE NAMING CONVENTIONS
+Files specified as arguments to
+.Nm ftp
+commands are processed according to the following rules.
+.Bl -enum
+.It
+If the file name
+.Sq Fl
+is specified, the
+.Ar stdin
+(for reading) or
+.Ar stdout
+(for writing) is used.
+.It
+If the first character of the file name is
+.Sq \&| ,
+the
+remainder of the argument is interpreted as a shell command.
+.Nm Ftp
+then forks a shell, using
+.Xr popen 3
+with the argument supplied, and reads (writes) from the stdout
+(stdin).
+If the shell command includes spaces, the argument
+must be quoted; e.g.
+\*(Lq" ls -lt"\*(Rq.
+A particularly
+useful example of this mechanism is: \*(Lqdir more\*(Rq.
+.It
+Failing the above checks, if ``globbing'' is enabled,
+local file names are expanded
+according to the rules used in the
+.Xr csh 1 ;
+c.f. the
+.Ic glob
+command.
+If the
+.Nm ftp
+command expects a single local file (.e.g.
+.Ic put ) ,
+only the first filename generated by the "globbing" operation is used.
+.It
+For
+.Ic mget
+commands and
+.Ic get
+commands with unspecified local file names, the local filename is
+the remote filename, which may be altered by a
+.Ic case ,
+.Ic ntrans ,
+or
+.Ic nmap
+setting.
+The resulting filename may then be altered if
+.Ic runique
+is on.
+.It
+For
+.Ic mput
+commands and
+.Ic put
+commands with unspecified remote file names, the remote filename is
+the local filename, which may be altered by a
+.Ic ntrans
+or
+.Ic nmap
+setting.
+The resulting filename may then be altered by the remote server if
+.Ic sunique
+is on.
+.El
+.Sh FILE TRANSFER PARAMETERS
+The FTP specification specifies many parameters which may
+affect a file transfer.
+The
+.Ic type
+may be one of \*(Lqascii\*(Rq, \*(Lqimage\*(Rq (binary),
+\*(Lqebcdic\*(Rq, and \*(Lqlocal byte size\*(Rq (for
+.Tn PDP Ns -10's
+and
+.Tn PDP Ns -20's
+mostly).
+.Nm Ftp
+supports the ascii and image types of file transfer,
+plus local byte size 8 for
+.Ic tenex
+mode transfers.
+.Pp
+.Nm Ftp
+supports only the default values for the remaining
+file transfer parameters:
+.Ic mode ,
+.Ic form ,
+and
+.Ic struct .
+.Sh THE .netrc FILE
+The
+.Pa .netrc
+file contains login and initialization information
+used by the auto-login process.
+It resides in the user's home directory.
+The following tokens are recognized; they may be separated by spaces,
+tabs, or new-lines:
+.Bl -tag -width password
+.It Ic machine Ar name
+Identify a remote machine
+.Ar name .
+The auto-login process searches the
+.Pa .netrc
+file for a
+.Ic machine
+token that matches the remote machine specified on the
+.Nm ftp
+command line or as an
+.Ic open
+command argument.
+Once a match is made, the subsequent
+.Pa .netrc
+tokens are processed,
+stopping when the end of file is reached or another
+.Ic machine
+or a
+.Ic default
+token is encountered.
+.It Ic default
+This is the same as
+.Ic machine
+.Ar name
+except that
+.Ic default
+matches any name.
+There can be only one
+.Ic default
+token, and it must be after all
+.Ic machine
+tokens.
+This is normally used as:
+.Pp
+.Dl default login anonymous password user@site
+.Pp
+thereby giving the user
+.Ar automatic
+anonymous ftp login to
+machines not specified in
+.Pa .netrc .
+This can be overridden
+by using the
+.Fl n
+flag to disable auto-login.
+.It Ic login Ar name
+Identify a user on the remote machine.
+If this token is present, the auto-login process will initiate
+a login using the specified
+.Ar name .
+.It Ic password Ar string
+Supply a password.
+If this token is present, the auto-login process will supply the
+specified string if the remote server requires a password as part
+of the login process.
+Note that if this token is present in the
+.Pa .netrc
+file for any user other
+than
+.Ar anonymous ,
+.Nm ftp
+will abort the auto-login process if the
+.Pa .netrc
+is readable by
+anyone besides the user.
+.It Ic account Ar string
+Supply an additional account password.
+If this token is present, the auto-login process will supply the
+specified string if the remote server requires an additional
+account password, or the auto-login process will initiate an
+.Dv ACCT
+command if it does not.
+.It Ic macdef Ar name
+Define a macro.
+This token functions like the
+.Nm ftp
+.Ic macdef
+command functions.
+A macro is defined with the specified name; its contents begin with the
+next
+.Pa .netrc
+line and continue until a null line (consecutive new-line
+characters) is encountered.
+If a macro named
+.Ic init
+is defined, it is automatically executed as the last step in the
+auto-login process.
+.El
+.Sh ENVIRONMENT
+.Nm Ftp
+utilizes the following environment variables.
+.Bl -tag -width Fl
+.It Ev HOME
+For default location of a
+.Pa .netrc
+file, if one exists.
+.It Ev SHELL
+For default shell.
+.El
+.Sh SEE ALSO
+.Xr ftpd 8
+.Sh HISTORY
+The
+.Nm ftp
+command appeared in
+.Bx 4.2 .
+.Sh BUGS
+Correct execution of many commands depends upon proper behavior
+by the remote server.
+.Pp
+An error in the treatment of carriage returns
+in the
+.Bx 4.2
+ascii-mode transfer code
+has been corrected.
+This correction may result in incorrect transfers of binary files
+to and from
+.Bx 4.2
+servers using the ascii type.
+Avoid this problem by using the binary image type.
diff --git a/usr.bin/ftp/ftp.c b/usr.bin/ftp/ftp.c
new file mode 100644
index 0000000..19e8f8e
--- /dev/null
+++ b/usr.bin/ftp/ftp.c
@@ -0,0 +1,1470 @@
+/*
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ftp.c 8.4 (Berkeley) 4/6/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <arpa/ftp.h>
+#include <arpa/telnet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <varargs.h>
+
+#include "ftp_var.h"
+
+extern int h_errno;
+
+struct sockaddr_in hisctladdr;
+struct sockaddr_in data_addr;
+int data = -1;
+int abrtflag = 0;
+jmp_buf ptabort;
+int ptabflg;
+int ptflag = 0;
+struct sockaddr_in myctladdr;
+off_t restart_point = 0;
+
+
+FILE *cin, *cout;
+
+char *
+hookup(host, port)
+ char *host;
+ int port;
+{
+ struct hostent *hp = 0;
+ int s, len, tos;
+ static char hostnamebuf[80];
+
+ memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
+ hisctladdr.sin_addr.s_addr = inet_addr(host);
+ if (hisctladdr.sin_addr.s_addr != -1) {
+ hisctladdr.sin_family = AF_INET;
+ (void) strncpy(hostnamebuf, host, sizeof(hostnamebuf));
+ } else {
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ warnx("%s: %s", host, hstrerror(h_errno));
+ code = -1;
+ return ((char *) 0);
+ }
+ hisctladdr.sin_family = hp->h_addrtype;
+ memmove((caddr_t)&hisctladdr.sin_addr,
+ hp->h_addr_list[0], hp->h_length);
+ (void) strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf));
+ }
+ hostname = hostnamebuf;
+ s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ warn("socket");
+ code = -1;
+ return (0);
+ }
+ hisctladdr.sin_port = port;
+ while (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) {
+ if (hp && hp->h_addr_list[1]) {
+ int oerrno = errno;
+ char *ia;
+
+ ia = inet_ntoa(hisctladdr.sin_addr);
+ errno = oerrno;
+ warn("connect to address %s", ia);
+ hp->h_addr_list++;
+ memmove((caddr_t)&hisctladdr.sin_addr,
+ hp->h_addr_list[0], hp->h_length);
+ fprintf(stdout, "Trying %s...\n",
+ inet_ntoa(hisctladdr.sin_addr));
+ (void) close(s);
+ s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ warn("socket");
+ code = -1;
+ return (0);
+ }
+ continue;
+ }
+ warn("connect");
+ code = -1;
+ goto bad;
+ }
+ len = sizeof (myctladdr);
+ if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
+ warn("getsockname");
+ code = -1;
+ goto bad;
+ }
+#ifdef IP_TOS
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+ warn("setsockopt TOS (ignored)");
+#endif
+ cin = fdopen(s, "r");
+ cout = fdopen(s, "w");
+ if (cin == NULL || cout == NULL) {
+ warnx("fdopen failed.");
+ if (cin)
+ (void) fclose(cin);
+ if (cout)
+ (void) fclose(cout);
+ code = -1;
+ goto bad;
+ }
+ if (verbose)
+ printf("Connected to %s.\n", hostname);
+ if (getreply(0) > 2) { /* read startup message from server */
+ if (cin)
+ (void) fclose(cin);
+ if (cout)
+ (void) fclose(cout);
+ code = -1;
+ goto bad;
+ }
+#ifdef SO_OOBINLINE
+ {
+ int on = 1;
+
+ if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on))
+ < 0 && debug) {
+ warn("setsockopt");
+ }
+ }
+#endif /* SO_OOBINLINE */
+
+ return (hostname);
+bad:
+ (void) close(s);
+ return ((char *)0);
+}
+
+int
+login(host)
+ char *host;
+{
+ char tmp[80];
+ char *user, *pass, *acct;
+ int n, aflag = 0;
+
+ user = pass = acct = 0;
+ if (ruserpass(host, &user, &pass, &acct) < 0) {
+ code = -1;
+ return (0);
+ }
+ while (user == NULL) {
+ char *myname = getlogin();
+
+ if (myname == NULL) {
+ struct passwd *pp = getpwuid(getuid());
+
+ if (pp != NULL)
+ myname = pp->pw_name;
+ }
+ if (myname)
+ printf("Name (%s:%s): ", host, myname);
+ else
+ printf("Name (%s): ", host);
+ (void) fgets(tmp, sizeof(tmp) - 1, stdin);
+ tmp[strlen(tmp) - 1] = '\0';
+ if (*tmp == '\0')
+ user = myname;
+ else
+ user = tmp;
+ }
+ n = command("USER %s", user);
+ if (n == CONTINUE) {
+ if (pass == NULL)
+ pass = getpass("Password:");
+ n = command("PASS %s", pass);
+ }
+ if (n == CONTINUE) {
+ aflag++;
+ acct = getpass("Account:");
+ n = command("ACCT %s", acct);
+ }
+ if (n != COMPLETE) {
+ warnx("Login failed.");
+ return (0);
+ }
+ if (!aflag && acct != NULL)
+ (void) command("ACCT %s", acct);
+ if (proxy)
+ return (1);
+ for (n = 0; n < macnum; ++n) {
+ if (!strcmp("init", macros[n].mac_name)) {
+ (void) strcpy(line, "$init");
+ makeargv();
+ domacro(margc, margv);
+ break;
+ }
+ }
+ return (1);
+}
+
+void
+cmdabort()
+{
+
+ printf("\n");
+ (void) fflush(stdout);
+ abrtflag++;
+ if (ptflag)
+ longjmp(ptabort,1);
+}
+
+/*VARARGS*/
+int
+command(va_alist)
+va_dcl
+{
+ va_list ap;
+ char *fmt;
+ int r;
+ sig_t oldintr;
+
+ abrtflag = 0;
+ if (debug) {
+ printf("---> ");
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+ if (strncmp("PASS ", fmt, 5) == 0)
+ printf("PASS XXXX");
+ else
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ printf("\n");
+ (void) fflush(stdout);
+ }
+ if (cout == NULL) {
+ warn("No control connection for command");
+ code = -1;
+ return (0);
+ }
+ oldintr = signal(SIGINT, cmdabort);
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+ vfprintf(cout, fmt, ap);
+ va_end(ap);
+ fprintf(cout, "\r\n");
+ (void) fflush(cout);
+ cpend = 1;
+ r = getreply(!strcmp(fmt, "QUIT"));
+ if (abrtflag && oldintr != SIG_IGN)
+ (*oldintr)(SIGINT);
+ (void) signal(SIGINT, oldintr);
+ return (r);
+}
+
+char reply_string[BUFSIZ]; /* last line of previous reply */
+
+int
+getreply(expecteof)
+ int expecteof;
+{
+ int c, n;
+ int dig;
+ int originalcode = 0, continuation = 0;
+ sig_t oldintr;
+ int pflag = 0;
+ char *cp, *pt = pasv;
+
+ oldintr = signal(SIGINT, cmdabort);
+ for (;;) {
+ dig = n = code = 0;
+ cp = reply_string;
+ while ((c = getc(cin)) != '\n') {
+ if (c == IAC) { /* handle telnet commands */
+ switch (c = getc(cin)) {
+ case WILL:
+ case WONT:
+ c = getc(cin);
+ fprintf(cout, "%c%c%c", IAC, DONT, c);
+ (void) fflush(cout);
+ break;
+ case DO:
+ case DONT:
+ c = getc(cin);
+ fprintf(cout, "%c%c%c", IAC, WONT, c);
+ (void) fflush(cout);
+ break;
+ default:
+ break;
+ }
+ continue;
+ }
+ dig++;
+ if (c == EOF) {
+ if (expecteof) {
+ (void) signal(SIGINT,oldintr);
+ code = 221;
+ return (0);
+ }
+ lostpeer();
+ if (verbose) {
+ printf("421 Service not available, remote server has closed connection\n");
+ (void) fflush(stdout);
+ }
+ code = 421;
+ return (4);
+ }
+ if (c != '\r' && (verbose > 0 ||
+ (verbose > -1 && n == '5' && dig > 4))) {
+ if (proxflag &&
+ (dig == 1 || dig == 5 && verbose == 0))
+ printf("%s:",hostname);
+ (void) putchar(c);
+ }
+ if (dig < 4 && isdigit(c))
+ code = code * 10 + (c - '0');
+ if (!pflag && code == 227)
+ pflag = 1;
+ if (dig > 4 && pflag == 1 && isdigit(c))
+ pflag = 2;
+ if (pflag == 2) {
+ if (c != '\r' && c != ')')
+ *pt++ = c;
+ else {
+ *pt = '\0';
+ pflag = 3;
+ }
+ }
+ if (dig == 4 && c == '-') {
+ if (continuation)
+ code = 0;
+ continuation++;
+ }
+ if (n == 0)
+ n = c;
+ if (cp < &reply_string[sizeof(reply_string) - 1])
+ *cp++ = c;
+ }
+ if (verbose > 0 || verbose > -1 && n == '5') {
+ (void) putchar(c);
+ (void) fflush (stdout);
+ }
+ if (continuation && code != originalcode) {
+ if (originalcode == 0)
+ originalcode = code;
+ continue;
+ }
+ *cp = '\0';
+ if (n != '1')
+ cpend = 0;
+ (void) signal(SIGINT,oldintr);
+ if (code == 421 || originalcode == 421)
+ lostpeer();
+ if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN)
+ (*oldintr)(SIGINT);
+ return (n - '0');
+ }
+}
+
+int
+empty(mask, sec)
+ struct fd_set *mask;
+ int sec;
+{
+ struct timeval t;
+
+ t.tv_sec = (long) sec;
+ t.tv_usec = 0;
+ return (select(32, mask, (struct fd_set *) 0, (struct fd_set *) 0, &t));
+}
+
+jmp_buf sendabort;
+
+void
+abortsend()
+{
+
+ mflag = 0;
+ abrtflag = 0;
+ printf("\nsend aborted\nwaiting for remote to finish abort\n");
+ (void) fflush(stdout);
+ longjmp(sendabort, 1);
+}
+
+#define HASHBYTES 1024
+
+void
+sendrequest(cmd, local, remote, printnames)
+ char *cmd, *local, *remote;
+ int printnames;
+{
+ struct stat st;
+ struct timeval start, stop;
+ int c, d;
+ FILE *fin, *dout = 0, *popen();
+ int (*closefunc) __P((FILE *));
+ sig_t oldintr, oldintp;
+ long bytes = 0, hashbytes = HASHBYTES;
+ char *lmode, buf[BUFSIZ], *bufp;
+
+ if (verbose && printnames) {
+ if (local && *local != '-')
+ printf("local: %s ", local);
+ if (remote)
+ printf("remote: %s\n", remote);
+ }
+ if (proxy) {
+ proxtrans(cmd, local, remote);
+ return;
+ }
+ if (curtype != type)
+ changetype(type, 0);
+ closefunc = NULL;
+ oldintr = NULL;
+ oldintp = NULL;
+ lmode = "w";
+ if (setjmp(sendabort)) {
+ while (cpend) {
+ (void) getreply(0);
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (oldintr)
+ (void) signal(SIGINT,oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE,oldintp);
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, abortsend);
+ if (strcmp(local, "-") == 0)
+ fin = stdin;
+ else if (*local == '|') {
+ oldintp = signal(SIGPIPE,SIG_IGN);
+ fin = popen(local + 1, "r");
+ if (fin == NULL) {
+ warn("%s", local + 1);
+ (void) signal(SIGINT, oldintr);
+ (void) signal(SIGPIPE, oldintp);
+ code = -1;
+ return;
+ }
+ closefunc = pclose;
+ } else {
+ fin = fopen(local, "r");
+ if (fin == NULL) {
+ warn("local: %s", local);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ closefunc = fclose;
+ if (fstat(fileno(fin), &st) < 0 ||
+ (st.st_mode&S_IFMT) != S_IFREG) {
+ fprintf(stdout, "%s: not a plain file.\n", local);
+ (void) signal(SIGINT, oldintr);
+ fclose(fin);
+ code = -1;
+ return;
+ }
+ }
+ if (initconn()) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ code = -1;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ if (setjmp(sendabort))
+ goto abort;
+
+ if (restart_point &&
+ (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) {
+ int rc;
+
+ switch (curtype) {
+ case TYPE_A:
+ rc = fseek(fin, (long) restart_point, SEEK_SET);
+ break;
+ case TYPE_I:
+ case TYPE_L:
+ rc = lseek(fileno(fin), restart_point, SEEK_SET);
+ break;
+ }
+ if (rc < 0) {
+ warn("local: %s", local);
+ restart_point = 0;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ if (command("REST %ld", (long) restart_point)
+ != CONTINUE) {
+ restart_point = 0;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ restart_point = 0;
+ lmode = "r+w";
+ }
+ if (remote) {
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ } else
+ if (command("%s", cmd) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ dout = dataconn(lmode);
+ if (dout == NULL)
+ goto abort;
+ (void) gettimeofday(&start, (struct timezone *)0);
+ oldintp = signal(SIGPIPE, SIG_IGN);
+ switch (curtype) {
+
+ case TYPE_I:
+ case TYPE_L:
+ errno = d = 0;
+ while ((c = read(fileno(fin), buf, sizeof (buf))) > 0) {
+ bytes += c;
+ for (bufp = buf; c > 0; c -= d, bufp += d)
+ if ((d = write(fileno(dout), bufp, c)) <= 0)
+ break;
+ if (hash) {
+ while (bytes >= hashbytes) {
+ (void) putchar('#');
+ hashbytes += HASHBYTES;
+ }
+ (void) fflush(stdout);
+ }
+ }
+ if (hash && bytes > 0) {
+ if (bytes < HASHBYTES)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (c < 0)
+ warn("local: %s", local);
+ if (d < 0) {
+ if (errno != EPIPE)
+ warn("netout");
+ bytes = -1;
+ }
+ break;
+
+ case TYPE_A:
+ while ((c = getc(fin)) != EOF) {
+ if (c == '\n') {
+ while (hash && (bytes >= hashbytes)) {
+ (void) putchar('#');
+ (void) fflush(stdout);
+ hashbytes += HASHBYTES;
+ }
+ if (ferror(dout))
+ break;
+ (void) putc('\r', dout);
+ bytes++;
+ }
+ (void) putc(c, dout);
+ bytes++;
+ /* if (c == '\r') { */
+ /* (void) putc('\0', dout); // this violates rfc */
+ /* bytes++; */
+ /* } */
+ }
+ if (hash) {
+ if (bytes < hashbytes)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (ferror(fin))
+ warn("local: %s", local);
+ if (ferror(dout)) {
+ if (errno != EPIPE)
+ warn("netout");
+ bytes = -1;
+ }
+ break;
+ }
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ (void) fclose(dout);
+ (void) getreply(0);
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (bytes > 0)
+ ptransfer("sent", bytes, &start, &stop);
+ return;
+abort:
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (!cpend) {
+ code = -1;
+ return;
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (dout)
+ (void) fclose(dout);
+ (void) getreply(0);
+ code = -1;
+ if (closefunc != NULL && fin != NULL)
+ (*closefunc)(fin);
+ if (bytes > 0)
+ ptransfer("sent", bytes, &start, &stop);
+}
+
+jmp_buf recvabort;
+
+void
+abortrecv()
+{
+
+ mflag = 0;
+ abrtflag = 0;
+ printf("\nreceive aborted\nwaiting for remote to finish abort\n");
+ (void) fflush(stdout);
+ longjmp(recvabort, 1);
+}
+
+void
+recvrequest(cmd, local, remote, lmode, printnames)
+ char *cmd, *local, *remote, *lmode;
+ int printnames;
+{
+ FILE *fout, *din = 0;
+ int (*closefunc) __P((FILE *));
+ sig_t oldintr, oldintp;
+ int c, d, is_retr, tcrflag, bare_lfs = 0;
+ static int bufsize;
+ static char *buf;
+ long bytes = 0, hashbytes = HASHBYTES;
+ struct timeval start, stop;
+ struct stat st;
+
+ is_retr = strcmp(cmd, "RETR") == 0;
+ if (is_retr && verbose && printnames) {
+ if (local && *local != '-')
+ printf("local: %s ", local);
+ if (remote)
+ printf("remote: %s\n", remote);
+ }
+ if (proxy && is_retr) {
+ proxtrans(cmd, local, remote);
+ return;
+ }
+ closefunc = NULL;
+ oldintr = NULL;
+ oldintp = NULL;
+ tcrflag = !crflag && is_retr;
+ if (setjmp(recvabort)) {
+ while (cpend) {
+ (void) getreply(0);
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (oldintr)
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, abortrecv);
+ if (strcmp(local, "-") && *local != '|') {
+ if (access(local, 2) < 0) {
+ char *dir = strrchr(local, '/');
+
+ if (errno != ENOENT && errno != EACCES) {
+ warn("local: %s", local);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (dir != NULL)
+ *dir = 0;
+ d = access(dir ? local : ".", 2);
+ if (dir != NULL)
+ *dir = '/';
+ if (d < 0) {
+ warn("local: %s", local);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (!runique && errno == EACCES &&
+ chmod(local, 0600) < 0) {
+ warn("local: %s", local);
+ (void) signal(SIGINT, oldintr);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (runique && errno == EACCES &&
+ (local = gunique(local)) == NULL) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ }
+ else if (runique && (local = gunique(local)) == NULL) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ }
+ if (!is_retr) {
+ if (curtype != TYPE_A)
+ changetype(TYPE_A, 0);
+ } else if (curtype != type)
+ changetype(type, 0);
+ if (initconn()) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (setjmp(recvabort))
+ goto abort;
+ if (is_retr && restart_point &&
+ command("REST %ld", (long) restart_point) != CONTINUE)
+ return;
+ if (remote) {
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ } else {
+ if (command("%s", cmd) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ }
+ din = dataconn("r");
+ if (din == NULL)
+ goto abort;
+ if (strcmp(local, "-") == 0)
+ fout = stdout;
+ else if (*local == '|') {
+ oldintp = signal(SIGPIPE, SIG_IGN);
+ fout = popen(local + 1, "w");
+ if (fout == NULL) {
+ warn("%s", local+1);
+ goto abort;
+ }
+ closefunc = pclose;
+ } else {
+ fout = fopen(local, lmode);
+ if (fout == NULL) {
+ warn("local: %s", local);
+ goto abort;
+ }
+ closefunc = fclose;
+ }
+ if (fstat(fileno(fout), &st) < 0 || st.st_blksize == 0)
+ st.st_blksize = BUFSIZ;
+ if (st.st_blksize > bufsize) {
+ if (buf)
+ (void) free(buf);
+ buf = malloc((unsigned)st.st_blksize);
+ if (buf == NULL) {
+ warn("malloc");
+ bufsize = 0;
+ goto abort;
+ }
+ bufsize = st.st_blksize;
+ }
+ (void) gettimeofday(&start, (struct timezone *)0);
+ switch (curtype) {
+
+ case TYPE_I:
+ case TYPE_L:
+ if (restart_point &&
+ lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
+ warn("local: %s", local);
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ return;
+ }
+ errno = d = 0;
+ while ((c = read(fileno(din), buf, bufsize)) > 0) {
+ if ((d = write(fileno(fout), buf, c)) != c)
+ break;
+ bytes += c;
+ if (hash) {
+ while (bytes >= hashbytes) {
+ (void) putchar('#');
+ hashbytes += HASHBYTES;
+ }
+ (void) fflush(stdout);
+ }
+ }
+ if (hash && bytes > 0) {
+ if (bytes < HASHBYTES)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (c < 0) {
+ if (errno != EPIPE)
+ warn("netin");
+ bytes = -1;
+ }
+ if (d < c) {
+ if (d < 0)
+ warn("local: %s", local);
+ else
+ warnx("%s: short write", local);
+ }
+ break;
+
+ case TYPE_A:
+ if (restart_point) {
+ int i, n, ch;
+
+ if (fseek(fout, 0L, SEEK_SET) < 0)
+ goto done;
+ n = restart_point;
+ for (i = 0; i++ < n;) {
+ if ((ch = getc(fout)) == EOF)
+ goto done;
+ if (ch == '\n')
+ i++;
+ }
+ if (fseek(fout, 0L, SEEK_CUR) < 0) {
+done:
+ warn("local: %s", local);
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ return;
+ }
+ }
+ while ((c = getc(din)) != EOF) {
+ if (c == '\n')
+ bare_lfs++;
+ while (c == '\r') {
+ while (hash && (bytes >= hashbytes)) {
+ (void) putchar('#');
+ (void) fflush(stdout);
+ hashbytes += HASHBYTES;
+ }
+ bytes++;
+ if ((c = getc(din)) != '\n' || tcrflag) {
+ if (ferror(fout))
+ goto break2;
+ (void) putc('\r', fout);
+ if (c == '\0') {
+ bytes++;
+ goto contin2;
+ }
+ if (c == EOF)
+ goto contin2;
+ }
+ }
+ (void) putc(c, fout);
+ bytes++;
+ contin2: ;
+ }
+break2:
+ if (bare_lfs) {
+ printf("WARNING! %d bare linefeeds received in ASCII mode\n", bare_lfs);
+ printf("File may not have transferred correctly.\n");
+ }
+ if (hash) {
+ if (bytes < hashbytes)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (ferror(din)) {
+ if (errno != EPIPE)
+ warn("netin");
+ bytes = -1;
+ }
+ if (ferror(fout))
+ warn("local: %s", local);
+ break;
+ }
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ (void) fclose(din);
+ (void) getreply(0);
+ if (bytes > 0 && is_retr)
+ ptransfer("received", bytes, &start, &stop);
+ return;
+abort:
+
+/* abort using RFC959 recommended IP,SYNC sequence */
+
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintr);
+ (void) signal(SIGINT, SIG_IGN);
+ if (!cpend) {
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+
+ abort_remote(din);
+ code = -1;
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (closefunc != NULL && fout != NULL)
+ (*closefunc)(fout);
+ if (din)
+ (void) fclose(din);
+ if (bytes > 0)
+ ptransfer("received", bytes, &start, &stop);
+ (void) signal(SIGINT, oldintr);
+}
+
+/*
+ * Need to start a listen on the data channel before we send the command,
+ * otherwise the server's connect may fail.
+ */
+int
+initconn()
+{
+ char *p, *a;
+ int result, len, tmpno = 0;
+ int on = 1;
+
+noport:
+ data_addr = myctladdr;
+ if (sendport)
+ data_addr.sin_port = 0; /* let system pick one */
+ if (data != -1)
+ (void) close(data);
+ data = socket(AF_INET, SOCK_STREAM, 0);
+ if (data < 0) {
+ warn("socket");
+ if (tmpno)
+ sendport = 1;
+ return (1);
+ }
+ if (!sendport)
+ if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) {
+ warn("setsockopt (reuse address)");
+ goto bad;
+ }
+ if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
+ warn("bind");
+ goto bad;
+ }
+ if (options & SO_DEBUG &&
+ setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0)
+ warn("setsockopt (ignored)");
+ len = sizeof (data_addr);
+ if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
+ warn("getsockname");
+ goto bad;
+ }
+ if (listen(data, 1) < 0)
+ warn("listen");
+ if (sendport) {
+ a = (char *)&data_addr.sin_addr;
+ p = (char *)&data_addr.sin_port;
+#define UC(b) (((int)b)&0xff)
+ result =
+ command("PORT %d,%d,%d,%d,%d,%d",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ if (result == ERROR && sendport == -1) {
+ sendport = 0;
+ tmpno = 1;
+ goto noport;
+ }
+ return (result != COMPLETE);
+ }
+ if (tmpno)
+ sendport = 1;
+#ifdef IP_TOS
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
+ warn("setsockopt TOS (ignored)");
+#endif
+ return (0);
+bad:
+ (void) close(data), data = -1;
+ if (tmpno)
+ sendport = 1;
+ return (1);
+}
+
+FILE *
+dataconn(lmode)
+ char *lmode;
+{
+ struct sockaddr_in from;
+ int s, fromlen = sizeof (from), tos;
+
+ s = accept(data, (struct sockaddr *) &from, &fromlen);
+ if (s < 0) {
+ warn("accept");
+ (void) close(data), data = -1;
+ return (NULL);
+ }
+ (void) close(data);
+ data = s;
+#ifdef IP_TOS
+ tos = IPTOS_THROUGHPUT;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+ warn("setsockopt TOS (ignored)");
+#endif
+ return (fdopen(data, lmode));
+}
+
+void
+ptransfer(direction, bytes, t0, t1)
+ char *direction;
+ long bytes;
+ struct timeval *t0, *t1;
+{
+ struct timeval td;
+ float s, bs;
+
+ if (verbose) {
+ tvsub(&td, t1, t0);
+ s = td.tv_sec + (td.tv_usec / 1000000.);
+#define nz(x) ((x) == 0 ? 1 : (x))
+ bs = bytes / nz(s);
+ printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n",
+ bytes, direction, s, bs / 1024.);
+ }
+}
+
+/*
+void
+tvadd(tsum, t0)
+ struct timeval *tsum, *t0;
+{
+
+ tsum->tv_sec += t0->tv_sec;
+ tsum->tv_usec += t0->tv_usec;
+ if (tsum->tv_usec > 1000000)
+ tsum->tv_sec++, tsum->tv_usec -= 1000000;
+}
+*/
+
+void
+tvsub(tdiff, t1, t0)
+ struct timeval *tdiff, *t1, *t0;
+{
+
+ tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
+ tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
+ if (tdiff->tv_usec < 0)
+ tdiff->tv_sec--, tdiff->tv_usec += 1000000;
+}
+
+void
+psabort()
+{
+
+ abrtflag++;
+}
+
+void
+pswitch(flag)
+ int flag;
+{
+ sig_t oldintr;
+ static struct comvars {
+ int connect;
+ char name[MAXHOSTNAMELEN];
+ struct sockaddr_in mctl;
+ struct sockaddr_in hctl;
+ FILE *in;
+ FILE *out;
+ int tpe;
+ int curtpe;
+ int cpnd;
+ int sunqe;
+ int runqe;
+ int mcse;
+ int ntflg;
+ char nti[17];
+ char nto[17];
+ int mapflg;
+ char mi[MAXPATHLEN];
+ char mo[MAXPATHLEN];
+ } proxstruct, tmpstruct;
+ struct comvars *ip, *op;
+
+ abrtflag = 0;
+ oldintr = signal(SIGINT, psabort);
+ if (flag) {
+ if (proxy)
+ return;
+ ip = &tmpstruct;
+ op = &proxstruct;
+ proxy++;
+ } else {
+ if (!proxy)
+ return;
+ ip = &proxstruct;
+ op = &tmpstruct;
+ proxy = 0;
+ }
+ ip->connect = connected;
+ connected = op->connect;
+ if (hostname) {
+ (void) strncpy(ip->name, hostname, sizeof(ip->name) - 1);
+ ip->name[strlen(ip->name)] = '\0';
+ } else
+ ip->name[0] = 0;
+ hostname = op->name;
+ ip->hctl = hisctladdr;
+ hisctladdr = op->hctl;
+ ip->mctl = myctladdr;
+ myctladdr = op->mctl;
+ ip->in = cin;
+ cin = op->in;
+ ip->out = cout;
+ cout = op->out;
+ ip->tpe = type;
+ type = op->tpe;
+ ip->curtpe = curtype;
+ curtype = op->curtpe;
+ ip->cpnd = cpend;
+ cpend = op->cpnd;
+ ip->sunqe = sunique;
+ sunique = op->sunqe;
+ ip->runqe = runique;
+ runique = op->runqe;
+ ip->mcse = mcase;
+ mcase = op->mcse;
+ ip->ntflg = ntflag;
+ ntflag = op->ntflg;
+ (void) strncpy(ip->nti, ntin, 16);
+ (ip->nti)[strlen(ip->nti)] = '\0';
+ (void) strcpy(ntin, op->nti);
+ (void) strncpy(ip->nto, ntout, 16);
+ (ip->nto)[strlen(ip->nto)] = '\0';
+ (void) strcpy(ntout, op->nto);
+ ip->mapflg = mapflag;
+ mapflag = op->mapflg;
+ (void) strncpy(ip->mi, mapin, MAXPATHLEN - 1);
+ (ip->mi)[strlen(ip->mi)] = '\0';
+ (void) strcpy(mapin, op->mi);
+ (void) strncpy(ip->mo, mapout, MAXPATHLEN - 1);
+ (ip->mo)[strlen(ip->mo)] = '\0';
+ (void) strcpy(mapout, op->mo);
+ (void) signal(SIGINT, oldintr);
+ if (abrtflag) {
+ abrtflag = 0;
+ (*oldintr)(SIGINT);
+ }
+}
+
+void
+abortpt()
+{
+
+ printf("\n");
+ (void) fflush(stdout);
+ ptabflg++;
+ mflag = 0;
+ abrtflag = 0;
+ longjmp(ptabort, 1);
+}
+
+void
+proxtrans(cmd, local, remote)
+ char *cmd, *local, *remote;
+{
+ sig_t oldintr;
+ int secndflag = 0, prox_type, nfnd;
+ char *cmd2;
+ struct fd_set mask;
+
+ if (strcmp(cmd, "RETR"))
+ cmd2 = "RETR";
+ else
+ cmd2 = runique ? "STOU" : "STOR";
+ if ((prox_type = type) == 0) {
+ if (unix_server && unix_proxy)
+ prox_type = TYPE_I;
+ else
+ prox_type = TYPE_A;
+ }
+ if (curtype != prox_type)
+ changetype(prox_type, 1);
+ if (command("PASV") != COMPLETE) {
+ printf("proxy server does not support third party transfers.\n");
+ return;
+ }
+ pswitch(0);
+ if (!connected) {
+ printf("No primary connection\n");
+ pswitch(1);
+ code = -1;
+ return;
+ }
+ if (curtype != prox_type)
+ changetype(prox_type, 1);
+ if (command("PORT %s", pasv) != COMPLETE) {
+ pswitch(1);
+ return;
+ }
+ if (setjmp(ptabort))
+ goto abort;
+ oldintr = signal(SIGINT, abortpt);
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ pswitch(1);
+ return;
+ }
+ sleep(2);
+ pswitch(1);
+ secndflag++;
+ if (command("%s %s", cmd2, local) != PRELIM)
+ goto abort;
+ ptflag++;
+ (void) getreply(0);
+ pswitch(0);
+ (void) getreply(0);
+ (void) signal(SIGINT, oldintr);
+ pswitch(1);
+ ptflag = 0;
+ printf("local: %s remote: %s\n", local, remote);
+ return;
+abort:
+ (void) signal(SIGINT, SIG_IGN);
+ ptflag = 0;
+ if (strcmp(cmd, "RETR") && !proxy)
+ pswitch(1);
+ else if (!strcmp(cmd, "RETR") && proxy)
+ pswitch(0);
+ if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */
+ if (command("%s %s", cmd2, local) != PRELIM) {
+ pswitch(0);
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ }
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ pswitch(!proxy);
+ if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */
+ if (command("%s %s", cmd2, local) != PRELIM) {
+ pswitch(0);
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ }
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ pswitch(!proxy);
+ if (cpend) {
+ FD_ZERO(&mask);
+ FD_SET(fileno(cin), &mask);
+ if ((nfnd = empty(&mask, 10)) <= 0) {
+ if (nfnd < 0) {
+ warn("abort");
+ }
+ if (ptabflg)
+ code = -1;
+ lostpeer();
+ }
+ (void) getreply(0);
+ (void) getreply(0);
+ }
+ if (proxy)
+ pswitch(0);
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+}
+
+void
+reset(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct fd_set mask;
+ int nfnd = 1;
+
+ FD_ZERO(&mask);
+ while (nfnd > 0) {
+ FD_SET(fileno(cin), &mask);
+ if ((nfnd = empty(&mask,0)) < 0) {
+ warn("reset");
+ code = -1;
+ lostpeer();
+ }
+ else if (nfnd) {
+ (void) getreply(0);
+ }
+ }
+}
+
+char *
+gunique(local)
+ char *local;
+{
+ static char new[MAXPATHLEN];
+ char *cp = strrchr(local, '/');
+ int d, count=0;
+ char ext = '1';
+
+ if (cp)
+ *cp = '\0';
+ d = access(cp ? local : ".", 2);
+ if (cp)
+ *cp = '/';
+ if (d < 0) {
+ warn("local: %s", local);
+ return ((char *) 0);
+ }
+ (void) strcpy(new, local);
+ cp = new + strlen(new);
+ *cp++ = '.';
+ while (!d) {
+ if (++count == 100) {
+ printf("runique: can't find unique file name.\n");
+ return ((char *) 0);
+ }
+ *cp++ = ext;
+ *cp = '\0';
+ if (ext == '9')
+ ext = '0';
+ else
+ ext++;
+ if ((d = access(new, 0)) < 0)
+ break;
+ if (ext != '0')
+ cp--;
+ else if (*(cp - 2) == '.')
+ *(cp - 1) = '1';
+ else {
+ *(cp - 2) = *(cp - 2) + 1;
+ cp--;
+ }
+ }
+ return (new);
+}
+
+void
+abort_remote(din)
+ FILE *din;
+{
+ char buf[BUFSIZ];
+ int nfnd;
+ struct fd_set mask;
+
+ /*
+ * send IAC in urgent mode instead of DM because 4.3BSD places oob mark
+ * after urgent byte rather than before as is protocol now
+ */
+ sprintf(buf, "%c%c%c", IAC, IP, IAC);
+ if (send(fileno(cout), buf, 3, MSG_OOB) != 3)
+ warn("abort");
+ fprintf(cout,"%cABOR\r\n", DM);
+ (void) fflush(cout);
+ FD_ZERO(&mask);
+ FD_SET(fileno(cin), &mask);
+ if (din) {
+ FD_SET(fileno(din), &mask);
+ }
+ if ((nfnd = empty(&mask, 10)) <= 0) {
+ if (nfnd < 0) {
+ warn("abort");
+ }
+ if (ptabflg)
+ code = -1;
+ lostpeer();
+ }
+ if (din && FD_ISSET(fileno(din), &mask)) {
+ while (read(fileno(din), buf, BUFSIZ) > 0)
+ /* LOOP */;
+ }
+ if (getreply(0) == ERROR && code == 552) {
+ /* 552 needed for nic style abort */
+ (void) getreply(0);
+ }
+ (void) getreply(0);
+}
diff --git a/usr.bin/ftp/ftp_var.h b/usr.bin/ftp/ftp_var.h
new file mode 100644
index 0000000..3a0d1bc
--- /dev/null
+++ b/usr.bin/ftp/ftp_var.h
@@ -0,0 +1,125 @@
+/*
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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_var.h 8.3 (Berkeley) 4/2/94
+ */
+
+/*
+ * FTP global variables.
+ */
+
+#include <sys/param.h>
+#include <setjmp.h>
+
+#include "extern.h"
+
+/*
+ * Options and other state info.
+ */
+int trace; /* trace packets exchanged */
+int hash; /* print # for each buffer transferred */
+int sendport; /* use PORT cmd for each data connection */
+int verbose; /* print messages coming back from server */
+int connected; /* connected to server */
+int fromatty; /* input is from a terminal */
+int interactive; /* interactively prompt on m* cmds */
+int debug; /* debugging level */
+int bell; /* ring bell on cmd completion */
+int doglob; /* glob local file names */
+int autologin; /* establish user account on connection */
+int proxy; /* proxy server connection active */
+int proxflag; /* proxy connection exists */
+int sunique; /* store files on server with unique name */
+int runique; /* store local files with unique name */
+int mcase; /* map upper to lower case for mget names */
+int ntflag; /* use ntin ntout tables for name translation */
+int mapflag; /* use mapin mapout templates on file names */
+int code; /* return/reply code for ftp command */
+int crflag; /* if 1, strip car. rets. on ascii gets */
+char pasv[64]; /* passive port for proxy data connection */
+char *altarg; /* argv[1] with no shell-like preprocessing */
+char ntin[17]; /* input translation table */
+char ntout[17]; /* output translation table */
+char mapin[MAXPATHLEN]; /* input map template */
+char mapout[MAXPATHLEN]; /* output map template */
+char typename[32]; /* name of file transfer type */
+int type; /* requested file transfer type */
+int curtype; /* current file transfer type */
+char structname[32]; /* name of file transfer structure */
+int stru; /* file transfer structure */
+char formname[32]; /* name of file transfer format */
+int form; /* file transfer format */
+char modename[32]; /* name of file transfer mode */
+int mode; /* file transfer mode */
+char bytename[32]; /* local byte size in ascii */
+int bytesize; /* local byte size in binary */
+
+char *hostname; /* name of host connected to */
+int unix_server; /* server is unix, can use binary for ascii */
+int unix_proxy; /* proxy is unix, can use binary for ascii */
+
+struct servent *sp; /* service spec for tcp/ftp */
+
+jmp_buf toplevel; /* non-local goto stuff for cmd scanner */
+
+char line[200]; /* input line buffer */
+char *stringbase; /* current scan point in line buffer */
+char argbuf[200]; /* argument storage buffer */
+char *argbase; /* current storage point in arg buffer */
+int margc; /* count of arguments on input line */
+char *margv[20]; /* args parsed from input line */
+int cpend; /* flag: if != 0, then pending server reply */
+int mflag; /* flag: if != 0, then active multi command */
+
+int options; /* used during socket creation */
+
+/*
+ * Format of command table.
+ */
+struct cmd {
+ char *c_name; /* name of command */
+ char *c_help; /* help string */
+ char c_bell; /* give bell when command completes */
+ char c_conn; /* must be connected to use command */
+ char c_proxy; /* proxy server may execute */
+ void (*c_handler) __P((int, char **)); /* function to call */
+};
+
+struct macel {
+ char mac_name[9]; /* macro name */
+ char *mac_start; /* start of macro in macbuf */
+ char *mac_end; /* end of macro in macbuf */
+};
+
+int macnum; /* number of defined macros */
+struct macel macros[16];
+char macbuf[4096];
diff --git a/usr.bin/ftp/main.c b/usr.bin/ftp/main.c
new file mode 100644
index 0000000..37fa85b
--- /dev/null
+++ b/usr.bin/ftp/main.c
@@ -0,0 +1,514 @@
+/*
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 4/3/94";
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Command Interface.
+ */
+/*#include <sys/ioctl.h>*/
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <arpa/ftp.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ftp_var.h"
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, top;
+ struct passwd *pw = NULL;
+ char *cp, homedir[MAXPATHLEN];
+
+ sp = getservbyname("ftp", "tcp");
+ if (sp == 0)
+ errx(1, "ftp/tcp: unknown service");
+ doglob = 1;
+ interactive = 1;
+ autologin = 1;
+
+ while ((ch = getopt(argc, argv, "dgintv")) != EOF) {
+ switch (*cp) {
+ case 'd':
+ options |= SO_DEBUG;
+ debug++;
+ break;
+
+ case 'g':
+ doglob = 0;
+ break;
+
+ case 'i':
+ interactive = 0;
+ break;
+
+ case 'n':
+ autologin = 0;
+ break;
+
+ case 't':
+ trace++;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ default:
+ (void)fprintf(stderr,
+ "usage: ftp [-dgintv] [host [port]]\n");
+ exit(1);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ fromatty = isatty(fileno(stdin));
+ if (fromatty)
+ verbose++;
+ 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) {
+ home = homedir;
+ (void) strcpy(home, pw->pw_dir);
+ }
+ if (argc > 0) {
+ char *xargv[5];
+ extern char *__progname;
+
+ if (setjmp(toplevel))
+ exit(0);
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+ xargv[0] = __progname;
+ xargv[1] = argv[0];
+ xargv[2] = argv[1];
+ xargv[3] = argv[2];
+ xargv[4] = NULL;
+ setpeer(argc+1, xargv);
+ }
+ top = setjmp(toplevel) == 0;
+ if (top) {
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+ }
+ for (;;) {
+ cmdscanner(top);
+ top = 1;
+ }
+}
+
+void
+intr()
+{
+
+ longjmp(toplevel, 1);
+}
+
+void
+lostpeer()
+{
+
+ if (connected) {
+ if (cout != NULL) {
+ (void) shutdown(fileno(cout), 1+1);
+ (void) fclose(cout);
+ cout = NULL;
+ }
+ if (data >= 0) {
+ (void) shutdown(data, 1+1);
+ (void) close(data);
+ data = -1;
+ }
+ connected = 0;
+ }
+ pswitch(1);
+ if (connected) {
+ if (cout != NULL) {
+ (void) shutdown(fileno(cout), 1+1);
+ (void) fclose(cout);
+ cout = NULL;
+ }
+ connected = 0;
+ }
+ proxflag = 0;
+ pswitch(0);
+}
+
+/*
+char *
+tail(filename)
+ char *filename;
+{
+ char *s;
+
+ while (*filename) {
+ s = strrchr(filename, '/');
+ if (s == NULL)
+ break;
+ if (s[1])
+ return (s + 1);
+ *s = '\0';
+ }
+ return (filename);
+}
+*/
+
+/*
+ * Command parser.
+ */
+void
+cmdscanner(top)
+ int top;
+{
+ struct cmd *c;
+ int l;
+
+ if (!top)
+ (void) putchar('\n');
+ for (;;) {
+ if (fromatty) {
+ printf("ftp> ");
+ (void) fflush(stdout);
+ }
+ if (fgets(line, sizeof line, stdin) == NULL)
+ quit(0, 0);
+ l = strlen(line);
+ if (l == 0)
+ break;
+ if (line[--l] == '\n') {
+ if (l == 0)
+ break;
+ line[l] = '\0';
+ } else if (l == sizeof(line) - 2) {
+ printf("sorry, input line too long\n");
+ while ((l = getchar()) != '\n' && l != EOF)
+ /* void */;
+ break;
+ } /* else it was a line without a newline */
+ makeargv();
+ if (margc == 0) {
+ continue;
+ }
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ if (c->c_conn && !connected) {
+ printf("Not connected.\n");
+ continue;
+ }
+ (*c->c_handler)(margc, margv);
+ if (bell && c->c_bell)
+ (void) putchar('\007');
+ if (c->c_handler != help)
+ break;
+ }
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+}
+
+struct cmd *
+getcmd(name)
+ char *name;
+{
+ char *p, *q;
+ struct cmd *c, *found;
+ int nmatches, longest;
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; p = c->c_name; 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()
+{
+ char **argp;
+
+ margc = 0;
+ argp = margv;
+ stringbase = line; /* scan from first of buffer */
+ argbase = argbuf; /* store from first of buffer */
+ slrflag = 0;
+ while (*argp++ = slurpstring())
+ margc++;
+}
+
+/*
+ * Parse string into argbuf;
+ * implemented with FSM to
+ * handle quoting and strings
+ */
+char *
+slurpstring()
+{
+ 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++;
+ stringbase++;
+ return ((*sb == '!') ? "!" : "$");
+ /* NOTREACHED */
+ case 1:
+ slrflag++;
+ altarg = stringbase;
+ break;
+ default:
+ break;
+ }
+ }
+
+S0:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case ' ':
+ case '\t':
+ 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 '\\':
+ sb++; goto S2; /* slurp next character */
+
+ case '"':
+ sb++; goto S3; /* slurp quoted string */
+
+ default:
+ *ap++ = *sb++; /* add character to token */
+ got_one = 1;
+ goto S1;
+ }
+
+S2:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ default:
+ *ap++ = *sb++;
+ got_one = 1;
+ goto S1;
+ }
+
+S3:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case '"':
+ sb++; goto S1;
+
+ default:
+ *ap++ = *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 ((char *)0);
+}
+
+#define HELPINDENT ((int) sizeof ("directory"))
+
+/*
+ * Help command.
+ * Call each command handler with argc == 0 and argv[0] == name.
+ */
+void
+help(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct cmd *c;
+
+ if (argc == 1) {
+ int i, j, w, k;
+ int columns, width = 0, lines;
+
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
+ int len = strlen(c->c_name);
+
+ if (len > width)
+ width = len;
+ }
+ width = (width + 8) &~ 7;
+ columns = 80 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ c = cmdtab + j * lines + i;
+ if (c->c_name && (!proxy || c->c_proxy)) {
+ printf("%s", c->c_name);
+ }
+ else if (c->c_name) {
+ for (k=0; k < strlen(c->c_name); k++) {
+ (void) putchar(' ');
+ }
+ }
+ if (c + lines >= &cmdtab[NCMDS]) {
+ printf("\n");
+ break;
+ }
+ w = strlen(c->c_name);
+ while (w < width) {
+ w = (w + 8) &~ 7;
+ (void) putchar('\t');
+ }
+ }
+ }
+ return;
+ }
+ while (--argc > 0) {
+ char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%-*s\t%s\n", HELPINDENT,
+ c->c_name, c->c_help);
+ }
+}
diff --git a/usr.bin/ftp/pathnames.h b/usr.bin/ftp/pathnames.h
new file mode 100644
index 0000000..be72b7e
--- /dev/null
+++ b/usr.bin/ftp/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1989, 1993
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/ftpXXXXXX"
diff --git a/usr.bin/ftp/ruserpass.c b/usr.bin/ftp/ruserpass.c
new file mode 100644
index 0000000..07bb1f4
--- /dev/null
+++ b/usr.bin/ftp/ruserpass.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 1985, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ruserpass.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ftp_var.h"
+
+static int token __P((void));
+static FILE *cfile;
+
+#define DEFAULT 1
+#define LOGIN 2
+#define PASSWD 3
+#define ACCOUNT 4
+#define MACDEF 5
+#define ID 10
+#define MACH 11
+
+static char tokval[100];
+
+static struct toktab {
+ char *tokstr;
+ int tval;
+} toktab[]= {
+ { "default", DEFAULT },
+ { "login", LOGIN },
+ { "password", PASSWD },
+ { "passwd", PASSWD },
+ { "account", ACCOUNT },
+ { "machine", MACH },
+ { "macdef", MACDEF },
+ { NULL, 0 }
+};
+
+int
+ruserpass(host, aname, apass, aacct)
+ char *host, **aname, **apass, **aacct;
+{
+ char *hdir, buf[BUFSIZ], *tmp;
+ char myname[MAXHOSTNAMELEN], *mydomain;
+ int t, i, c, usedefault = 0;
+ struct stat stb;
+
+ hdir = getenv("HOME");
+ if (hdir == NULL)
+ hdir = ".";
+ (void) sprintf(buf, "%s/.netrc", hdir);
+ cfile = fopen(buf, "r");
+ if (cfile == NULL) {
+ if (errno != ENOENT)
+ warn("%s", buf);
+ return (0);
+ }
+ if (gethostname(myname, sizeof(myname)) < 0)
+ myname[0] = '\0';
+ if ((mydomain = strchr(myname, '.')) == NULL)
+ mydomain = "";
+next:
+ while ((t = token())) switch(t) {
+
+ case DEFAULT:
+ usedefault = 1;
+ /* FALL THROUGH */
+
+ case MACH:
+ if (!usedefault) {
+ if (token() != ID)
+ continue;
+ /*
+ * Allow match either for user's input host name
+ * or official hostname. Also allow match of
+ * incompletely-specified host in local domain.
+ */
+ if (strcasecmp(host, tokval) == 0)
+ goto match;
+ if (strcasecmp(hostname, tokval) == 0)
+ goto match;
+ if ((tmp = strchr(hostname, '.')) != NULL &&
+ strcasecmp(tmp, mydomain) == 0 &&
+ strncasecmp(hostname, tokval, tmp-hostname) == 0 &&
+ tokval[tmp - hostname] == '\0')
+ goto match;
+ if ((tmp = strchr(host, '.')) != NULL &&
+ strcasecmp(tmp, mydomain) == 0 &&
+ strncasecmp(host, tokval, tmp - host) == 0 &&
+ tokval[tmp - host] == '\0')
+ goto match;
+ continue;
+ }
+ match:
+ while ((t = token()) && t != MACH && t != DEFAULT) switch(t) {
+
+ case LOGIN:
+ if (token())
+ if (*aname == 0) {
+ *aname = malloc((unsigned) strlen(tokval) + 1);
+ (void) strcpy(*aname, tokval);
+ } else {
+ if (strcmp(*aname, tokval))
+ goto next;
+ }
+ break;
+ case PASSWD:
+ if (strcmp(*aname, "anonymous") &&
+ fstat(fileno(cfile), &stb) >= 0 &&
+ (stb.st_mode & 077) != 0) {
+ warnx("Error: .netrc file is readable by others.");
+ warnx("Remove password or make file unreadable by others.");
+ goto bad;
+ }
+ if (token() && *apass == 0) {
+ *apass = malloc((unsigned) strlen(tokval) + 1);
+ (void) strcpy(*apass, tokval);
+ }
+ break;
+ case ACCOUNT:
+ if (fstat(fileno(cfile), &stb) >= 0
+ && (stb.st_mode & 077) != 0) {
+ warnx("Error: .netrc file is readable by others.");
+ warnx("Remove account or make file unreadable by others.");
+ goto bad;
+ }
+ if (token() && *aacct == 0) {
+ *aacct = malloc((unsigned) strlen(tokval) + 1);
+ (void) strcpy(*aacct, tokval);
+ }
+ break;
+ case MACDEF:
+ if (proxy) {
+ (void) fclose(cfile);
+ return (0);
+ }
+ while ((c=getc(cfile)) != EOF && c == ' ' || c == '\t');
+ if (c == EOF || c == '\n') {
+ printf("Missing macdef name argument.\n");
+ goto bad;
+ }
+ if (macnum == 16) {
+ printf("Limit of 16 macros have already been defined\n");
+ goto bad;
+ }
+ tmp = macros[macnum].mac_name;
+ *tmp++ = c;
+ for (i=0; i < 8 && (c=getc(cfile)) != EOF &&
+ !isspace(c); ++i) {
+ *tmp++ = c;
+ }
+ if (c == EOF) {
+ printf("Macro definition missing null line terminator.\n");
+ goto bad;
+ }
+ *tmp = '\0';
+ if (c != '\n') {
+ while ((c=getc(cfile)) != EOF && c != '\n');
+ }
+ if (c == EOF) {
+ printf("Macro definition missing null line terminator.\n");
+ goto bad;
+ }
+ if (macnum == 0) {
+ macros[macnum].mac_start = macbuf;
+ }
+ else {
+ macros[macnum].mac_start = macros[macnum-1].mac_end + 1;
+ }
+ tmp = macros[macnum].mac_start;
+ while (tmp != macbuf + 4096) {
+ if ((c=getc(cfile)) == EOF) {
+ printf("Macro definition missing null line terminator.\n");
+ goto bad;
+ }
+ *tmp = c;
+ if (*tmp == '\n') {
+ if (*(tmp-1) == '\0') {
+ macros[macnum++].mac_end = tmp - 1;
+ break;
+ }
+ *tmp = '\0';
+ }
+ tmp++;
+ }
+ if (tmp == macbuf + 4096) {
+ printf("4K macro buffer exceeded\n");
+ goto bad;
+ }
+ break;
+ default:
+ warnx("Unknown .netrc keyword %s", tokval);
+ break;
+ }
+ goto done;
+ }
+done:
+ (void) fclose(cfile);
+ return (0);
+bad:
+ (void) fclose(cfile);
+ return (-1);
+}
+
+static int
+token()
+{
+ char *cp;
+ int c;
+ struct toktab *t;
+
+ if (feof(cfile) || ferror(cfile))
+ return (0);
+ while ((c = getc(cfile)) != EOF &&
+ (c == '\n' || c == '\t' || c == ' ' || c == ','))
+ continue;
+ if (c == EOF)
+ return (0);
+ cp = tokval;
+ if (c == '"') {
+ while ((c = getc(cfile)) != EOF && c != '"') {
+ if (c == '\\')
+ c = getc(cfile);
+ *cp++ = c;
+ }
+ } else {
+ *cp++ = c;
+ while ((c = getc(cfile)) != EOF
+ && c != '\n' && c != '\t' && c != ' ' && c != ',') {
+ if (c == '\\')
+ c = getc(cfile);
+ *cp++ = c;
+ }
+ }
+ *cp = 0;
+ if (tokval[0] == 0)
+ return (0);
+ for (t = toktab; t->tokstr; t++)
+ if (!strcmp(t->tokstr, tokval))
+ return (t->tval);
+ return (ID);
+}
OpenPOWER on IntegriCloud