Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.bin / ftp / cmds.c
1 /* $FreeBSD: src/usr.bin/ftp/cmds.c,v 1.16.2.3 2002/08/27 09:55:08 yar Exp $    */
2 /*      $NetBSD: cmds.c,v 1.30.2.1 1997/11/18 00:58:26 mellon Exp $     */
3
4 /*
5  * Copyright (c) 1985, 1989, 1993, 1994
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #include <sys/cdefs.h>
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)cmds.c      8.6 (Berkeley) 10/9/94";
41 #else
42 __RCSID("$FreeBSD: src/usr.bin/ftp/cmds.c,v 1.16.2.3 2002/08/27 09:55:08 yar Exp $");
43 __RCSID_SOURCE("$NetBSD: cmds.c,v 1.30.2.1 1997/11/18 00:58:26 mellon Exp $");
44 #endif
45 #endif /* not lint */
46
47 /*
48  * FTP User Program -- Command Routines.
49  */
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <sys/stat.h>
53 #include <sys/wait.h>
54 #include <arpa/ftp.h>
55
56 #include <ctype.h>
57 #include <err.h>
58 #include <glob.h>
59 #include <netdb.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <unistd.h>
64
65 #include "ftp_var.h"
66 #include "pathnames.h"
67
68 jmp_buf jabort;
69 char   *mname;
70 char   *home = "/";
71
72 struct  types {
73         char    *t_name;
74         char    *t_mode;
75         int     t_type;
76         char    *t_arg;
77 } types[] = {
78         { "ascii",      "A",    TYPE_A, 0 },
79         { "binary",     "I",    TYPE_I, 0 },
80         { "image",      "I",    TYPE_I, 0 },
81         { "ebcdic",     "E",    TYPE_E, 0 },
82         { "tenex",      "L",    TYPE_L, bytename },
83         { NULL }
84 };
85
86 /*
87  * Set transfer type.
88  */
89 void
90 settype(argc, argv)
91         int argc;
92         char *argv[];
93 {
94         struct types *p;
95         int comret;
96
97         if (argc > 2) {
98                 char *sep;
99
100                 printf("usage: %s [", argv[0]);
101                 sep = " ";
102                 for (p = types; p->t_name; p++) {
103                         printf("%s%s", sep, p->t_name);
104                         sep = " | ";
105                 }
106                 puts(" ]");
107                 code = -1;
108                 return;
109         }
110         if (argc < 2) {
111                 printf("Using %s mode to transfer files.\n", typename);
112                 code = 0;
113                 return;
114         }
115         for (p = types; p->t_name; p++)
116                 if (strcmp(argv[1], p->t_name) == 0)
117                         break;
118         if (p->t_name == 0) {
119                 printf("%s: unknown mode.\n", argv[1]);
120                 code = -1;
121                 return;
122         }
123         if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
124                 comret = command("TYPE %s %s", p->t_mode, p->t_arg);
125         else
126                 comret = command("TYPE %s", p->t_mode);
127         if (comret == COMPLETE) {
128                 (void)strcpy(typename, p->t_name);
129                 curtype = type = p->t_type;
130         }
131 }
132
133 /*
134  * Internal form of settype; changes current type in use with server
135  * without changing our notion of the type for data transfers.
136  * Used to change to and from ascii for listings.
137  */
138 void
139 changetype(newtype, show)
140         int newtype, show;
141 {
142         struct types *p;
143         int comret, oldverbose = verbose;
144
145         if (newtype == 0)
146                 newtype = TYPE_I;
147         if (newtype == curtype)
148                 return;
149         if (debug == 0 && show == 0)
150                 verbose = 0;
151         for (p = types; p->t_name; p++)
152                 if (newtype == p->t_type)
153                         break;
154         if (p->t_name == 0) {
155                 warnx("internal error: unknown type %d.", newtype);
156                 return;
157         }
158         if (newtype == TYPE_L && bytename[0] != '\0')
159                 comret = command("TYPE %s %s", p->t_mode, bytename);
160         else
161                 comret = command("TYPE %s", p->t_mode);
162         if (comret == COMPLETE)
163                 curtype = newtype;
164         verbose = oldverbose;
165 }
166
167 char *stype[] = {
168         "type",
169         "",
170         0
171 };
172
173 /*
174  * Set binary transfer type.
175  */
176 /*VARARGS*/
177 void
178 setbinary(argc, argv)
179         int argc;
180         char *argv[];
181 {
182
183         stype[1] = "binary";
184         settype(2, stype);
185 }
186
187 /*
188  * Set ascii transfer type.
189  */
190 /*VARARGS*/
191 void
192 setascii(argc, argv)
193         int argc;
194         char *argv[];
195 {
196
197         stype[1] = "ascii";
198         settype(2, stype);
199 }
200
201 /*
202  * Set tenex transfer type.
203  */
204 /*VARARGS*/
205 void
206 settenex(argc, argv)
207         int argc;
208         char *argv[];
209 {
210
211         stype[1] = "tenex";
212         settype(2, stype);
213 }
214
215 /*
216  * Set file transfer mode.
217  */
218 /*ARGSUSED*/
219 void
220 setftmode(argc, argv)
221         int argc;
222         char *argv[];
223 {
224
225         printf("We only support %s mode, sorry.\n", modename);
226         code = -1;
227 }
228
229 /*
230  * Set file transfer format.
231  */
232 /*ARGSUSED*/
233 void
234 setform(argc, argv)
235         int argc;
236         char *argv[];
237 {
238
239         printf("We only support %s format, sorry.\n", formname);
240         code = -1;
241 }
242
243 /*
244  * Set file transfer structure.
245  */
246 /*ARGSUSED*/
247 void
248 setstruct(argc, argv)
249         int argc;
250         char *argv[];
251 {
252
253         printf("We only support %s structure, sorry.\n", structname);
254         code = -1;
255 }
256
257 /*
258  * Send a single file.
259  */
260 void
261 put(argc, argv)
262         int argc;
263         char *argv[];
264 {
265         char *cmd;
266         int loc = 0;
267         char *oldargv1, *oldargv2;
268
269         if (argc == 2) {
270                 argc++;
271                 argv[2] = argv[1];
272                 loc++;
273         }
274         if (argc < 2 && !another(&argc, &argv, "local-file"))
275                 goto usage;
276         if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
277 usage:
278                 printf("usage: %s local-file [ remote-file ]\n", argv[0]);
279                 code = -1;
280                 return;
281         }
282         oldargv1 = argv[1];
283         oldargv2 = argv[2];
284         if (!globulize(&argv[1])) {
285                 code = -1;
286                 return;
287         }
288         /*
289          * If "globulize" modifies argv[1], and argv[2] is a copy of
290          * the old argv[1], make it a copy of the new argv[1].
291          */
292         if (argv[1] != oldargv1 && argv[2] == oldargv1) {
293                 argv[2] = argv[1];
294         }
295         cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
296         if (loc && ntflag) {
297                 argv[2] = dotrans(argv[2]);
298         }
299         if (loc && mapflag) {
300                 argv[2] = domap(argv[2]);
301         }
302         sendrequest(cmd, argv[1], argv[2],
303             argv[1] != oldargv1 || argv[2] != oldargv2);
304         if (oldargv1 != argv[1])        /* free up after globulize() */
305                 free(argv[1]);
306 }
307
308 /*
309  * Send multiple files.
310  */
311 void
312 mput(argc, argv)
313         int argc;
314         char *argv[];
315 {
316         int i;
317         sig_t oldintr;
318         int ointer;
319         char *tp;
320
321         if (argc < 2 && !another(&argc, &argv, "local-files")) {
322                 printf("usage: %s local-files\n", argv[0]);
323                 code = -1;
324                 return;
325         }
326         mname = argv[0];
327         mflag = 1;
328         oldintr = signal(SIGINT, mabort);
329         (void)setjmp(jabort);
330         if (proxy) {
331                 char *cp, *tp2, tmpbuf[MAXPATHLEN];
332
333                 while ((cp = remglob(argv, 0, NULL)) != NULL) {
334                         if (*cp == '\0') {
335                                 mflag = 0;
336                                 continue;
337                         }
338                         if (mflag && confirm(argv[0], cp)) {
339                                 tp = cp;
340                                 if (mcase) {
341                                         while (*tp && !islower((unsigned char)*tp)) {
342                                                 tp++;
343                                         }
344                                         if (!*tp) {
345                                                 tp = cp;
346                                                 tp2 = tmpbuf;
347                                                 while ((*tp2 = *tp) != '\0') {
348                                                      if (isupper((unsigned char)*tp2))
349                                                         *tp2 = tolower((unsigned char)*tp2);
350                                                      tp++;
351                                                      tp2++;
352                                                 }
353                                         }
354                                         tp = tmpbuf;
355                                 }
356                                 if (ntflag) {
357                                         tp = dotrans(tp);
358                                 }
359                                 if (mapflag) {
360                                         tp = domap(tp);
361                                 }
362                                 sendrequest((sunique) ? "STOU" : "STOR",
363                                     cp, tp, cp != tp || !interactive);
364                                 if (!mflag && fromatty) {
365                                         ointer = interactive;
366                                         interactive = 1;
367                                         if (confirm("Continue with", "mput")) {
368                                                 mflag++;
369                                         }
370                                         interactive = ointer;
371                                 }
372                         }
373                 }
374                 (void)signal(SIGINT, oldintr);
375                 mflag = 0;
376                 return;
377         }
378         for (i = 1; i < argc; i++) {
379                 char **cpp;
380                 glob_t gl;
381                 int flags;
382
383                 if (!doglob) {
384                         if (mflag && confirm(argv[0], argv[i])) {
385                                 tp = (ntflag) ? dotrans(argv[i]) : argv[i];
386                                 tp = (mapflag) ? domap(tp) : tp;
387                                 sendrequest((sunique) ? "STOU" : "STOR",
388                                     argv[i], tp, tp != argv[i] || !interactive);
389                                 if (!mflag && fromatty) {
390                                         ointer = interactive;
391                                         interactive = 1;
392                                         if (confirm("Continue with", "mput")) {
393                                                 mflag++;
394                                         }
395                                         interactive = ointer;
396                                 }
397                         }
398                         continue;
399                 }
400
401                 memset(&gl, 0, sizeof(gl));
402                 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
403                 if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
404                         warnx("%s: not found", argv[i]);
405                         globfree(&gl);
406                         continue;
407                 }
408                 for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) {
409                         if (mflag && confirm(argv[0], *cpp)) {
410                                 tp = (ntflag) ? dotrans(*cpp) : *cpp;
411                                 tp = (mapflag) ? domap(tp) : tp;
412                                 sendrequest((sunique) ? "STOU" : "STOR",
413                                     *cpp, tp, *cpp != tp || !interactive);
414                                 if (!mflag && fromatty) {
415                                         ointer = interactive;
416                                         interactive = 1;
417                                         if (confirm("Continue with", "mput")) {
418                                                 mflag++;
419                                         }
420                                         interactive = ointer;
421                                 }
422                         }
423                 }
424                 globfree(&gl);
425         }
426         (void)signal(SIGINT, oldintr);
427         mflag = 0;
428 }
429
430 void
431 reget(argc, argv)
432         int argc;
433         char *argv[];
434 {
435
436         (void)getit(argc, argv, 1, "r+w");
437 }
438
439 void
440 get(argc, argv)
441         int argc;
442         char *argv[];
443 {
444
445         (void)getit(argc, argv, 0, restart_point ? "r+w" : "w" );
446 }
447
448 /*
449  * Receive one file.
450  */
451 int
452 getit(argc, argv, restartit, mode)
453         int argc;
454         char *argv[];
455         int restartit;
456         const char *mode;
457 {
458         int loc = 0;
459         int rval = 0;
460         char *oldargv1, *oldargv2, *globargv2;
461
462         if (argc == 2) {
463                 argc++;
464                 argv[2] = argv[1];
465                 loc++;
466         }
467         if (argc < 2 && !another(&argc, &argv, "remote-file"))
468                 goto usage;
469         if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
470 usage:
471                 printf("usage: %s remote-file [ local-file ]\n", argv[0]);
472                 code = -1;
473                 return (0);
474         }
475         oldargv1 = argv[1];
476         oldargv2 = argv[2];
477         if (!globulize(&argv[2])) {
478                 code = -1;
479                 return (0);
480         }
481         globargv2 = argv[2];
482         if (loc && mcase) {
483                 char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
484
485                 while (*tp && !islower((unsigned char)*tp)) {
486                         tp++;
487                 }
488                 if (!*tp) {
489                         tp = argv[2];
490                         tp2 = tmpbuf;
491                         while ((*tp2 = *tp) != '\0') {
492                                 if (isupper((unsigned char)*tp2)) {
493                                         *tp2 = tolower((unsigned char)*tp2);
494                                 }
495                                 tp++;
496                                 tp2++;
497                         }
498                         argv[2] = tmpbuf;
499                 }
500         }
501         if (loc && ntflag)
502                 argv[2] = dotrans(argv[2]);
503         if (loc && mapflag)
504                 argv[2] = domap(argv[2]);
505         if (restartit) {
506                 struct stat stbuf;
507                 int ret;
508
509                 ret = stat(argv[2], &stbuf);
510                 if (restartit == 1) {
511                         if (ret < 0) {
512                                 warn("local: %s", argv[2]);
513                                 goto freegetit;
514                         }
515                         restart_point = stbuf.st_size;
516                 } else {
517                         if (ret == 0) {
518                                 time_t mtime;
519
520                                 mtime = remotemodtime(argv[1], 0);
521                                 if (mtime == -1)
522                                         goto freegetit;
523                                 if (stbuf.st_mtime >= mtime) {
524                                         rval = 1;
525                                         goto freegetit;
526                                 }
527                         }
528                 }
529         }
530
531         recvrequest("RETR", argv[2], argv[1], mode,
532             argv[1] != oldargv1 || argv[2] != oldargv2, loc);
533         restart_point = 0;
534 freegetit:
535         if (oldargv2 != globargv2)      /* free up after globulize() */
536                 free(globargv2);
537         return (rval);
538 }
539
540 /* ARGSUSED */
541 void
542 mabort(signo)
543         int signo;
544 {
545         int ointer, oconf;
546
547         alarmtimer(0);
548         putchar('\n');
549         (void)fflush(stdout);
550         if (mflag && fromatty) {
551                 ointer = interactive;
552                 oconf = confirmrest;
553                 interactive = 1;
554                 confirmrest = 0;
555                 if (confirm("Continue with", mname)) {
556                         interactive = ointer;
557                         confirmrest = oconf;
558                         longjmp(jabort, 0);
559                 }
560                 interactive = ointer;
561                 confirmrest = oconf;
562         }
563         mflag = 0;
564         longjmp(jabort, 0);
565 }
566
567 /*
568  * Get multiple files.
569  */
570 void
571 mget(argc, argv)
572         int argc;
573         char *argv[];
574 {
575         sig_t oldintr;
576         int ch, ointer;
577         char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
578
579         if (argc < 2 && !another(&argc, &argv, "remote-files")) {
580                 printf("usage: %s remote-files\n", argv[0]);
581                 code = -1;
582                 return;
583         }
584         mname = argv[0];
585         mflag = 1;
586         oldintr = signal(SIGINT, mabort);
587         (void)setjmp(jabort);
588         while ((cp = remglob(argv, proxy, NULL)) != NULL) {
589                 if (*cp == '\0') {
590                         mflag = 0;
591                         continue;
592                 }
593                 if (mflag && confirm(argv[0], cp)) {
594                         tp = cp;
595                         if (mcase) {
596                                 for (tp2 = tmpbuf; (ch = *tp++) != 0; )
597                                         *tp2++ = isupper((unsigned char)ch) ?
598                                                  tolower((unsigned char)ch) :
599                                                  ch;
600                                 *tp2 = '\0';
601                                 tp = tmpbuf;
602                         }
603                         if (ntflag) {
604                                 tp = dotrans(tp);
605                         }
606                         if (mapflag) {
607                                 tp = domap(tp);
608                         }
609                         recvrequest("RETR", tp, cp, "w",
610                             tp != cp || !interactive, 1);
611                         if (!mflag && fromatty) {
612                                 ointer = interactive;
613                                 interactive = 1;
614                                 if (confirm("Continue with", "mget")) {
615                                         mflag++;
616                                 }
617                                 interactive = ointer;
618                         }
619                 }
620         }
621         (void)signal(SIGINT, oldintr);
622         mflag = 0;
623 }
624
625 char *
626 onoff(bool)
627         int bool;
628 {
629
630         return (bool ? "on" : "off");
631 }
632
633 /*
634  * Show status.
635  */
636 /*ARGSUSED*/
637 void
638 status(argc, argv)
639         int argc;
640         char *argv[];
641 {
642         int i;
643
644         if (connected)
645                 printf("Connected %sto %s.\n",
646                     connected == -1 ? "and logged in" : "", hostname);
647         else
648                 puts("Not connected.");
649         if (!proxy) {
650                 pswitch(1);
651                 if (connected) {
652                         printf("Connected for proxy commands to %s.\n",
653                             hostname);
654                 }
655                 else {
656                         puts("No proxy connection.");
657                 }
658                 pswitch(0);
659         }
660         printf("Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
661             *gateserver ? gateserver : "(none)", gateport);
662         printf("Passive mode: %s.\n", onoff(passivemode));
663         printf("Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
664                 modename, typename, formname, structname);
665         printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
666                 onoff(verbose), onoff(bell), onoff(interactive),
667                 onoff(doglob));
668         printf("Store unique: %s; Receive unique: %s.\n", onoff(sunique),
669                 onoff(runique));
670         printf("Preserve modification times: %s.\n", onoff(preserve));
671         printf("Case: %s; CR stripping: %s.\n", onoff(mcase), onoff(crflag));
672         if (ntflag) {
673                 printf("Ntrans: (in) %s (out) %s\n", ntin, ntout);
674         }
675         else {
676                 puts("Ntrans: off.");
677         }
678         if (mapflag) {
679                 printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
680         }
681         else {
682                 puts("Nmap: off.");
683         }
684         printf("Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
685             onoff(hash), mark, onoff(progress));
686         printf("Use of PORT cmds: %s.\n", onoff(sendport));
687         printf("Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4),
688             (!try_epsv && epsv4) ? " (but disabled for this connection)" : "");
689 #ifndef SMALL
690         printf("Command line editing: %s.\n", onoff(editing));
691 #endif /* !SMALL */
692         if (macnum > 0) {
693                 puts("Macros:");
694                 for (i=0; i<macnum; i++) {
695                         printf("\t%s\n", macros[i].mac_name);
696                 }
697         }
698         code = 0;
699 }
700
701 /*
702  * Toggle a variable
703  */
704 int
705 togglevar(argc, argv, var, mesg)
706         int   argc;
707         char *argv[];
708         int  *var;
709         const char *mesg;
710 {
711         if (argc < 2) {
712                 *var = !*var;
713         } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
714                 *var = 1;
715         } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
716                 *var = 0;
717         } else {
718                 printf("usage: %s [ on | off ]\n", argv[0]);
719                 return (-1);
720         }
721         if (mesg)
722                 printf("%s %s.\n", mesg, onoff(*var));
723         return (*var);
724 }
725
726 /*
727  * Set beep on cmd completed mode.
728  */
729 /*VARARGS*/
730 void
731 setbell(argc, argv)
732         int argc;
733         char *argv[];
734 {
735
736         code = togglevar(argc, argv, &bell, "Bell mode");
737 }
738
739 #ifndef SMALL
740 /*
741  * Set command line editing
742  */
743 /*VARARGS*/
744 void
745 setedit(argc, argv)
746         int argc;
747         char *argv[];
748 {
749
750         code = togglevar(argc, argv, &editing, "Editing mode");
751         controlediting();
752 }
753 #endif /* !SMALL */
754
755 /*
756  * Turn on packet tracing.
757  */
758 /*VARARGS*/
759 void
760 settrace(argc, argv)
761         int argc;
762         char *argv[];
763 {
764
765         code = togglevar(argc, argv, &trace, "Packet tracing");
766 }
767
768 /*
769  * Toggle hash mark printing during transfers, or set hash mark bytecount.
770  */
771 /*VARARGS*/
772 void
773 sethash(argc, argv)
774         int argc;
775         char *argv[];
776 {
777         if (argc == 1)
778                 hash = !hash;
779         else if (argc != 2) {
780                 printf("usage: %s [ on | off | bytecount ]\n", argv[0]);
781                 code = -1;
782                 return;
783         } else if (strcasecmp(argv[1], "on") == 0)
784                 hash = 1;
785         else if (strcasecmp(argv[1], "off") == 0)
786                 hash = 0;
787         else {
788                 int nmark;
789                 char *ep;
790
791                 nmark = strtol(argv[1], &ep, 10);
792                 if (nmark < 1 || *ep != '\0') {
793                         printf("mark: bad bytecount value `%s'.\n", argv[1]);
794                         code = -1;
795                         return;
796                 }
797                 mark = nmark;
798                 hash = 1;
799         }
800         printf("Hash mark printing %s", onoff(hash));
801         if (hash)
802                 printf(" (%d bytes/hash mark)", mark);
803         puts(".");
804         code = hash;
805 }
806
807 /*
808  * Turn on printing of server echo's.
809  */
810 /*VARARGS*/
811 void
812 setverbose(argc, argv)
813         int argc;
814         char *argv[];
815 {
816
817         code = togglevar(argc, argv, &verbose, "Verbose mode");
818 }
819
820 /*
821  * Toggle PORT cmd use before each data connection.
822  */
823 /*VARARGS*/
824 void
825 setport(argc, argv)
826         int argc;
827         char *argv[];
828 {
829
830         code = togglevar(argc, argv, &sendport, "Use of PORT cmds");
831 }
832
833 /*
834  * Toggle transfer progress bar.
835  */
836 /*VARARGS*/
837 void
838 setprogress(argc, argv)
839         int argc;
840         char *argv[];
841 {
842
843         code = togglevar(argc, argv, &progress, "Progress bar");
844 }
845
846 /*
847  * Turn on interactive prompting during mget, mput, and mdelete.
848  */
849 /*VARARGS*/
850 void
851 setprompt(argc, argv)
852         int argc;
853         char *argv[];
854 {
855
856         code = togglevar(argc, argv, &interactive, "Interactive mode");
857 }
858
859 /*
860  * Toggle gate-ftp mode, or set gate-ftp server
861  */
862 /*VARARGS*/
863 void
864 setgate(argc, argv)
865         int argc;
866         char *argv[];
867 {
868         static char gsbuf[MAXHOSTNAMELEN];
869
870         if (argc > 3) {
871                 printf("usage: %s [ on | off | gateserver [ port ] ]\n",
872                     argv[0]);
873                 code = -1;
874                 return;
875         } else if (argc < 2) {
876                 gatemode = !gatemode;
877         } else {
878                 if (argc == 2 && strcasecmp(argv[1], "on") == 0)
879                         gatemode = 1;
880                 else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
881                         gatemode = 0;
882                 else {
883                         if (argc == 3) {
884                                 char *ep;
885                                 long port;
886
887                                 port = strtol(argv[2], &ep, 10);
888                                 if (port < 0 || port > 0xffff || *ep != '\0') {
889                                         printf("%s: bad gateport value.\n",
890                                             argv[2]);
891                                         code = -1;
892                                         return;
893                                 }
894                                 if (gateport != NULL)
895                                         free(gateport);
896                                 asprintf(&gateport, "%ld", port);
897                         }
898                         strncpy(gsbuf, argv[1], sizeof(gsbuf) - 1);
899                         gsbuf[sizeof(gsbuf) - 1] = '\0';
900                         gateserver = gsbuf;
901                         gatemode = 1;
902                 }
903         }
904         if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
905                 printf(
906                     "Disabling gate-ftp mode - no gate-ftp server defined.\n");
907                 gatemode = 0;
908         } else {
909                 printf("Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
910                     *gateserver ? gateserver : "(none)", gateport);
911         }
912         code = gatemode;
913 }
914
915 /*
916  * Toggle metacharacter interpretation on local file names.
917  */
918 /*VARARGS*/
919 void
920 setglob(argc, argv)
921         int argc;
922         char *argv[];
923 {
924
925         code = togglevar(argc, argv, &doglob, "Globbing");
926 }
927
928 /*
929  * Toggle preserving modification times on retrieved files.
930  */
931 /*VARARGS*/
932 void
933 setpreserve(argc, argv)
934         int argc;
935         char *argv[];
936 {
937
938         code = togglevar(argc, argv, &preserve, "Preserve modification times");
939 }
940
941 /*
942  * Set debugging mode on/off and/or set level of debugging.
943  */
944 /*VARARGS*/
945 void
946 setdebug(argc, argv)
947         int argc;
948         char *argv[];
949 {
950         if (argc > 2) {
951                 printf("usage: %s [ on | off | debuglevel ]\n", argv[0]);
952                 code = -1;
953                 return;
954         } else if (argc == 2) {
955                 if (strcasecmp(argv[1], "on") == 0)
956                         debug = 1;
957                 else if (strcasecmp(argv[1], "off") == 0)
958                         debug = 0;
959                 else {
960                         char *ep;
961                         long val;
962
963                         val = strtol(argv[1], &ep, 10);
964                         if (val < 0 || val > INT_MAX || *ep != '\0') {
965                                 printf("%s: bad debugging value.\n", argv[1]);
966                                 code = -1;
967                                 return;
968                         }
969                         debug = (int)val;
970                 }
971         } else
972                 debug = !debug;
973         if (debug)
974                 options |= SO_DEBUG;
975         else
976                 options &= ~SO_DEBUG;
977         printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
978         code = debug > 0;
979 }
980
981 /*
982  * Set current working directory on remote machine.
983  */
984 void
985 cd(argc, argv)
986         int argc;
987         char *argv[];
988 {
989         int r;
990
991         if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
992             argc > 2) {
993                 printf("usage: %s remote-directory\n", argv[0]);
994                 code = -1;
995                 return;
996         }
997         r = command("CWD %s", argv[1]);
998         if (r == ERROR && code == 500) {
999                 if (verbose)
1000                         puts("CWD command not recognized, trying XCWD.");
1001                 r = command("XCWD %s", argv[1]);
1002         }
1003         if (r == COMPLETE)
1004                 dirchange = 1;
1005 }
1006
1007 /*
1008  * Set current working directory on local machine.
1009  */
1010 void
1011 lcd(argc, argv)
1012         int argc;
1013         char *argv[];
1014 {
1015         char buf[MAXPATHLEN];
1016         char *oldargv1;
1017
1018         if (argc < 2)
1019                 argc++, argv[1] = home;
1020         if (argc != 2) {
1021                 printf("usage: %s local-directory\n", argv[0]);
1022                 code = -1;
1023                 return;
1024         }
1025         oldargv1 = argv[1];
1026         if (!globulize(&argv[1])) {
1027                 code = -1;
1028                 return;
1029         }
1030         if (chdir(argv[1]) < 0) {
1031                 warn("local: %s", argv[1]);
1032                 code = -1;
1033         } else {
1034                 if (getcwd(buf, sizeof(buf)) != NULL)
1035                         printf("Local directory now %s\n", buf);
1036                 else
1037                         warn("getcwd: %s", argv[1]);
1038                 code = 0;
1039         }
1040         if (oldargv1 != argv[1])        /* free up after globulize() */
1041                 free(argv[1]);
1042 }
1043
1044 /*
1045  * Delete a single file.
1046  */
1047 void
1048 delete(argc, argv)
1049         int argc;
1050         char *argv[];
1051 {
1052
1053         if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) {
1054                 printf("usage: %s remote-file\n", argv[0]);
1055                 code = -1;
1056                 return;
1057         }
1058         (void)command("DELE %s", argv[1]);
1059 }
1060
1061 /*
1062  * Delete multiple files.
1063  */
1064 void
1065 mdelete(argc, argv)
1066         int argc;
1067         char *argv[];
1068 {
1069         sig_t oldintr;
1070         int ointer;
1071         char *cp;
1072
1073         if (argc < 2 && !another(&argc, &argv, "remote-files")) {
1074                 printf("usage: %s remote-files\n", argv[0]);
1075                 code = -1;
1076                 return;
1077         }
1078         mname = argv[0];
1079         mflag = 1;
1080         oldintr = signal(SIGINT, mabort);
1081         (void)setjmp(jabort);
1082         while ((cp = remglob(argv, 0, NULL)) != NULL) {
1083                 if (*cp == '\0') {
1084                         mflag = 0;
1085                         continue;
1086                 }
1087                 if (mflag && confirm(argv[0], cp)) {
1088                         (void)command("DELE %s", cp);
1089                         if (!mflag && fromatty) {
1090                                 ointer = interactive;
1091                                 interactive = 1;
1092                                 if (confirm("Continue with", "mdelete")) {
1093                                         mflag++;
1094                                 }
1095                                 interactive = ointer;
1096                         }
1097                 }
1098         }
1099         (void)signal(SIGINT, oldintr);
1100         mflag = 0;
1101 }
1102
1103 /*
1104  * Rename a remote file.
1105  */
1106 void
1107 renamefile(argc, argv)
1108         int argc;
1109         char *argv[];
1110 {
1111
1112         if (argc < 2 && !another(&argc, &argv, "from-name"))
1113                 goto usage;
1114         if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1115 usage:
1116                 printf("usage: %s from-name to-name\n", argv[0]);
1117                 code = -1;
1118                 return;
1119         }
1120         if (command("RNFR %s", argv[1]) == CONTINUE)
1121                 (void)command("RNTO %s", argv[2]);
1122 }
1123
1124 /*
1125  * Get a directory listing of remote files.
1126  */
1127 void
1128 ls(argc, argv)
1129         int argc;
1130         char *argv[];
1131 {
1132         const char *cmd;
1133         char *oldargv2, *globargv2;
1134
1135         if (argc < 2)
1136                 argc++, argv[1] = NULL;
1137         if (argc < 3)
1138                 argc++, argv[2] = "-";
1139         if (argc > 3) {
1140                 printf("usage: %s remote-directory local-file\n", argv[0]);
1141                 code = -1;
1142                 return;
1143         }
1144         cmd = strcmp(argv[0], "nlist") == 0 ? "NLST" : "LIST";
1145         oldargv2 = argv[2];
1146         if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
1147                 code = -1;
1148                 return;
1149         }
1150         globargv2 = argv[2];
1151         if (strcmp(argv[2], "-") && *argv[2] != '|')
1152                 if (!globulize(&argv[2]) || !confirm("output to local-file:",
1153                     argv[2])) {
1154                         code = -1;
1155                         goto freels;
1156         }
1157         recvrequest(cmd, argv[2], argv[1], "w", 0, 0);
1158
1159         /* flush results in case commands are coming from a pipe */
1160         fflush(stdout);
1161 freels:
1162         if (argv[2] != globargv2)               /* free up after globulize() */
1163                 free(argv[2]);
1164         if (globargv2 != oldargv2)
1165                 free(globargv2);
1166 }
1167
1168 /*
1169  * Get a directory listing of multiple remote files.
1170  */
1171 void
1172 mls(argc, argv)
1173         int argc;
1174         char *argv[];
1175 {
1176         sig_t oldintr;
1177         int ointer, i;
1178         int dolist;
1179         char mode[1], *dest, *odest;
1180
1181         if (argc < 2 && !another(&argc, &argv, "remote-files"))
1182                 goto usage;
1183         if (argc < 3 && !another(&argc, &argv, "local-file")) {
1184 usage:
1185                 printf("usage: %s remote-files local-file\n", argv[0]);
1186                 code = -1;
1187                 return;
1188         }
1189         odest = dest = argv[argc - 1];
1190         argv[argc - 1] = NULL;
1191         if (strcmp(dest, "-") && *dest != '|')
1192                 if (!globulize(&dest) ||
1193                     !confirm("output to local-file:", dest)) {
1194                         code = -1;
1195                         return;
1196         }
1197         dolist = strcmp(argv[0], "mls");
1198         mname = argv[0];
1199         mflag = 1;
1200         oldintr = signal(SIGINT, mabort);
1201         (void)setjmp(jabort);
1202         for (i = 1; mflag && i < argc-1; ++i) {
1203                 *mode = (i == 1) ? 'w' : 'a';
1204                 recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode,
1205                     0, 0);
1206                 if (!mflag && fromatty) {
1207                         ointer = interactive;
1208                         interactive = 1;
1209                         if (confirm("Continue with", argv[0])) {
1210                                 mflag ++;
1211                         }
1212                         interactive = ointer;
1213                 }
1214         }
1215         (void)signal(SIGINT, oldintr);
1216         mflag = 0;
1217         if (dest != odest)                      /* free up after globulize() */
1218                 free(dest);
1219 }
1220
1221 /*
1222  * Do a shell escape
1223  */
1224 /*ARGSUSED*/
1225 void
1226 shell(argc, argv)
1227         int argc;
1228         char *argv[];
1229 {
1230         pid_t pid;
1231         sig_t old1, old2;
1232         char shellnam[MAXPATHLEN], *shell, *namep;
1233         int wait_status;
1234
1235         old1 = signal (SIGINT, SIG_IGN);
1236         old2 = signal (SIGQUIT, SIG_IGN);
1237         if ((pid = fork()) == 0) {
1238                 for (pid = 3; pid < 20; pid++)
1239                         (void)close(pid);
1240                 (void)signal(SIGINT, SIG_DFL);
1241                 (void)signal(SIGQUIT, SIG_DFL);
1242                 shell = getenv("SHELL");
1243                 if (shell == NULL)
1244                         shell = _PATH_BSHELL;
1245                 namep = strrchr(shell, '/');
1246                 if (namep == NULL)
1247                         namep = shell;
1248                 shellnam[0] = '-';
1249                 (void)strncpy(shellnam + 1, ++namep, sizeof(shellnam) - 2);
1250                 shellnam[sizeof(shellnam) - 1] = '\0';
1251                 if (strcmp(namep, "sh") != 0)
1252                         shellnam[0] = '+';
1253                 if (debug) {
1254                         puts(shell);
1255                         (void)fflush(stdout);
1256                 }
1257                 if (argc > 1) {
1258                         execl(shell, shellnam, "-c", altarg, (char *)0);
1259                 }
1260                 else {
1261                         execl(shell, shellnam, (char *)0);
1262                 }
1263                 warn("%s", shell);
1264                 code = -1;
1265                 exit(1);
1266         }
1267         if (pid > 0)
1268                 while (wait(&wait_status) != pid)
1269                         ;
1270         (void)signal(SIGINT, old1);
1271         (void)signal(SIGQUIT, old2);
1272         if (pid == -1) {
1273                 warn("Try again later");
1274                 code = -1;
1275         }
1276         else {
1277                 code = 0;
1278         }
1279 }
1280
1281 /*
1282  * Send new user information (re-login)
1283  */
1284 void
1285 user(argc, argv)
1286         int argc;
1287         char *argv[];
1288 {
1289         char acct[80];
1290         int n, aflag = 0;
1291
1292         if (argc < 2)
1293                 (void)another(&argc, &argv, "username");
1294         if (argc < 2 || argc > 4) {
1295                 printf("usage: %s username [password] [account]\n", argv[0]);
1296                 code = -1;
1297                 return;
1298         }
1299         n = command("USER %s", argv[1]);
1300         if (n == CONTINUE) {
1301                 if (argc < 3 )
1302                         argv[2] = getpass("Password: "), argc++;
1303                 n = command("PASS %s", argv[2]);
1304         }
1305         if (n == CONTINUE) {
1306                 if (argc < 4) {
1307                         (void)fputs("Account: ", stdout);
1308                         (void)fflush(stdout);
1309                         (void)fgets(acct, sizeof(acct) - 1, stdin);
1310                         acct[strlen(acct) - 1] = '\0';
1311                         argv[3] = acct; argc++;
1312                 }
1313                 n = command("ACCT %s", argv[3]);
1314                 aflag++;
1315         }
1316         if (n != COMPLETE) {
1317                 puts("Login failed.");
1318                 return;
1319         }
1320         if (!aflag && argc == 4) {
1321                 (void)command("ACCT %s", argv[3]);
1322         }
1323         connected = -1;
1324 }
1325
1326 /*
1327  * Print working directory on remote machine.
1328  */
1329 /*VARARGS*/
1330 void
1331 pwd(argc, argv)
1332         int argc;
1333         char *argv[];
1334 {
1335         int oldverbose = verbose;
1336
1337         /*
1338          * If we aren't verbose, this doesn't do anything!
1339          */
1340         verbose = 1;
1341         if (command("PWD") == ERROR && code == 500) {
1342                 puts("PWD command not recognized, trying XPWD.");
1343                 (void)command("XPWD");
1344         }
1345         verbose = oldverbose;
1346 }
1347
1348 /*
1349  * Print working directory on local machine.
1350  */
1351 void
1352 lpwd(argc, argv)
1353         int argc;
1354         char *argv[];
1355 {
1356         char buf[MAXPATHLEN];
1357
1358         if (getcwd(buf, sizeof(buf)) != NULL)
1359                 printf("Local directory %s\n", buf);
1360         else
1361                 warn("getcwd");
1362         code = 0;
1363 }
1364
1365 /*
1366  * Make a directory.
1367  */
1368 void
1369 makedir(argc, argv)
1370         int argc;
1371         char *argv[];
1372 {
1373
1374         if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1375             argc > 2) {
1376                 printf("usage: %s directory-name\n", argv[0]);
1377                 code = -1;
1378                 return;
1379         }
1380         if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1381                 if (verbose)
1382                         puts("MKD command not recognized, trying XMKD.");
1383                 (void)command("XMKD %s", argv[1]);
1384         }
1385 }
1386
1387 /*
1388  * Remove a directory.
1389  */
1390 void
1391 removedir(argc, argv)
1392         int argc;
1393         char *argv[];
1394 {
1395
1396         if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1397             argc > 2) {
1398                 printf("usage: %s directory-name\n", argv[0]);
1399                 code = -1;
1400                 return;
1401         }
1402         if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1403                 if (verbose)
1404                         puts("RMD command not recognized, trying XRMD.");
1405                 (void)command("XRMD %s", argv[1]);
1406         }
1407 }
1408
1409 /*
1410  * Send a line, verbatim, to the remote machine.
1411  */
1412 void
1413 quote(argc, argv)
1414         int argc;
1415         char *argv[];
1416 {
1417
1418         if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1419                 printf("usage: %s line-to-send\n", argv[0]);
1420                 code = -1;
1421                 return;
1422         }
1423         quote1("", argc, argv);
1424 }
1425
1426 /*
1427  * Send a SITE command to the remote machine.  The line
1428  * is sent verbatim to the remote machine, except that the
1429  * word "SITE" is added at the front.
1430  */
1431 void
1432 site(argc, argv)
1433         int argc;
1434         char *argv[];
1435 {
1436
1437         if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1438                 printf("usage: %s line-to-send\n", argv[0]);
1439                 code = -1;
1440                 return;
1441         }
1442         quote1("SITE ", argc, argv);
1443 }
1444
1445 /*
1446  * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1447  * Send the result as a one-line command and get response.
1448  */
1449 void
1450 quote1(initial, argc, argv)
1451         const char *initial;
1452         int argc;
1453         char *argv[];
1454 {
1455         int i, len, len1;
1456         char buf[BUFSIZ];               /* must be >= sizeof(line) */
1457
1458         len = snprintf(buf, sizeof(buf), "%s", initial);
1459         if (len >= 0 && len < sizeof(buf)) {
1460                 for (i = 1; i < argc; i++) {
1461                         len1 = snprintf(&buf[len], sizeof(buf) - len,
1462                             i == 1 ? "%s" : " %s", argv[i]);
1463                         if (len1 < 0 || len1 > sizeof(buf) - len)
1464                                 break;
1465                         len += len1;
1466                 }
1467         }
1468         if (command("%s", buf) == PRELIM) {
1469                 while (getreply(0) == PRELIM)
1470                         continue;
1471         }
1472 }
1473
1474 void
1475 do_chmod(argc, argv)
1476         int argc;
1477         char *argv[];
1478 {
1479
1480         if (argc < 2 && !another(&argc, &argv, "mode"))
1481                 goto usage;
1482         if ((argc < 3 && !another(&argc, &argv, "file-name")) || argc > 3) {
1483 usage:
1484                 printf("usage: %s mode file-name\n", argv[0]);
1485                 code = -1;
1486                 return;
1487         }
1488         (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1489 }
1490
1491 void
1492 do_umask(argc, argv)
1493         int argc;
1494         char *argv[];
1495 {
1496         int oldverbose = verbose;
1497
1498         verbose = 1;
1499         (void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1500         verbose = oldverbose;
1501 }
1502
1503 void
1504 idle(argc, argv)
1505         int argc;
1506         char *argv[];
1507 {
1508         int oldverbose = verbose;
1509
1510         verbose = 1;
1511         (void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1512         verbose = oldverbose;
1513 }
1514
1515 /*
1516  * Ask the other side for help.
1517  */
1518 void
1519 rmthelp(argc, argv)
1520         int argc;
1521         char *argv[];
1522 {
1523         int oldverbose = verbose;
1524
1525         verbose = 1;
1526         (void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1527         verbose = oldverbose;
1528 }
1529
1530 /*
1531  * Terminate session and exit.
1532  */
1533 /*VARARGS*/
1534 void
1535 quit(argc, argv)
1536         int argc;
1537         char *argv[];
1538 {
1539
1540         if (connected)
1541                 disconnect(0, 0);
1542         pswitch(1);
1543         if (connected) {
1544                 disconnect(0, 0);
1545         }
1546         exit(0);
1547 }
1548
1549 /*
1550  * Terminate session, but don't exit.
1551  */
1552 void
1553 disconnect(argc, argv)
1554         int argc;
1555         char *argv[];
1556 {
1557
1558         if (!connected)
1559                 return;
1560         (void)command("QUIT");
1561         if (cout) {
1562                 (void)fclose(cout);
1563         }
1564         cout = NULL;
1565         connected = 0;
1566         data = -1;
1567         if (!proxy) {
1568                 macnum = 0;
1569         }
1570 }
1571
1572 void
1573 account(argc, argv)
1574         int argc;
1575         char *argv[];
1576 {
1577         char *ap;
1578
1579         if (argc > 2) {
1580                 printf("usage: %s [password]\n", argv[0]);
1581                 code = -1;
1582                 return;
1583         }
1584         else if (argc == 2)
1585                 ap = argv[1];
1586         else
1587                 ap = getpass("Account:");
1588         (void)command("ACCT %s", ap);
1589 }
1590
1591 jmp_buf abortprox;
1592
1593 void
1594 proxabort(notused)
1595         int notused;
1596 {
1597
1598         alarmtimer(0);
1599         if (!proxy) {
1600                 pswitch(1);
1601         }
1602         if (connected) {
1603                 proxflag = 1;
1604         }
1605         else {
1606                 proxflag = 0;
1607         }
1608         pswitch(0);
1609         longjmp(abortprox, 1);
1610 }
1611
1612 void
1613 doproxy(argc, argv)
1614         int argc;
1615         char *argv[];
1616 {
1617         struct cmd *c;
1618         int cmdpos;
1619         sig_t oldintr;
1620
1621         if (argc < 2 && !another(&argc, &argv, "command")) {
1622                 printf("usage: %s command\n", argv[0]);
1623                 code = -1;
1624                 return;
1625         }
1626         c = getcmd(argv[1]);
1627         if (c == (struct cmd *) -1) {
1628                 puts("?Ambiguous command.");
1629                 (void)fflush(stdout);
1630                 code = -1;
1631                 return;
1632         }
1633         if (c == 0) {
1634                 puts("?Invalid command.");
1635                 (void)fflush(stdout);
1636                 code = -1;
1637                 return;
1638         }
1639         if (!c->c_proxy) {
1640                 puts("?Invalid proxy command.");
1641                 (void)fflush(stdout);
1642                 code = -1;
1643                 return;
1644         }
1645         if (setjmp(abortprox)) {
1646                 code = -1;
1647                 return;
1648         }
1649         oldintr = signal(SIGINT, proxabort);
1650         pswitch(1);
1651         if (c->c_conn && !connected) {
1652                 puts("Not connected.");
1653                 (void)fflush(stdout);
1654                 pswitch(0);
1655                 (void)signal(SIGINT, oldintr);
1656                 code = -1;
1657                 return;
1658         }
1659         cmdpos = strcspn(line, " \t");
1660         if (cmdpos > 0)         /* remove leading "proxy " from input buffer */
1661                 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1662         (*c->c_handler)(argc-1, argv+1);
1663         if (connected) {
1664                 proxflag = 1;
1665         }
1666         else {
1667                 proxflag = 0;
1668         }
1669         pswitch(0);
1670         (void)signal(SIGINT, oldintr);
1671 }
1672
1673 void
1674 setcase(argc, argv)
1675         int argc;
1676         char *argv[];
1677 {
1678
1679         code = togglevar(argc, argv, &mcase, "Case mapping");
1680 }
1681
1682 void
1683 setcr(argc, argv)
1684         int argc;
1685         char *argv[];
1686 {
1687
1688         code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1689 }
1690
1691 void
1692 setntrans(argc, argv)
1693         int argc;
1694         char *argv[];
1695 {
1696         if (argc == 1) {
1697                 ntflag = 0;
1698                 puts("Ntrans off.");
1699                 code = ntflag;
1700                 return;
1701         }
1702         ntflag++;
1703         code = ntflag;
1704         (void)strncpy(ntin, argv[1], sizeof(ntin) - 1);
1705         ntin[sizeof(ntin) - 1] = '\0';
1706         if (argc == 2) {
1707                 ntout[0] = '\0';
1708                 return;
1709         }
1710         (void)strncpy(ntout, argv[2], sizeof(ntout) - 1);
1711         ntout[sizeof(ntout) - 1] = '\0';
1712 }
1713
1714 char *
1715 dotrans(name)
1716         char *name;
1717 {
1718         static char new[MAXPATHLEN];
1719         char *cp1, *cp2 = new;
1720         int i, ostop, found;
1721
1722         for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1723                 continue;
1724         for (cp1 = name; *cp1; cp1++) {
1725                 found = 0;
1726                 for (i = 0; *(ntin + i) && i < 16; i++) {
1727                         if (*cp1 == *(ntin + i)) {
1728                                 found++;
1729                                 if (i < ostop) {
1730                                         *cp2++ = *(ntout + i);
1731                                 }
1732                                 break;
1733                         }
1734                 }
1735                 if (!found) {
1736                         *cp2++ = *cp1;
1737                 }
1738         }
1739         *cp2 = '\0';
1740         return (new);
1741 }
1742
1743 void
1744 setnmap(argc, argv)
1745         int argc;
1746         char *argv[];
1747 {
1748         char *cp;
1749
1750         if (argc == 1) {
1751                 mapflag = 0;
1752                 puts("Nmap off.");
1753                 code = mapflag;
1754                 return;
1755         }
1756         if ((argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1757                 printf("usage: %s [mapin mapout]\n", argv[0]);
1758                 code = -1;
1759                 return;
1760         }
1761         mapflag = 1;
1762         code = 1;
1763         cp = strchr(altarg, ' ');
1764         if (proxy) {
1765                 while(*++cp == ' ')
1766                         continue;
1767                 altarg = cp;
1768                 cp = strchr(altarg, ' ');
1769         }
1770         *cp = '\0';
1771         (void)strncpy(mapin, altarg, MAXPATHLEN - 1);
1772         while (*++cp == ' ')
1773                 continue;
1774         (void)strncpy(mapout, cp, MAXPATHLEN - 1);
1775 }
1776
1777 char *
1778 domap(name)
1779         char *name;
1780 {
1781         static char new[MAXPATHLEN];
1782         char *cp1 = name, *cp2 = mapin;
1783         char *tp[9], *te[9];
1784         int i, toks[9], toknum = 0, match = 1;
1785
1786         for (i=0; i < 9; ++i) {
1787                 toks[i] = 0;
1788         }
1789         while (match && *cp1 && *cp2) {
1790                 switch (*cp2) {
1791                         case '\\':
1792                                 if (*++cp2 != *cp1) {
1793                                         match = 0;
1794                                 }
1795                                 break;
1796                         case '$':
1797                                 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1798                                         if (*cp1 != *(++cp2+1)) {
1799                                                 toks[toknum = *cp2 - '1']++;
1800                                                 tp[toknum] = cp1;
1801                                                 while (*++cp1 && *(cp2+1)
1802                                                         != *cp1);
1803                                                 te[toknum] = cp1;
1804                                         }
1805                                         cp2++;
1806                                         break;
1807                                 }
1808                                 /* FALLTHROUGH */
1809                         default:
1810                                 if (*cp2 != *cp1) {
1811                                         match = 0;
1812                                 }
1813                                 break;
1814                 }
1815                 if (match && *cp1) {
1816                         cp1++;
1817                 }
1818                 if (match && *cp2) {
1819                         cp2++;
1820                 }
1821         }
1822         if (!match && *cp1) /* last token mismatch */
1823         {
1824                 toks[toknum] = 0;
1825         }
1826         cp1 = new;
1827         *cp1 = '\0';
1828         cp2 = mapout;
1829         while (*cp2) {
1830                 match = 0;
1831                 switch (*cp2) {
1832                         case '\\':
1833                                 if (*(cp2 + 1)) {
1834                                         *cp1++ = *++cp2;
1835                                 }
1836                                 break;
1837                         case '[':
1838 LOOP:
1839                                 if (*++cp2 == '$' &&
1840                                     isdigit((unsigned char)*(cp2+1))) {
1841                                         if (*++cp2 == '0') {
1842                                                 char *cp3 = name;
1843
1844                                                 while (*cp3) {
1845                                                         *cp1++ = *cp3++;
1846                                                 }
1847                                                 match = 1;
1848                                         }
1849                                         else if (toks[toknum = *cp2 - '1']) {
1850                                                 char *cp3 = tp[toknum];
1851
1852                                                 while (cp3 != te[toknum]) {
1853                                                         *cp1++ = *cp3++;
1854                                                 }
1855                                                 match = 1;
1856                                         }
1857                                 }
1858                                 else {
1859                                         while (*cp2 && *cp2 != ',' &&
1860                                             *cp2 != ']') {
1861                                                 if (*cp2 == '\\') {
1862                                                         cp2++;
1863                                                 }
1864                                                 else if (*cp2 == '$' &&
1865                                                         isdigit((unsigned char)*(cp2+1))) {
1866                                                         if (*++cp2 == '0') {
1867                                                            char *cp3 = name;
1868
1869                                                            while (*cp3) {
1870                                                                 *cp1++ = *cp3++;
1871                                                            }
1872                                                         }
1873                                                         else if (toks[toknum =
1874                                                             *cp2 - '1']) {
1875                                                            char *cp3=tp[toknum];
1876
1877                                                            while (cp3 !=
1878                                                                   te[toknum]) {
1879                                                                 *cp1++ = *cp3++;
1880                                                            }
1881                                                         }
1882                                                 }
1883                                                 else if (*cp2) {
1884                                                         *cp1++ = *cp2++;
1885                                                 }
1886                                         }
1887                                         if (!*cp2) {
1888                                                 puts(
1889 "nmap: unbalanced brackets.");
1890                                                 return (name);
1891                                         }
1892                                         match = 1;
1893                                         cp2--;
1894                                 }
1895                                 if (match) {
1896                                         while (*++cp2 && *cp2 != ']') {
1897                                               if (*cp2 == '\\' && *(cp2 + 1)) {
1898                                                         cp2++;
1899                                               }
1900                                         }
1901                                         if (!*cp2) {
1902                                                 puts(
1903 "nmap: unbalanced brackets.");
1904                                                 return (name);
1905                                         }
1906                                         break;
1907                                 }
1908                                 switch (*++cp2) {
1909                                         case ',':
1910                                                 goto LOOP;
1911                                         case ']':
1912                                                 break;
1913                                         default:
1914                                                 cp2--;
1915                                                 goto LOOP;
1916                                 }
1917                                 break;
1918                         case '$':
1919                                 if (isdigit((unsigned char)*(cp2 + 1))) {
1920                                         if (*++cp2 == '0') {
1921                                                 char *cp3 = name;
1922
1923                                                 while (*cp3) {
1924                                                         *cp1++ = *cp3++;
1925                                                 }
1926                                         }
1927                                         else if (toks[toknum = *cp2 - '1']) {
1928                                                 char *cp3 = tp[toknum];
1929
1930                                                 while (cp3 != te[toknum]) {
1931                                                         *cp1++ = *cp3++;
1932                                                 }
1933                                         }
1934                                         break;
1935                                 }
1936                                 /* intentional drop through */
1937                         default:
1938                                 *cp1++ = *cp2;
1939                                 break;
1940                 }
1941                 cp2++;
1942         }
1943         *cp1 = '\0';
1944         if (!*new) {
1945                 return (name);
1946         }
1947         return (new);
1948 }
1949
1950 void
1951 setpassive(argc, argv)
1952         int argc;
1953         char *argv[];
1954 {
1955
1956         code = togglevar(argc, argv, &passivemode,
1957             verbose ? "Passive mode" : NULL);
1958 }
1959
1960 void
1961 setepsv4(argc, argv)
1962         int argc;
1963         char *argv[];
1964 {
1965         code = togglevar(argc, argv, &epsv4,
1966             verbose ? "Use of EPSV/EPRT on IPv4 ftp" : NULL);
1967         try_epsv = epsv4;
1968 }
1969
1970 void
1971 setsunique(argc, argv)
1972         int argc;
1973         char *argv[];
1974 {
1975
1976         code = togglevar(argc, argv, &sunique, "Store unique");
1977 }
1978
1979 void
1980 setrunique(argc, argv)
1981         int argc;
1982         char *argv[];
1983 {
1984
1985         code = togglevar(argc, argv, &runique, "Receive unique");
1986 }
1987
1988 /* change directory to parent directory */
1989 void
1990 cdup(argc, argv)
1991         int argc;
1992         char *argv[];
1993 {
1994         int r;
1995
1996         r = command("CDUP");
1997         if (r == ERROR && code == 500) {
1998                 if (verbose)
1999                         puts("CDUP command not recognized, trying XCUP.");
2000                 r = command("XCUP");
2001         }
2002         if (r == COMPLETE)
2003                 dirchange = 1;
2004 }
2005
2006 /*
2007  * Restart transfer at specific point
2008  */
2009 void
2010 restart(argc, argv)
2011         int argc;
2012         char *argv[];
2013 {
2014
2015         if (argc > 2) {
2016                 printf("usage: %s [restart_point]\n", argv[0]);
2017                 code = -1;
2018                 return;
2019         }
2020         if (argc == 2) {
2021                 quad_t  rp;
2022                 char *ep;
2023
2024                 rp = strtoq(argv[1], &ep, 10);
2025                 if (rp < 0 || *ep != '\0')
2026                         printf("restart: Invalid offset `%s'\n", argv[1]);
2027                 else
2028                         restart_point = rp;
2029         }
2030         if (restart_point == 0)
2031                 puts("No restart point defined");
2032         else
2033                 printf("Restarting at %qd for next get, put or append\n",
2034                     (long long)restart_point);
2035 }
2036
2037 /* 
2038  * Show remote system type
2039  */
2040 void
2041 syst(argc, argv)
2042         int argc;
2043         char *argv[];
2044 {
2045
2046         (void)command("SYST");
2047 }
2048
2049 void
2050 macdef(argc, argv)
2051         int argc;
2052         char *argv[];
2053 {
2054         char *tmp;
2055         int c;
2056
2057         if (macnum == 16) {
2058                 puts("Limit of 16 macros have already been defined.");
2059                 code = -1;
2060                 return;
2061         }
2062         if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2063                 printf("usage: %s macro_name\n", argv[0]);
2064                 code = -1;
2065                 return;
2066         }
2067         if (interactive)
2068                 puts(
2069 "Enter macro line by line, terminating it with a null line.");
2070         (void)strncpy(macros[macnum].mac_name, argv[1],
2071             sizeof(macros[macnum].mac_name) - 1);
2072         macros[macnum].mac_name[sizeof(macros[macnum].mac_name) - 1] = '\0';
2073         if (macnum == 0)
2074                 macros[macnum].mac_start = macbuf;
2075         else
2076                 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2077         tmp = macros[macnum].mac_start;
2078         while (tmp != macbuf+4096) {
2079                 if ((c = getchar()) == EOF) {
2080                         puts("macdef: end of file encountered.");
2081                         code = -1;
2082                         return;
2083                 }
2084                 if ((*tmp = c) == '\n') {
2085                         if (tmp == macros[macnum].mac_start) {
2086                                 macros[macnum++].mac_end = tmp;
2087                                 code = 0;
2088                                 return;
2089                         }
2090                         if (*(tmp-1) == '\0') {
2091                                 macros[macnum++].mac_end = tmp - 1;
2092                                 code = 0;
2093                                 return;
2094                         }
2095                         *tmp = '\0';
2096                 }
2097                 tmp++;
2098         }
2099         while (1) {
2100                 while ((c = getchar()) != '\n' && c != EOF)
2101                         /* LOOP */;
2102                 if (c == EOF || getchar() == '\n') {
2103                         puts("Macro not defined - 4K buffer exceeded.");
2104                         code = -1;
2105                         return;
2106                 }
2107         }
2108 }
2109
2110 /*
2111  * Restrict FTP data port range to a high group of "safe" ports
2112  */
2113 void
2114 setrestrict(argc, argv)
2115         int argc;
2116         char *argv[];
2117 {
2118         code = togglevar(argc, argv, &restricted_data_ports,
2119                          verbose ? "Restricted data ports" : NULL);
2120 }
2121
2122 /*
2123  * Get size of file on remote machine
2124  */
2125 void
2126 sizecmd(argc, argv)
2127         int argc;
2128         char *argv[];
2129 {
2130         off_t size;
2131
2132         if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2133                 printf("usage: %s filename\n", argv[0]);
2134                 code = -1;
2135                 return;
2136         }
2137         size = remotesize(argv[1], 1);
2138         if (size != -1)
2139                 printf("%s\t%qd\n", argv[1], (long long)size);
2140         code = size;
2141 }
2142
2143 /*
2144  * Get last modification time of file on remote machine
2145  */
2146 void
2147 modtime(argc, argv)
2148         int argc;
2149         char *argv[];
2150 {
2151         time_t mtime;
2152
2153         if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2154                 printf("usage: %s filename\n", argv[0]);
2155                 code = -1;
2156                 return;
2157         }
2158         mtime = remotemodtime(argv[1], 1);
2159         if (mtime != -1)
2160                 printf("%s\t%s", argv[1], asctime(localtime(&mtime)));
2161         code = mtime;
2162 }
2163
2164 /*
2165  * Show status on remote machine
2166  */
2167 void
2168 rmtstatus(argc, argv)
2169         int argc;
2170         char *argv[];
2171 {
2172
2173         (void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
2174 }
2175
2176 /*
2177  * Get file if modtime is more recent than current file
2178  */
2179 void
2180 newer(argc, argv)
2181         int argc;
2182         char *argv[];
2183 {
2184
2185         if (getit(argc, argv, -1, "w"))
2186                 printf("Local file \"%s\" is newer than remote file \"%s\".\n",
2187                         argv[2], argv[1]);
2188 }
2189
2190 /*
2191  * Display one file through $PAGER (defaults to "more").
2192  */
2193 void
2194 page(argc, argv)
2195         int argc;
2196         char *argv[];
2197 {
2198         int ohash, overbose;
2199         char *p, *pager, *oldargv1;
2200
2201         if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2202                 printf("usage: %s filename\n", argv[0]);
2203                 code = -1;
2204                 return;
2205         }
2206         oldargv1 = argv[1];
2207         if (!globulize(&argv[1])) {
2208                 code = -1;
2209                 return;
2210         }
2211         p = getenv("PAGER");
2212         if (p == NULL)
2213                 p = PAGER;
2214         if ((pager = malloc(strlen(p) + 2)) == NULL)
2215                 errx(1, "Can't allocate memory for $PAGER");
2216         (void)sprintf(pager, "|%s", p);
2217
2218         ohash = hash;
2219         overbose = verbose;
2220         hash = verbose = 0;
2221         recvrequest("RETR", pager, argv[1], "r+w", 1, 0);
2222         (void)free(pager);
2223         hash = ohash;
2224         verbose = overbose;
2225         if (oldargv1 != argv[1])        /* free up after globulize() */
2226                 free(argv[1]);
2227 }