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