1 /* $NetBSD: cmds.c,v 1.125 2008/05/10 00:05:31 skd Exp $ */
4 * Copyright (c) 1996-2007 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
12 * NASA Ames Research Center.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
37 * Copyright (c) 1985, 1989, 1993, 1994
38 * The Regents of the University of California. All rights reserved.
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 3. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
66 * Copyright (C) 1997 and 1998 WIDE Project.
67 * All rights reserved.
69 * Redistribution and use in source and binary forms, with or without
70 * modification, are permitted provided that the following conditions
72 * 1. Redistributions of source code must retain the above copyright
73 * notice, this list of conditions and the following disclaimer.
74 * 2. Redistributions in binary form must reproduce the above copyright
75 * notice, this list of conditions and the following disclaimer in the
76 * documentation and/or other materials provided with the distribution.
77 * 3. Neither the name of the project nor the names of its contributors
78 * may be used to endorse or promote products derived from this software
79 * without specific prior written permission.
81 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
82 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
83 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
84 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
85 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
86 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
87 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
88 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
89 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
90 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
94 #include <sys/cdefs.h>
97 static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94";
99 __RCSID("$NetBSD: cmds.c,v 1.125 2008/05/10 00:05:31 skd Exp $");
101 #endif /* not lint */
104 * FTP User Program -- Command Routines.
106 #include <sys/types.h>
107 #include <sys/socket.h>
108 #include <sys/stat.h>
109 #include <sys/wait.h>
110 #include <arpa/ftp.h>
127 static struct types {
133 { "ascii", "A", TYPE_A, 0 },
134 { "binary", "I", TYPE_I, 0 },
135 { "image", "I", TYPE_I, 0 },
136 { "ebcdic", "E", TYPE_E, 0 },
137 { "tenex", "L", TYPE_L, bytename },
141 static sigjmp_buf jabort;
143 static int confirm(const char *, const char *);
144 static void mintr(int);
145 static void mabort(const char *);
147 static const char *doprocess(char *, size_t, const char *, int, int, int);
148 static const char *domap(char *, size_t, const char *);
149 static const char *docase(char *, size_t, const char *);
150 static const char *dotrans(char *, size_t, const char *);
153 * Confirm if "cmd" is to be performed upon "file".
154 * If "file" is NULL, generate a "Continue with" prompt instead.
157 confirm(const char *cmd, const char *file)
159 const char *errormsg;
161 const char *promptleft, *promptright;
163 if (!interactive || confirmrest)
166 promptleft = "Continue with";
173 fprintf(ttyout, "%s %s [anpqy?]? ", promptleft, promptright);
174 (void)fflush(ttyout);
175 if (getline(stdin, line, sizeof(line), &errormsg) < 0) {
177 fprintf(ttyout, "%s; %s aborted\n", errormsg, cmd);
180 switch (tolower((unsigned char)*line)) {
184 "Prompting off for duration of %s.\n", cmd);
188 fputs("Interactive mode: off.\n", ttyout);
192 fprintf(ttyout, "%s aborted.\n", cmd);
198 " confirmation options:\n"
199 "\ta answer `yes' for the duration of %s\n"
200 "\tn answer `no' for this file\n"
201 "\tp turn off `prompt' mode\n"
202 "\tq stop the current %s\n"
203 "\ty answer `yes' for this file\n"
204 "\t? this help list\n",
206 continue; /* back to while(1) */
217 settype(int argc, char *argv[])
222 if (argc == 0 || argc > 2) {
225 UPRINTF("usage: %s [", argv[0]);
227 for (p = types; p->t_name; p++) {
228 fprintf(ttyout, "%s%s", sep, p->t_name);
231 fputs(" ]\n", ttyout);
236 fprintf(ttyout, "Using %s mode to transfer files.\n", typename);
240 for (p = types; p->t_name; p++)
241 if (strcmp(argv[1], p->t_name) == 0)
243 if (p->t_name == 0) {
244 fprintf(ttyout, "%s: unknown mode.\n", argv[1]);
248 if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
249 comret = command("TYPE %s %s", p->t_mode, p->t_arg);
251 comret = command("TYPE %s", p->t_mode);
252 if (comret == COMPLETE) {
253 (void)strlcpy(typename, p->t_name, sizeof(typename));
254 curtype = type = p->t_type;
259 * Internal form of settype; changes current type in use with server
260 * without changing our notion of the type for data transfers.
261 * Used to change to and from ascii for listings.
264 changetype(int newtype, int show)
267 int comret, oldverbose = verbose;
271 if (newtype == curtype)
273 if (ftp_debug == 0 && show == 0)
275 for (p = types; p->t_name; p++)
276 if (newtype == p->t_type)
278 if (p->t_name == 0) {
279 errx(1, "changetype: unknown type %d", newtype);
281 if (newtype == TYPE_L && bytename[0] != '\0')
282 comret = command("TYPE %s %s", p->t_mode, bytename);
284 comret = command("TYPE %s", p->t_mode);
285 if (comret == COMPLETE)
287 verbose = oldverbose;
297 * Set binary transfer type.
301 setbinary(int argc, char *argv[])
305 UPRINTF("usage: %s\n", argv[0]);
314 * Set ascii transfer type.
318 setascii(int argc, char *argv[])
322 UPRINTF("usage: %s\n", argv[0]);
331 * Set tenex transfer type.
335 settenex(int argc, char *argv[])
339 UPRINTF("usage: %s\n", argv[0]);
348 * Set file transfer mode.
352 setftmode(int argc, char *argv[])
356 UPRINTF("usage: %s mode-name\n", argv[0]);
360 fprintf(ttyout, "We only support %s mode, sorry.\n", modename);
365 * Set file transfer format.
369 setform(int argc, char *argv[])
373 UPRINTF("usage: %s format\n", argv[0]);
377 fprintf(ttyout, "We only support %s format, sorry.\n", formname);
382 * Set file transfer structure.
386 setstruct(int argc, char *argv[])
390 UPRINTF("usage: %s struct-mode\n", argv[0]);
394 fprintf(ttyout, "We only support %s structure, sorry.\n", structname);
399 * Send a single file.
402 put(int argc, char *argv[])
404 char buf[MAXPATHLEN];
415 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-file")))
417 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
419 UPRINTF("usage: %s local-file [remote-file]\n", argv[0]);
423 if ((locfile = globulize(argv[1])) == NULL) {
428 if (loc) /* If argv[2] is a copy of the old argv[1], update it */
430 cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
431 remfile = doprocess(buf, sizeof(buf), remfile,
432 0, loc && ntflag, loc && mapflag);
433 sendrequest(cmd, locfile, remfile,
434 locfile != argv[1] || remfile != argv[2]);
439 doprocess(char *dst, size_t dlen, const char *src,
440 int casef, int transf, int mapf)
443 src = docase(dst, dlen, src);
445 src = dotrans(dst, dlen, src);
447 src = domap(dst, dlen, src);
452 * Send multiple files.
455 mput(int argc, char *argv[])
462 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-files"))) {
463 UPRINTF("usage: %s local-files\n", argv[0]);
468 oldintr = xsignal(SIGINT, mintr);
469 if (sigsetjmp(jabort, 1))
474 while ((cp = remglob(argv, 0, NULL)) != NULL) {
475 if (*cp == '\0' || !connected) {
479 if (mflag && confirm(argv[0], cp)) {
480 char buf[MAXPATHLEN];
481 tp = doprocess(buf, sizeof(buf), cp,
482 mcase, ntflag, mapflag);
483 sendrequest((sunique) ? "STOU" : "STOR",
484 cp, tp, cp != tp || !interactive);
485 if (!mflag && fromatty) {
486 ointer = interactive;
488 if (confirm(argv[0], NULL)) {
491 interactive = ointer;
497 for (i = 1; i < argc && connected; i++) {
503 if (mflag && confirm(argv[0], argv[i])) {
504 char buf[MAXPATHLEN];
505 tp = doprocess(buf, sizeof(buf), argv[i],
507 sendrequest((sunique) ? "STOU" : "STOR",
508 argv[i], tp, tp != argv[i] || !interactive);
509 if (!mflag && fromatty) {
510 ointer = interactive;
512 if (confirm(argv[0], NULL)) {
515 interactive = ointer;
521 memset(&gl, 0, sizeof(gl));
522 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
523 if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
524 warnx("Glob pattern `%s' not found", argv[i]);
528 for (cpp = gl.gl_pathv; cpp && *cpp != NULL && connected;
530 if (mflag && confirm(argv[0], *cpp)) {
531 char buf[MAXPATHLEN];
533 tp = doprocess(buf, sizeof(buf), *cpp,
535 sendrequest((sunique) ? "STOU" : "STOR",
536 *cpp, tp, *cpp != tp || !interactive);
537 if (!mflag && fromatty) {
538 ointer = interactive;
540 if (confirm(argv[0], NULL)) {
543 interactive = ointer;
550 (void)xsignal(SIGINT, oldintr);
555 reget(int argc, char *argv[])
558 (void)getit(argc, argv, 1, "r+");
562 get(int argc, char *argv[])
565 (void)getit(argc, argv, 0, restart_point ? "r+" : "w" );
570 * If restartit is 1, restart the xfer always.
571 * If restartit is -1, restart the xfer only if the remote file is newer.
574 getit(int argc, char *argv[], int restartit, const char *mode)
577 char *remfile, *olocfile;
579 char buf[MAXPATHLEN];
587 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "remote-file")))
589 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
591 UPRINTF("usage: %s remote-file [local-file]\n", argv[0]);
596 if ((olocfile = globulize(argv[2])) == NULL) {
600 locfile = doprocess(buf, sizeof(buf), olocfile,
601 loc && mcase, loc && ntflag, loc && mapflag);
606 if (! features[FEAT_REST_STREAM]) {
608 "Restart is not supported by the remote server.\n");
611 ret = stat(locfile, &stbuf);
612 if (restartit == 1) {
614 warn("Can't stat `%s'", locfile);
617 restart_point = stbuf.st_size;
622 mtime = remotemodtime(argv[1], 0);
625 if (stbuf.st_mtime >= mtime) {
633 recvrequest("RETR", locfile, remfile, mode,
634 remfile != argv[1] || locfile != argv[2], loc);
637 (void)free(olocfile);
648 write(fileno(ttyout), "\n", 1);
649 siglongjmp(jabort, 1);
653 mabort(const char *cmd)
657 if (mflag && fromatty) {
658 ointer = interactive;
662 if (confirm(cmd, NULL)) {
663 interactive = ointer;
667 interactive = ointer;
674 * Get multiple files.
677 mget(int argc, char *argv[])
686 (argc == 1 && !another(&argc, &argv, "remote-files"))) {
687 UPRINTF("usage: %s remote-files\n", argv[0]);
694 if (strcmp(argv[0], "mreget") == 0) {
695 if (! features[FEAT_REST_STREAM]) {
697 "Restart is not supported by the remote server.\n");
702 oldintr = xsignal(SIGINT, mintr);
703 if (sigsetjmp(jabort, 1))
705 while ((cp = remglob(argv, proxy, NULL)) != NULL) {
706 char buf[MAXPATHLEN];
707 if (*cp == '\0' || !connected) {
713 if (! fileindir(cp, localcwd)) {
714 fprintf(ttyout, "Skipping non-relative filename `%s'\n",
718 if (!confirm(argv[0], cp))
720 tp = doprocess(buf, sizeof(buf), cp, mcase, ntflag, mapflag);
724 if (stat(tp, &stbuf) == 0)
725 restart_point = stbuf.st_size;
727 warn("Can't stat `%s'", tp);
729 recvrequest("RETR", tp, cp, restart_point ? "r+" : "w",
730 tp != cp || !interactive, 1);
732 if (!mflag && fromatty) {
733 ointer = interactive;
735 if (confirm(argv[0], NULL))
737 interactive = ointer;
740 (void)xsignal(SIGINT, oldintr);
745 * Read list of filenames from a local file and get those
748 fget(int argc, char *argv[])
752 char buf[MAXPATHLEN];
755 UPRINTF("usage: %s localfile\n", argv[0]);
760 fp = fopen(argv[1], "r");
762 fprintf(ttyout, "Can't open source file %s\n", argv[1]);
768 mode = restart_point ? "r+" : "w";
770 while (getline(fp, buf, sizeof(buf), NULL) >= 0) {
774 (void)getit(argc, argv, 0, mode);
783 return (bool ? "on" : "off");
791 status(int argc, char *argv[])
795 UPRINTF("usage: %s\n", argv[0]);
801 fprintf(ttyout, "Connected %sto %s.\n",
802 connected == -1 ? "and logged in" : "", hostname);
804 fputs("Not connected.\n", ttyout);
808 fprintf(ttyout, "Connected for proxy commands to %s.\n",
812 fputs("No proxy connection.\n", ttyout);
816 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
817 *gateserver ? gateserver : "(none)", gateport);
818 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
819 onoff(passivemode), onoff(activefallback));
820 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
821 modename, typename, formname, structname);
822 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
823 onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob));
824 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n",
825 onoff(sunique), onoff(runique));
826 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve));
827 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase),
830 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout);
833 fputs("Ntrans: off.\n", ttyout);
836 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout);
839 fputs("Nmap: off.\n", ttyout);
842 "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
843 onoff(hash), mark, onoff(progress));
845 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
846 onoff(rate_get), rate_get, rate_get_incr);
848 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
849 onoff(rate_put), rate_put, rate_put_incr);
851 "Socket buffer sizes: send %d, receive %d.\n",
852 sndbuf_size, rcvbuf_size);
853 fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport));
854 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4),
855 epsv4bad ? " (disabled for this connection)" : "");
856 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv6: %s%s.\n", onoff(epsv6),
857 epsv6bad ? " (disabled for this connection)" : "");
858 fprintf(ttyout, "Command line editing: %s.\n",
859 #ifdef NO_EDITCOMPLETE
860 "support not compiled in"
861 #else /* !def NO_EDITCOMPLETE */
863 #endif /* !def NO_EDITCOMPLETE */
868 fputs("Macros:\n", ttyout);
869 for (i=0; i<macnum; i++) {
870 fprintf(ttyout, "\t%s\n", macros[i].mac_name);
873 #endif /* !def NO_STATUS */
874 fprintf(ttyout, "Version: %s %s\n", FTP_PRODUCT, FTP_VERSION);
882 togglevar(int argc, char *argv[], int *var, const char *mesg)
886 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
888 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
891 UPRINTF("usage: %s [ on | off ]\n", argv[0]);
895 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var));
900 * Set beep on cmd completed mode.
904 setbell(int argc, char *argv[])
907 code = togglevar(argc, argv, &bell, "Bell mode");
911 * Set command line editing
915 setedit(int argc, char *argv[])
918 #ifdef NO_EDITCOMPLETE
920 UPRINTF("usage: %s\n", argv[0]);
925 fputs("Editing support not compiled in; ignoring command.\n",
927 #else /* !def NO_EDITCOMPLETE */
928 code = togglevar(argc, argv, &editing, "Editing mode");
930 #endif /* !def NO_EDITCOMPLETE */
934 * Turn on packet tracing.
938 settrace(int argc, char *argv[])
941 code = togglevar(argc, argv, &trace, "Packet tracing");
945 * Toggle hash mark printing during transfers, or set hash mark bytecount.
949 sethash(int argc, char *argv[])
953 else if (argc != 2) {
954 UPRINTF("usage: %s [ on | off | bytecount ]\n",
958 } else if (strcasecmp(argv[1], "on") == 0)
960 else if (strcasecmp(argv[1], "off") == 0)
965 nmark = strsuftoi(argv[1]);
967 fprintf(ttyout, "mark: bad bytecount value `%s'.\n",
975 fprintf(ttyout, "Hash mark printing %s", onoff(hash));
977 fprintf(ttyout, " (%d bytes/hash mark)", mark);
978 fputs(".\n", ttyout);
985 * Turn on printing of server echo's.
989 setverbose(int argc, char *argv[])
992 code = togglevar(argc, argv, &verbose, "Verbose mode");
996 * Toggle PORT/LPRT cmd use before each data connection.
1000 setport(int argc, char *argv[])
1003 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds");
1007 * Toggle transfer progress bar.
1011 setprogress(int argc, char *argv[])
1014 code = togglevar(argc, argv, &progress, "Progress bar");
1020 * Turn on interactive prompting during mget, mput, and mdelete.
1024 setprompt(int argc, char *argv[])
1027 code = togglevar(argc, argv, &interactive, "Interactive mode");
1031 * Toggle gate-ftp mode, or set gate-ftp server
1035 setgate(int argc, char *argv[])
1037 static char gsbuf[MAXHOSTNAMELEN];
1039 if (argc == 0 || argc > 3) {
1041 "usage: %s [ on | off | gateserver [port] ]\n", argv[0]);
1044 } else if (argc < 2) {
1045 gatemode = !gatemode;
1047 if (argc == 2 && strcasecmp(argv[1], "on") == 0)
1049 else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
1053 gateport = ftp_strdup(argv[2]);
1054 (void)strlcpy(gsbuf, argv[1], sizeof(gsbuf));
1059 if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
1061 "Disabling gate-ftp mode - no gate-ftp server defined.\n");
1064 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n",
1065 onoff(gatemode), *gateserver ? gateserver : "(none)",
1072 * Toggle metacharacter interpretation on local file names.
1076 setglob(int argc, char *argv[])
1079 code = togglevar(argc, argv, &doglob, "Globbing");
1083 * Toggle preserving modification times on retrieved files.
1087 setpreserve(int argc, char *argv[])
1090 code = togglevar(argc, argv, &preserve, "Preserve modification times");
1094 * Set debugging mode on/off and/or set level of debugging.
1098 setdebug(int argc, char *argv[])
1100 if (argc == 0 || argc > 2) {
1101 UPRINTF("usage: %s [ on | off | debuglevel ]\n", argv[0]);
1104 } else if (argc == 2) {
1105 if (strcasecmp(argv[1], "on") == 0)
1107 else if (strcasecmp(argv[1], "off") == 0)
1112 val = strsuftoi(argv[1]);
1114 fprintf(ttyout, "%s: bad debugging value.\n",
1122 ftp_debug = !ftp_debug;
1124 options |= SO_DEBUG;
1126 options &= ~SO_DEBUG;
1127 fprintf(ttyout, "Debugging %s (ftp_debug=%d).\n", onoff(ftp_debug), ftp_debug);
1128 code = ftp_debug > 0;
1132 * Set current working directory on remote machine.
1135 cd(int argc, char *argv[])
1139 if (argc == 0 || argc > 2 ||
1140 (argc == 1 && !another(&argc, &argv, "remote-directory"))) {
1141 UPRINTF("usage: %s remote-directory\n", argv[0]);
1145 r = command("CWD %s", argv[1]);
1146 if (r == ERROR && code == 500) {
1148 fputs("CWD command not recognized, trying XCWD.\n",
1150 r = command("XCWD %s", argv[1]);
1152 if (r == COMPLETE) {
1159 * Set current working directory on local machine.
1162 lcd(int argc, char *argv[])
1169 argv[1] = localhome;
1172 UPRINTF("usage: %s [local-directory]\n", argv[0]);
1175 if ((locdir = globulize(argv[1])) == NULL)
1177 if (chdir(locdir) == -1)
1178 warn("Can't chdir `%s'", locdir);
1182 fprintf(ttyout, "Local directory now: %s\n", localcwd);
1185 fprintf(ttyout, "Unable to determine local directory\n");
1192 * Delete a single file.
1195 delete(int argc, char *argv[])
1198 if (argc == 0 || argc > 2 ||
1199 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
1200 UPRINTF("usage: %s remote-file\n", argv[0]);
1204 if (command("DELE %s", argv[1]) == COMPLETE)
1209 * Delete multiple files.
1212 mdelete(int argc, char *argv[])
1219 (argc == 1 && !another(&argc, &argv, "remote-files"))) {
1220 UPRINTF("usage: %s [remote-files]\n", argv[0]);
1225 oldintr = xsignal(SIGINT, mintr);
1226 if (sigsetjmp(jabort, 1))
1228 while ((cp = remglob(argv, 0, NULL)) != NULL) {
1233 if (mflag && confirm(argv[0], cp)) {
1234 if (command("DELE %s", cp) == COMPLETE)
1236 if (!mflag && fromatty) {
1237 ointer = interactive;
1239 if (confirm(argv[0], NULL)) {
1242 interactive = ointer;
1246 (void)xsignal(SIGINT, oldintr);
1251 * Rename a remote file.
1254 renamefile(int argc, char *argv[])
1257 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "from-name")))
1259 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1261 UPRINTF("usage: %s from-name to-name\n", argv[0]);
1265 if (command("RNFR %s", argv[1]) == CONTINUE &&
1266 command("RNTO %s", argv[2]) == COMPLETE)
1271 * Get a directory listing of remote files.
1272 * Supports being invoked as:
1278 * pdir, pls LIST |$PAGER
1279 * mmlsd MLSD |$PAGER
1282 ls(int argc, char *argv[])
1285 char *remdir, *locfile;
1286 int freelocfile, pagecmd, mlsdcmd;
1290 freelocfile = pagecmd = mlsdcmd = 0;
1292 * the only commands that start with `p' are
1293 * the `pager' versions.
1295 if (argv[0][0] == 'p')
1297 if (strcmp(argv[0] + pagecmd , "mlsd") == 0) {
1298 if (! features[FEAT_MLST]) {
1300 "MLSD is not supported by the remote server.\n");
1310 else if (strcmp(argv[0] + pagecmd, "nlist") == 0)
1319 if (argc > 3 || ((pagecmd | mlsdcmd) && argc > 2)) {
1321 if (pagecmd || mlsdcmd)
1322 UPRINTF("usage: %s [remote-path]\n", argv[0]);
1324 UPRINTF("usage: %s [remote-path [local-file]]\n",
1334 p = getoptionvalue("pager");
1337 len = strlen(p) + 2;
1338 locfile = ftp_malloc(len);
1340 (void)strlcpy(locfile + 1, p, len - 1);
1342 } else if ((strcmp(locfile, "-") != 0) && *locfile != '|') {
1343 if ((locfile = globulize(locfile)) == NULL ||
1344 !confirm("output to local-file:", locfile)) {
1350 recvrequest(cmd, locfile, remdir, "w", 0, 0);
1352 if (freelocfile && locfile)
1353 (void)free(locfile);
1357 * Get a directory listing of multiple remote files.
1360 mls(int argc, char *argv[])
1365 char *mode, *dest, *odest;
1369 if (argc < 2 && !another(&argc, &argv, "remote-files"))
1371 if (argc < 3 && !another(&argc, &argv, "local-file")) {
1373 UPRINTF("usage: %s remote-files local-file\n", argv[0]);
1377 odest = dest = argv[argc - 1];
1378 argv[argc - 1] = NULL;
1379 if (strcmp(dest, "-") && *dest != '|')
1380 if (((dest = globulize(dest)) == NULL) ||
1381 !confirm("output to local-file:", dest)) {
1385 dolist = strcmp(argv[0], "mls");
1387 oldintr = xsignal(SIGINT, mintr);
1388 if (sigsetjmp(jabort, 1))
1390 for (i = 1; mflag && i < argc-1 && connected; i++) {
1391 mode = (i == 1) ? "w" : "a";
1392 recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode,
1394 if (!mflag && fromatty) {
1395 ointer = interactive;
1397 if (confirm(argv[0], NULL)) {
1400 interactive = ointer;
1403 (void)xsignal(SIGINT, oldintr);
1405 if (dest != odest) /* free up after globulize() */
1414 shell(int argc, char *argv[])
1418 char shellnam[MAXPATHLEN], *shell, *namep;
1422 UPRINTF("usage: %s [command [args]]\n", argv[0]);
1426 oldintr = xsignal(SIGINT, SIG_IGN);
1427 if ((pid = fork()) == 0) {
1428 for (pid = 3; pid < 20; pid++)
1430 (void)xsignal(SIGINT, SIG_DFL);
1431 shell = getenv("SHELL");
1433 shell = _PATH_BSHELL;
1434 namep = strrchr(shell, '/');
1439 (void)strlcpy(shellnam, namep, sizeof(shellnam));
1441 fputs(shell, ttyout);
1445 execl(shell, shellnam, "-c", altarg, (char *)0);
1448 execl(shell, shellnam, (char *)0);
1450 warn("Can't execute `%s'", shell);
1455 while (wait(&wait_status) != pid)
1457 (void)xsignal(SIGINT, oldintr);
1459 warn("Can't fork a subshell; try again later");
1466 * Send new user information (re-login)
1469 user(int argc, char *argv[])
1472 char emptypass[] = "";
1478 (void)another(&argc, &argv, "username");
1479 if (argc < 2 || argc > 4) {
1481 UPRINTF("usage: %s username [password [account]]\n",
1486 n = command("USER %s", argv[1]);
1487 if (n == CONTINUE) {
1489 password = getpass("Password: ");
1490 if (password == NULL)
1491 password = emptypass;
1495 n = command("PASS %s", password);
1496 memset(password, 0, strlen(password));
1498 if (n == CONTINUE) {
1501 password = getpass("Account: ");
1502 if (password == NULL)
1503 password = emptypass;
1507 n = command("ACCT %s", password);
1508 memset(password, 0, strlen(password));
1510 if (n != COMPLETE) {
1511 fputs("Login failed.\n", ttyout);
1514 if (!aflag && argc == 4) {
1516 (void)command("ACCT %s", password);
1517 memset(password, 0, strlen(password));
1524 * Print working directory on remote machine.
1528 pwd(int argc, char *argv[])
1533 UPRINTF("usage: %s\n", argv[0]);
1539 fprintf(ttyout, "Unable to determine remote directory\n");
1541 fprintf(ttyout, "Remote directory: %s\n", remotecwd);
1547 * Print working directory on local machine.
1550 lpwd(int argc, char *argv[])
1555 UPRINTF("usage: %s\n", argv[0]);
1561 fprintf(ttyout, "Unable to determine local directory\n");
1563 fprintf(ttyout, "Local directory: %s\n", localcwd);
1572 makedir(int argc, char *argv[])
1576 if (argc == 0 || argc > 2 ||
1577 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1578 UPRINTF("usage: %s directory-name\n", argv[0]);
1582 r = command("MKD %s", argv[1]);
1583 if (r == ERROR && code == 500) {
1585 fputs("MKD command not recognized, trying XMKD.\n",
1587 r = command("XMKD %s", argv[1]);
1594 * Remove a directory.
1597 removedir(int argc, char *argv[])
1601 if (argc == 0 || argc > 2 ||
1602 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1603 UPRINTF("usage: %s directory-name\n", argv[0]);
1607 r = command("RMD %s", argv[1]);
1608 if (r == ERROR && code == 500) {
1610 fputs("RMD command not recognized, trying XRMD.\n",
1612 r = command("XRMD %s", argv[1]);
1619 * Send a line, verbatim, to the remote machine.
1622 quote(int argc, char *argv[])
1626 (argc == 1 && !another(&argc, &argv, "command line to send"))) {
1627 UPRINTF("usage: %s line-to-send\n", argv[0]);
1631 quote1("", argc, argv);
1635 * Send a SITE command to the remote machine. The line
1636 * is sent verbatim to the remote machine, except that the
1637 * word "SITE" is added at the front.
1640 site(int argc, char *argv[])
1644 (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){
1645 UPRINTF("usage: %s line-to-send\n", argv[0]);
1649 quote1("SITE ", argc, argv);
1653 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1654 * Send the result as a one-line command and get response.
1657 quote1(const char *initial, int argc, char *argv[])
1660 char buf[BUFSIZ]; /* must be >= sizeof(line) */
1662 (void)strlcpy(buf, initial, sizeof(buf));
1663 for (i = 1; i < argc; i++) {
1664 (void)strlcat(buf, argv[i], sizeof(buf));
1666 (void)strlcat(buf, " ", sizeof(buf));
1668 if (command("%s", buf) == PRELIM) {
1669 while (getreply(0) == PRELIM)
1676 do_chmod(int argc, char *argv[])
1679 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode")))
1681 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
1683 UPRINTF("usage: %s mode remote-file\n", argv[0]);
1687 (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1690 #define COMMAND_1ARG(argc, argv, cmd) \
1694 command(cmd " %s", argv[1])
1697 do_umask(int argc, char *argv[])
1699 int oldverbose = verbose;
1702 UPRINTF("usage: %s [umask]\n", argv[0]);
1707 COMMAND_1ARG(argc, argv, "SITE UMASK");
1708 verbose = oldverbose;
1712 idlecmd(int argc, char *argv[])
1714 int oldverbose = verbose;
1716 if (argc < 1 || argc > 2) {
1717 UPRINTF("usage: %s [seconds]\n", argv[0]);
1722 COMMAND_1ARG(argc, argv, "SITE IDLE");
1723 verbose = oldverbose;
1727 * Ask the other side for help.
1730 rmthelp(int argc, char *argv[])
1732 int oldverbose = verbose;
1735 UPRINTF("usage: %s\n", argv[0]);
1740 COMMAND_1ARG(argc, argv, "HELP");
1741 verbose = oldverbose;
1745 * Terminate session and exit.
1746 * May be called with 0, NULL.
1750 quit(int argc, char *argv[])
1753 /* this may be called with argc == 0, argv == NULL */
1754 if (argc == 0 && argv != NULL) {
1755 UPRINTF("usage: %s\n", argv[0]);
1760 disconnect(0, NULL);
1763 disconnect(0, NULL);
1768 * Terminate session, but don't exit.
1769 * May be called with 0, NULL.
1772 disconnect(int argc, char *argv[])
1775 /* this may be called with argc == 0, argv == NULL */
1776 if (argc == 0 && argv != NULL) {
1777 UPRINTF("usage: %s\n", argv[0]);
1783 (void)command("QUIT");
1788 account(int argc, char *argv[])
1791 char emptypass[] = "";
1793 if (argc == 0 || argc > 2) {
1794 UPRINTF("usage: %s [password]\n", argv[0]);
1801 ap = getpass("Account:");
1805 (void)command("ACCT %s", ap);
1806 memset(ap, 0, strlen(ap));
1809 sigjmp_buf abortprox;
1812 proxabort(int notused)
1827 siglongjmp(abortprox, 1);
1831 doproxy(int argc, char *argv[])
1837 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) {
1838 UPRINTF("usage: %s command\n", argv[0]);
1842 c = getcmd(argv[1]);
1843 if (c == (struct cmd *) -1) {
1844 fputs("?Ambiguous command.\n", ttyout);
1849 fputs("?Invalid command.\n", ttyout);
1854 fputs("?Invalid proxy command.\n", ttyout);
1858 if (sigsetjmp(abortprox, 1)) {
1862 oldintr = xsignal(SIGINT, proxabort);
1864 if (c->c_conn && !connected) {
1865 fputs("Not connected.\n", ttyout);
1867 (void)xsignal(SIGINT, oldintr);
1871 cmdpos = strcspn(line, " \t");
1872 if (cmdpos > 0) /* remove leading "proxy " from input buffer */
1873 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1874 argv[1] = c->c_name;
1875 (*c->c_handler)(argc-1, argv+1);
1883 (void)xsignal(SIGINT, oldintr);
1887 setcase(int argc, char *argv[])
1890 code = togglevar(argc, argv, &mcase, "Case mapping");
1894 * convert the given name to lower case if it's all upper case, into
1895 * a static buffer which is returned to the caller
1898 docase(char *dst, size_t dlen, const char *src)
1903 for (i = 0; src[i] != '\0' && i < dlen - 1; i++) {
1905 if (islower((unsigned char)dst[i]))
1911 for (i = 0; dst[i] != '\0'; i++)
1912 if (isupper((unsigned char)dst[i]))
1913 dst[i] = tolower((unsigned char)dst[i]);
1919 setcr(int argc, char *argv[])
1922 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1926 setntrans(int argc, char *argv[])
1929 if (argc == 0 || argc > 3) {
1930 UPRINTF("usage: %s [inchars [outchars]]\n", argv[0]);
1936 fputs("Ntrans off.\n", ttyout);
1942 (void)strlcpy(ntin, argv[1], sizeof(ntin));
1947 (void)strlcpy(ntout, argv[2], sizeof(ntout));
1951 dotrans(char *dst, size_t dlen, const char *src)
1957 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1959 for (cp1 = src; *cp1; cp1++) {
1961 for (i = 0; *(ntin + i) && i < 16; i++) {
1962 if (*cp1 == *(ntin + i)) {
1965 *cp2++ = *(ntout + i);
1966 if (cp2 - dst >= dlen - 1)
1982 setnmap(int argc, char *argv[])
1988 fputs("Nmap off.\n", ttyout);
1993 (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1994 UPRINTF("usage: %s [mapin mapout]\n", argv[0]);
2000 cp = strchr(altarg, ' ');
2005 cp = strchr(altarg, ' ');
2008 (void)strlcpy(mapin, altarg, MAXPATHLEN);
2009 while (*++cp == ' ')
2011 (void)strlcpy(mapout, cp, MAXPATHLEN);
2015 domap(char *dst, size_t dlen, const char *src)
2017 const char *cp1 = src;
2019 const char *tp[9], *te[9];
2020 int i, toks[9], toknum = 0, match = 1;
2022 for (i=0; i < 9; ++i) {
2025 while (match && *cp1 && *cp2) {
2028 if (*++cp2 != *cp1) {
2033 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
2034 if (*cp1 != *(++cp2+1)) {
2035 toks[toknum = *cp2 - '1']++;
2037 while (*++cp1 && *(cp2+1)
2051 if (match && *cp1) {
2054 if (match && *cp2) {
2058 if (!match && *cp1) /* last token mismatch */
2075 if (*++cp1 == '$' &&
2076 isdigit((unsigned char)*(cp1+1))) {
2077 if (*++cp1 == '0') {
2078 const char *cp3 = src;
2085 else if (toks[toknum = *cp1 - '1']) {
2086 const char *cp3 = tp[toknum];
2088 while (cp3 != te[toknum]) {
2095 while (*cp1 && *cp1 != ',' &&
2100 else if (*cp1 == '$' &&
2101 isdigit((unsigned char)*(cp1+1))) {
2102 if (*++cp1 == '0') {
2103 const char *cp3 = src;
2109 else if (toks[toknum =
2111 const char *cp3=tp[toknum];
2125 "nmap: unbalanced brackets.\n",
2133 while (*++cp1 && *cp1 != ']') {
2134 if (*cp1 == '\\' && *(cp1 + 1)) {
2140 "nmap: unbalanced brackets.\n",
2157 if (isdigit((unsigned char)*(cp1 + 1))) {
2158 if (*++cp1 == '0') {
2159 const char *cp3 = src;
2165 else if (toks[toknum = *cp1 - '1']) {
2166 const char *cp3 = tp[toknum];
2168 while (cp3 != te[toknum]) {
2174 /* intentional drop through */
2182 return *dst ? dst : src;
2186 setpassive(int argc, char *argv[])
2190 passivemode = !passivemode;
2191 activefallback = passivemode;
2192 } else if (argc != 2) {
2194 UPRINTF("usage: %s [ on | off | auto ]\n", argv[0]);
2197 } else if (strcasecmp(argv[1], "on") == 0) {
2200 } else if (strcasecmp(argv[1], "off") == 0) {
2203 } else if (strcasecmp(argv[1], "auto") == 0) {
2208 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
2209 onoff(passivemode), onoff(activefallback));
2215 setepsv4(int argc, char *argv[])
2217 code = togglevar(argc, argv, &epsv4,
2218 verbose ? "EPSV/EPRT on IPv4" : NULL);
2223 setepsv6(int argc, char *argv[])
2225 code = togglevar(argc, argv, &epsv6,
2226 verbose ? "EPSV/EPRT on IPv6" : NULL);
2231 setepsv(int argc, char*argv[])
2233 setepsv4(argc,argv);
2234 setepsv6(argc,argv);
2238 setsunique(int argc, char *argv[])
2241 code = togglevar(argc, argv, &sunique, "Store unique");
2245 setrunique(int argc, char *argv[])
2248 code = togglevar(argc, argv, &runique, "Receive unique");
2252 parserate(int argc, char *argv[], int cmdlineopt)
2254 int dir, max, incr, showonly;
2255 sigfunc oldusr1, oldusr2;
2257 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) {
2261 "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n",
2265 "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n",
2269 dir = max = incr = showonly = 0;
2272 #define RATE_ALL (RATE_GET | RATE_PUT)
2274 if (strcasecmp(argv[1], "all") == 0)
2276 else if (strcasecmp(argv[1], "get") == 0)
2278 else if (strcasecmp(argv[1], "put") == 0)
2284 if ((max = strsuftoi(argv[2])) < 0)
2290 if ((incr = strsuftoi(argv[3])) <= 0)
2295 oldusr1 = xsignal(SIGUSR1, SIG_IGN);
2296 oldusr2 = xsignal(SIGUSR2, SIG_IGN);
2297 if (dir & RATE_GET) {
2300 rate_get_incr = incr;
2302 if (!cmdlineopt || verbose)
2304 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
2305 onoff(rate_get), rate_get, rate_get_incr);
2307 if (dir & RATE_PUT) {
2310 rate_put_incr = incr;
2312 if (!cmdlineopt || verbose)
2314 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
2315 onoff(rate_put), rate_put, rate_put_incr);
2317 (void)xsignal(SIGUSR1, oldusr1);
2318 (void)xsignal(SIGUSR2, oldusr2);
2323 setrate(int argc, char *argv[])
2326 code = parserate(argc, argv, 0);
2329 /* change directory to parent directory */
2331 cdup(int argc, char *argv[])
2336 UPRINTF("usage: %s\n", argv[0]);
2340 r = command("CDUP");
2341 if (r == ERROR && code == 500) {
2343 fputs("CDUP command not recognized, trying XCUP.\n",
2345 r = command("XCUP");
2347 if (r == COMPLETE) {
2354 * Restart transfer at specific point
2357 restart(int argc, char *argv[])
2360 if (argc == 0 || argc > 2) {
2361 UPRINTF("usage: %s [restart-point]\n", argv[0]);
2365 if (! features[FEAT_REST_STREAM]) {
2367 "Restart is not supported by the remote server.\n");
2374 rp = STRTOLL(argv[1], &ep, 10);
2375 if (rp < 0 || *ep != '\0')
2376 fprintf(ttyout, "restart: Invalid offset `%s'\n",
2381 if (restart_point == 0)
2382 fputs("No restart point defined.\n", ttyout);
2385 "Restarting at " LLF " for next get, put or append\n",
2386 (LLT)restart_point);
2390 * Show remote system type
2393 syst(int argc, char *argv[])
2395 int oldverbose = verbose;
2398 UPRINTF("usage: %s\n", argv[0]);
2402 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2403 (void)command("SYST");
2404 verbose = oldverbose;
2408 macdef(int argc, char *argv[])
2416 fputs("Limit of 16 macros have already been defined.\n",
2421 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2423 UPRINTF("usage: %s macro_name\n", argv[0]);
2429 "Enter macro line by line, terminating it with a null line.\n",
2431 (void)strlcpy(macros[macnum].mac_name, argv[1],
2432 sizeof(macros[macnum].mac_name));
2434 macros[macnum].mac_start = macbuf;
2436 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2437 tmp = macros[macnum].mac_start;
2438 while (tmp != macbuf+4096) {
2439 if ((c = getchar()) == EOF) {
2440 fputs("macdef: end of file encountered.\n", ttyout);
2444 if ((*tmp = c) == '\n') {
2445 if (tmp == macros[macnum].mac_start) {
2446 macros[macnum++].mac_end = tmp;
2450 if (*(tmp-1) == '\0') {
2451 macros[macnum++].mac_end = tmp - 1;
2460 while ((c = getchar()) != '\n' && c != EOF)
2462 if (c == EOF || getchar() == '\n') {
2463 fputs("Macro not defined - 4K buffer exceeded.\n",
2472 * Get size of file on remote machine
2475 sizecmd(int argc, char *argv[])
2479 if (argc == 0 || argc > 2 ||
2480 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2481 UPRINTF("usage: %s remote-file\n", argv[0]);
2485 size = remotesize(argv[1], 1);
2488 "%s\t" LLF "\n", argv[1], (LLT)size);
2493 * Get last modification time of file on remote machine
2496 modtime(int argc, char *argv[])
2500 if (argc == 0 || argc > 2 ||
2501 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2502 UPRINTF("usage: %s remote-file\n", argv[0]);
2506 mtime = remotemodtime(argv[1], 1);
2508 fprintf(ttyout, "%s\t%s", argv[1],
2509 rfc2822time(localtime(&mtime)));
2514 * Show status on remote machine
2517 rmtstatus(int argc, char *argv[])
2521 UPRINTF("usage: %s [remote-file]\n", argv[0]);
2525 COMMAND_1ARG(argc, argv, "STAT");
2529 * Get file if modtime is more recent than current file
2532 newer(int argc, char *argv[])
2535 if (getit(argc, argv, -1, "w"))
2537 "Local file \"%s\" is newer than remote file \"%s\".\n",
2542 * Display one local file through $PAGER.
2545 lpage(int argc, char *argv[])
2548 char *p, *pager, *locfile;
2550 if (argc == 0 || argc > 2 ||
2551 (argc == 1 && !another(&argc, &argv, "local-file"))) {
2552 UPRINTF("usage: %s local-file\n", argv[0]);
2556 if ((locfile = globulize(argv[1])) == NULL) {
2560 p = getoptionvalue("pager");
2563 len = strlen(p) + strlen(locfile) + 2;
2564 pager = ftp_malloc(len);
2565 (void)strlcpy(pager, p, len);
2566 (void)strlcat(pager, " ", len);
2567 (void)strlcat(pager, locfile, len);
2571 (void)free(locfile);
2575 * Display one remote file through $PAGER.
2578 page(int argc, char *argv[])
2580 int ohash, orestart_point, overbose;
2584 if (argc == 0 || argc > 2 ||
2585 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2586 UPRINTF("usage: %s remote-file\n", argv[0]);
2590 p = getoptionvalue("pager");
2593 len = strlen(p) + 2;
2594 pager = ftp_malloc(len);
2596 (void)strlcpy(pager + 1, p, len - 1);
2599 orestart_point = restart_point;
2601 hash = restart_point = verbose = 0;
2602 recvrequest("RETR", pager, argv[1], "r+", 1, 0);
2604 restart_point = orestart_point;
2610 * Set the socket send or receive buffer size.
2613 setxferbuf(int argc, char *argv[])
2619 UPRINTF("usage: %s size\n", argv[0]);
2623 if (strcasecmp(argv[0], "sndbuf") == 0)
2625 else if (strcasecmp(argv[0], "rcvbuf") == 0)
2627 else if (strcasecmp(argv[0], "xferbuf") == 0)
2632 if ((size = strsuftoi(argv[1])) == -1)
2636 fprintf(ttyout, "%s: size must be positive.\n", argv[0]);
2644 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n",
2645 sndbuf_size, rcvbuf_size);
2650 * Set or display options (defaults are provided by various env vars)
2653 setoption(int argc, char *argv[])
2658 if (argc == 0 || (argc != 1 && argc != 3)) {
2659 UPRINTF("usage: %s [option value]\n", argv[0]);
2663 #define OPTIONINDENT ((int) sizeof("http_proxy"))
2665 for (o = optiontab; o->name != NULL; o++) {
2666 fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT,
2667 o->name, o->value ? o->value : "");
2670 o = getoption(argv[1]);
2672 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2676 o->value = ftp_strdup(argv[2]);
2678 fprintf(ttyout, "Setting `%s' to `%s'.\n",
2688 unsetoption(int argc, char *argv[])
2693 if (argc == 0 || argc != 2) {
2694 UPRINTF("usage: %s option\n", argv[0]);
2698 o = getoption(argv[1]);
2700 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2704 fprintf(ttyout, "Unsetting `%s'.\n", o->name);
2709 * Display features supported by the remote host.
2712 feat(int argc, char *argv[])
2714 int oldverbose = verbose;
2717 UPRINTF("usage: %s\n", argv[0]);
2721 if (! features[FEAT_FEAT]) {
2723 "FEAT is not supported by the remote server.\n");
2726 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2727 (void)command("FEAT");
2728 verbose = oldverbose;
2732 mlst(int argc, char *argv[])
2734 int oldverbose = verbose;
2736 if (argc < 1 || argc > 2) {
2737 UPRINTF("usage: %s [remote-path]\n", argv[0]);
2741 if (! features[FEAT_MLST]) {
2743 "MLST is not supported by the remote server.\n");
2746 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2747 COMMAND_1ARG(argc, argv, "MLST");
2748 verbose = oldverbose;
2752 opts(int argc, char *argv[])
2754 int oldverbose = verbose;
2756 if (argc < 2 || argc > 3) {
2757 UPRINTF("usage: %s command [options]\n", argv[0]);
2761 if (! features[FEAT_FEAT]) {
2763 "OPTS is not supported by the remote server.\n");
2766 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2768 command("OPTS %s", argv[1]);
2770 command("OPTS %s %s", argv[1], argv[2]);
2771 verbose = oldverbose;