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