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