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