]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/ftp/cmds.c
This commit was generated by cvs2svn to compensate for changes in r56067,
[FreeBSD/FreeBSD.git] / usr.bin / ftp / cmds.c
1 /* $FreeBSD$    */
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$");
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 %d.\n", onoff(gatemode),
661             *gateserver ? gateserver : "(none)", ntohs(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 #ifndef SMALL
688         printf("Command line editing: %s.\n", onoff(editing));
689 #endif /* !SMALL */
690         if (macnum > 0) {
691                 puts("Macros:");
692                 for (i=0; i<macnum; i++) {
693                         printf("\t%s\n", macros[i].mac_name);
694                 }
695         }
696         code = 0;
697 }
698
699 /*
700  * Toggle a variable
701  */
702 int
703 togglevar(argc, argv, var, mesg)
704         int   argc;
705         char *argv[];
706         int  *var;
707         const char *mesg;
708 {
709         if (argc < 2) {
710                 *var = !*var;
711         } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
712                 *var = 1;
713         } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
714                 *var = 0;
715         } else {
716                 printf("usage: %s [ on | off ]\n", argv[0]);
717                 return (-1);
718         }
719         if (mesg)
720                 printf("%s %s.\n", mesg, onoff(*var));
721         return (*var);
722 }
723
724 /*
725  * Set beep on cmd completed mode.
726  */
727 /*VARARGS*/
728 void
729 setbell(argc, argv)
730         int argc;
731         char *argv[];
732 {
733
734         code = togglevar(argc, argv, &bell, "Bell mode");
735 }
736
737 #ifndef SMALL
738 /*
739  * Set command line editing
740  */
741 /*VARARGS*/
742 void
743 setedit(argc, argv)
744         int argc;
745         char *argv[];
746 {
747
748         code = togglevar(argc, argv, &editing, "Editing mode");
749         controlediting();
750 }
751 #endif /* !SMALL */
752
753 /*
754  * Turn on packet tracing.
755  */
756 /*VARARGS*/
757 void
758 settrace(argc, argv)
759         int argc;
760         char *argv[];
761 {
762
763         code = togglevar(argc, argv, &trace, "Packet tracing");
764 }
765
766 /*
767  * Toggle hash mark printing during transfers, or set hash mark bytecount.
768  */
769 /*VARARGS*/
770 void
771 sethash(argc, argv)
772         int argc;
773         char *argv[];
774 {
775         if (argc == 1)
776                 hash = !hash;
777         else if (argc != 2) {
778                 printf("usage: %s [ on | off | bytecount ]\n", argv[0]);
779                 code = -1;
780                 return;
781         } else if (strcasecmp(argv[1], "on") == 0)
782                 hash = 1;
783         else if (strcasecmp(argv[1], "off") == 0)
784                 hash = 0;
785         else {
786                 int nmark;
787                 char *ep;
788
789                 nmark = strtol(argv[1], &ep, 10);
790                 if (nmark < 1 || *ep != '\0') {
791                         printf("mark: bad bytecount value `%s'.\n", argv[1]);
792                         code = -1;
793                         return;
794                 }
795                 mark = nmark;
796                 hash = 1;
797         }
798         printf("Hash mark printing %s", onoff(hash));
799         if (hash)
800                 printf(" (%d bytes/hash mark)", mark);
801         puts(".");
802         code = hash;
803 }
804
805 /*
806  * Turn on printing of server echo's.
807  */
808 /*VARARGS*/
809 void
810 setverbose(argc, argv)
811         int argc;
812         char *argv[];
813 {
814
815         code = togglevar(argc, argv, &verbose, "Verbose mode");
816 }
817
818 /*
819  * Toggle PORT cmd use before each data connection.
820  */
821 /*VARARGS*/
822 void
823 setport(argc, argv)
824         int argc;
825         char *argv[];
826 {
827
828         code = togglevar(argc, argv, &sendport, "Use of PORT cmds");
829 }
830
831 /*
832  * Toggle transfer progress bar.
833  */
834 /*VARARGS*/
835 void
836 setprogress(argc, argv)
837         int argc;
838         char *argv[];
839 {
840
841         code = togglevar(argc, argv, &progress, "Progress bar");
842 }
843
844 /*
845  * Turn on interactive prompting during mget, mput, and mdelete.
846  */
847 /*VARARGS*/
848 void
849 setprompt(argc, argv)
850         int argc;
851         char *argv[];
852 {
853
854         code = togglevar(argc, argv, &interactive, "Interactive mode");
855 }
856
857 /*
858  * Toggle gate-ftp mode, or set gate-ftp server
859  */
860 /*VARARGS*/
861 void
862 setgate(argc, argv)
863         int argc;
864         char *argv[];
865 {
866         static char gsbuf[MAXHOSTNAMELEN];
867
868         if (argc > 3) {
869                 printf("usage: %s [ on | off | gateserver [ port ] ]\n",
870                     argv[0]);
871                 code = -1;
872                 return;
873         } else if (argc < 2) {
874                 gatemode = !gatemode;
875         } else {
876                 if (argc == 2 && strcasecmp(argv[1], "on") == 0)
877                         gatemode = 1;
878                 else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
879                         gatemode = 0;
880                 else {
881                         if (argc == 3) {
882                                 char *ep;
883                                 long port;
884
885                                 port = strtol(argv[2], &ep, 10);
886                                 if (port < 0 || port > 0xffff || *ep != '\0') {
887                                         printf("%s: bad gateport value.\n",
888                                             argv[2]);
889                                         code = -1;
890                                         return;
891                                 }
892                                 gateport = htons(port);
893                         }
894                         strncpy(gsbuf, argv[1], sizeof(gsbuf) - 1);
895                         gsbuf[sizeof(gsbuf) - 1] = '\0';
896                         gateserver = gsbuf;
897                         gatemode = 1;
898                 }
899         }
900         if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
901                 printf(
902                     "Disabling gate-ftp mode - no gate-ftp server defined.\n");
903                 gatemode = 0;
904         } else {
905                 printf("Gate ftp: %s, server %s, port %d.\n", onoff(gatemode),
906                     *gateserver ? gateserver : "(none)", ntohs(gateport));
907         }
908         code = gatemode;
909 }
910
911 /*
912  * Toggle metacharacter interpretation on local file names.
913  */
914 /*VARARGS*/
915 void
916 setglob(argc, argv)
917         int argc;
918         char *argv[];
919 {
920
921         code = togglevar(argc, argv, &doglob, "Globbing");
922 }
923
924 /*
925  * Toggle preserving modification times on retrieved files.
926  */
927 /*VARARGS*/
928 void
929 setpreserve(argc, argv)
930         int argc;
931         char *argv[];
932 {
933
934         code = togglevar(argc, argv, &preserve, "Preserve modification times");
935 }
936
937 /*
938  * Set debugging mode on/off and/or set level of debugging.
939  */
940 /*VARARGS*/
941 void
942 setdebug(argc, argv)
943         int argc;
944         char *argv[];
945 {
946         if (argc > 2) {
947                 printf("usage: %s [ on | off | debuglevel ]\n", argv[0]);
948                 code = -1;
949                 return;
950         } else if (argc == 2) {
951                 if (strcasecmp(argv[1], "on") == 0)
952                         debug = 1;
953                 else if (strcasecmp(argv[1], "off") == 0)
954                         debug = 0;
955                 else {
956                         char *ep;
957                         long val;
958
959                         val = strtol(argv[1], &ep, 10);
960                         if (val < 0 || val > INT_MAX || *ep != '\0') {
961                                 printf("%s: bad debugging value.\n", argv[1]);
962                                 code = -1;
963                                 return;
964                         }
965                         debug = (int)val;
966                 }
967         } else
968                 debug = !debug;
969         if (debug)
970                 options |= SO_DEBUG;
971         else
972                 options &= ~SO_DEBUG;
973         printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
974         code = debug > 0;
975 }
976
977 /*
978  * Set current working directory on remote machine.
979  */
980 void
981 cd(argc, argv)
982         int argc;
983         char *argv[];
984 {
985         int r;
986
987         if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
988             argc > 2) {
989                 printf("usage: %s remote-directory\n", argv[0]);
990                 code = -1;
991                 return;
992         }
993         r = command("CWD %s", argv[1]);
994         if (r == ERROR && code == 500) {
995                 if (verbose)
996                         puts("CWD command not recognized, trying XCWD.");
997                 r = command("XCWD %s", argv[1]);
998         }
999         if (r == COMPLETE)
1000                 dirchange = 1;
1001 }
1002
1003 /*
1004  * Set current working directory on local machine.
1005  */
1006 void
1007 lcd(argc, argv)
1008         int argc;
1009         char *argv[];
1010 {
1011         char buf[MAXPATHLEN];
1012         char *oldargv1;
1013
1014         if (argc < 2)
1015                 argc++, argv[1] = home;
1016         if (argc != 2) {
1017                 printf("usage: %s local-directory\n", argv[0]);
1018                 code = -1;
1019                 return;
1020         }
1021         oldargv1 = argv[1];
1022         if (!globulize(&argv[1])) {
1023                 code = -1;
1024                 return;
1025         }
1026         if (chdir(argv[1]) < 0) {
1027                 warn("local: %s", argv[1]);
1028                 code = -1;
1029         } else {
1030                 if (getcwd(buf, sizeof(buf)) != NULL)
1031                         printf("Local directory now %s\n", buf);
1032                 else
1033                         warn("getcwd: %s", argv[1]);
1034                 code = 0;
1035         }
1036         if (oldargv1 != argv[1])        /* free up after globulize() */
1037                 free(argv[1]);
1038 }
1039
1040 /*
1041  * Delete a single file.
1042  */
1043 void
1044 delete(argc, argv)
1045         int argc;
1046         char *argv[];
1047 {
1048
1049         if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) {
1050                 printf("usage: %s remote-file\n", argv[0]);
1051                 code = -1;
1052                 return;
1053         }
1054         (void)command("DELE %s", argv[1]);
1055 }
1056
1057 /*
1058  * Delete multiple files.
1059  */
1060 void
1061 mdelete(argc, argv)
1062         int argc;
1063         char *argv[];
1064 {
1065         sig_t oldintr;
1066         int ointer;
1067         char *cp;
1068
1069         if (argc < 2 && !another(&argc, &argv, "remote-files")) {
1070                 printf("usage: %s remote-files\n", argv[0]);
1071                 code = -1;
1072                 return;
1073         }
1074         mname = argv[0];
1075         mflag = 1;
1076         oldintr = signal(SIGINT, mabort);
1077         (void)setjmp(jabort);
1078         while ((cp = remglob(argv, 0, NULL)) != NULL) {
1079                 if (*cp == '\0') {
1080                         mflag = 0;
1081                         continue;
1082                 }
1083                 if (mflag && confirm(argv[0], cp)) {
1084                         (void)command("DELE %s", cp);
1085                         if (!mflag && fromatty) {
1086                                 ointer = interactive;
1087                                 interactive = 1;
1088                                 if (confirm("Continue with", "mdelete")) {
1089                                         mflag++;
1090                                 }
1091                                 interactive = ointer;
1092                         }
1093                 }
1094         }
1095         (void)signal(SIGINT, oldintr);
1096         mflag = 0;
1097 }
1098
1099 /*
1100  * Rename a remote file.
1101  */
1102 void
1103 renamefile(argc, argv)
1104         int argc;
1105         char *argv[];
1106 {
1107
1108         if (argc < 2 && !another(&argc, &argv, "from-name"))
1109                 goto usage;
1110         if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1111 usage:
1112                 printf("usage: %s from-name to-name\n", argv[0]);
1113                 code = -1;
1114                 return;
1115         }
1116         if (command("RNFR %s", argv[1]) == CONTINUE)
1117                 (void)command("RNTO %s", argv[2]);
1118 }
1119
1120 /*
1121  * Get a directory listing of remote files.
1122  */
1123 void
1124 ls(argc, argv)
1125         int argc;
1126         char *argv[];
1127 {
1128         const char *cmd;
1129         char *oldargv2, *globargv2;
1130
1131         if (argc < 2)
1132                 argc++, argv[1] = NULL;
1133         if (argc < 3)
1134                 argc++, argv[2] = "-";
1135         if (argc > 3) {
1136                 printf("usage: %s remote-directory local-file\n", argv[0]);
1137                 code = -1;
1138                 return;
1139         }
1140         cmd = strcmp(argv[0], "nlist") == 0 ? "NLST" : "LIST";
1141         oldargv2 = argv[2];
1142         if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
1143                 code = -1;
1144                 return;
1145         }
1146         globargv2 = argv[2];
1147         if (strcmp(argv[2], "-") && *argv[2] != '|')
1148                 if (!globulize(&argv[2]) || !confirm("output to local-file:",
1149                     argv[2])) {
1150                         code = -1;
1151                         goto freels;
1152         }
1153         recvrequest(cmd, argv[2], argv[1], "w", 0, 0);
1154
1155         /* flush results in case commands are coming from a pipe */
1156         fflush(stdout);
1157 freels:
1158         if (argv[2] != globargv2)               /* free up after globulize() */
1159                 free(argv[2]);
1160         if (globargv2 != oldargv2)
1161                 free(globargv2);
1162 }
1163
1164 /*
1165  * Get a directory listing of multiple remote files.
1166  */
1167 void
1168 mls(argc, argv)
1169         int argc;
1170         char *argv[];
1171 {
1172         sig_t oldintr;
1173         int ointer, i;
1174         int dolist;
1175         char mode[1], *dest, *odest;
1176
1177         if (argc < 2 && !another(&argc, &argv, "remote-files"))
1178                 goto usage;
1179         if (argc < 3 && !another(&argc, &argv, "local-file")) {
1180 usage:
1181                 printf("usage: %s remote-files local-file\n", argv[0]);
1182                 code = -1;
1183                 return;
1184         }
1185         odest = dest = argv[argc - 1];
1186         argv[argc - 1] = NULL;
1187         if (strcmp(dest, "-") && *dest != '|')
1188                 if (!globulize(&dest) ||
1189                     !confirm("output to local-file:", dest)) {
1190                         code = -1;
1191                         return;
1192         }
1193         dolist = strcmp(argv[0], "mls");
1194         mname = argv[0];
1195         mflag = 1;
1196         oldintr = signal(SIGINT, mabort);
1197         (void)setjmp(jabort);
1198         for (i = 1; mflag && i < argc-1; ++i) {
1199                 *mode = (i == 1) ? 'w' : 'a';
1200                 recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode,
1201                     0, 0);
1202                 if (!mflag && fromatty) {
1203                         ointer = interactive;
1204                         interactive = 1;
1205                         if (confirm("Continue with", argv[0])) {
1206                                 mflag ++;
1207                         }
1208                         interactive = ointer;
1209                 }
1210         }
1211         (void)signal(SIGINT, oldintr);
1212         mflag = 0;
1213         if (dest != odest)                      /* free up after globulize() */
1214                 free(dest);
1215 }
1216
1217 /*
1218  * Do a shell escape
1219  */
1220 /*ARGSUSED*/
1221 void
1222 shell(argc, argv)
1223         int argc;
1224         char *argv[];
1225 {
1226         pid_t pid;
1227         sig_t old1, old2;
1228         char shellnam[MAXPATHLEN], *shell, *namep;
1229         int wait_status;
1230
1231         old1 = signal (SIGINT, SIG_IGN);
1232         old2 = signal (SIGQUIT, SIG_IGN);
1233         if ((pid = fork()) == 0) {
1234                 for (pid = 3; pid < 20; pid++)
1235                         (void)close(pid);
1236                 (void)signal(SIGINT, SIG_DFL);
1237                 (void)signal(SIGQUIT, SIG_DFL);
1238                 shell = getenv("SHELL");
1239                 if (shell == NULL)
1240                         shell = _PATH_BSHELL;
1241                 namep = strrchr(shell, '/');
1242                 if (namep == NULL)
1243                         namep = shell;
1244                 shellnam[0] = '-';
1245                 (void)strncpy(shellnam + 1, ++namep, sizeof(shellnam) - 2);
1246                 shellnam[sizeof(shellnam) - 1] = '\0';
1247                 if (strcmp(namep, "sh") != 0)
1248                         shellnam[0] = '+';
1249                 if (debug) {
1250                         puts(shell);
1251                         (void)fflush(stdout);
1252                 }
1253                 if (argc > 1) {
1254                         execl(shell, shellnam, "-c", altarg, (char *)0);
1255                 }
1256                 else {
1257                         execl(shell, shellnam, (char *)0);
1258                 }
1259                 warn("%s", shell);
1260                 code = -1;
1261                 exit(1);
1262         }
1263         if (pid > 0)
1264                 while (wait(&wait_status) != pid)
1265                         ;
1266         (void)signal(SIGINT, old1);
1267         (void)signal(SIGQUIT, old2);
1268         if (pid == -1) {
1269                 warn("Try again later");
1270                 code = -1;
1271         }
1272         else {
1273                 code = 0;
1274         }
1275 }
1276
1277 /*
1278  * Send new user information (re-login)
1279  */
1280 void
1281 user(argc, argv)
1282         int argc;
1283         char *argv[];
1284 {
1285         char acct[80];
1286         int n, aflag = 0;
1287
1288         if (argc < 2)
1289                 (void)another(&argc, &argv, "username");
1290         if (argc < 2 || argc > 4) {
1291                 printf("usage: %s username [password] [account]\n", argv[0]);
1292                 code = -1;
1293                 return;
1294         }
1295         n = command("USER %s", argv[1]);
1296         if (n == CONTINUE) {
1297                 if (argc < 3 )
1298                         argv[2] = getpass("Password: "), argc++;
1299                 n = command("PASS %s", argv[2]);
1300         }
1301         if (n == CONTINUE) {
1302                 if (argc < 4) {
1303                         (void)fputs("Account: ", stdout);
1304                         (void)fflush(stdout);
1305                         (void)fgets(acct, sizeof(acct) - 1, stdin);
1306                         acct[strlen(acct) - 1] = '\0';
1307                         argv[3] = acct; argc++;
1308                 }
1309                 n = command("ACCT %s", argv[3]);
1310                 aflag++;
1311         }
1312         if (n != COMPLETE) {
1313                 puts("Login failed.");
1314                 return;
1315         }
1316         if (!aflag && argc == 4) {
1317                 (void)command("ACCT %s", argv[3]);
1318         }
1319         connected = -1;
1320 }
1321
1322 /*
1323  * Print working directory on remote machine.
1324  */
1325 /*VARARGS*/
1326 void
1327 pwd(argc, argv)
1328         int argc;
1329         char *argv[];
1330 {
1331         int oldverbose = verbose;
1332
1333         /*
1334          * If we aren't verbose, this doesn't do anything!
1335          */
1336         verbose = 1;
1337         if (command("PWD") == ERROR && code == 500) {
1338                 puts("PWD command not recognized, trying XPWD.");
1339                 (void)command("XPWD");
1340         }
1341         verbose = oldverbose;
1342 }
1343
1344 /*
1345  * Print working directory on local machine.
1346  */
1347 void
1348 lpwd(argc, argv)
1349         int argc;
1350         char *argv[];
1351 {
1352         char buf[MAXPATHLEN];
1353
1354         if (getcwd(buf, sizeof(buf)) != NULL)
1355                 printf("Local directory %s\n", buf);
1356         else
1357                 warn("getcwd");
1358         code = 0;
1359 }
1360
1361 /*
1362  * Make a directory.
1363  */
1364 void
1365 makedir(argc, argv)
1366         int argc;
1367         char *argv[];
1368 {
1369
1370         if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1371             argc > 2) {
1372                 printf("usage: %s directory-name\n", argv[0]);
1373                 code = -1;
1374                 return;
1375         }
1376         if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1377                 if (verbose)
1378                         puts("MKD command not recognized, trying XMKD.");
1379                 (void)command("XMKD %s", argv[1]);
1380         }
1381 }
1382
1383 /*
1384  * Remove a directory.
1385  */
1386 void
1387 removedir(argc, argv)
1388         int argc;
1389         char *argv[];
1390 {
1391
1392         if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1393             argc > 2) {
1394                 printf("usage: %s directory-name\n", argv[0]);
1395                 code = -1;
1396                 return;
1397         }
1398         if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1399                 if (verbose)
1400                         puts("RMD command not recognized, trying XRMD.");
1401                 (void)command("XRMD %s", argv[1]);
1402         }
1403 }
1404
1405 /*
1406  * Send a line, verbatim, to the remote machine.
1407  */
1408 void
1409 quote(argc, argv)
1410         int argc;
1411         char *argv[];
1412 {
1413
1414         if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1415                 printf("usage: %s line-to-send\n", argv[0]);
1416                 code = -1;
1417                 return;
1418         }
1419         quote1("", argc, argv);
1420 }
1421
1422 /*
1423  * Send a SITE command to the remote machine.  The line
1424  * is sent verbatim to the remote machine, except that the
1425  * word "SITE" is added at the front.
1426  */
1427 void
1428 site(argc, argv)
1429         int argc;
1430         char *argv[];
1431 {
1432
1433         if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1434                 printf("usage: %s line-to-send\n", argv[0]);
1435                 code = -1;
1436                 return;
1437         }
1438         quote1("SITE ", argc, argv);
1439 }
1440
1441 /*
1442  * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1443  * Send the result as a one-line command and get response.
1444  */
1445 void
1446 quote1(initial, argc, argv)
1447         const char *initial;
1448         int argc;
1449         char *argv[];
1450 {
1451         int i, len, len1;
1452         char buf[BUFSIZ];               /* must be >= sizeof(line) */
1453
1454         len = snprintf(buf, sizeof(buf), "%s", initial);
1455         if (len >= 0 && len < sizeof(buf)) {
1456                 for (i = 1; i < argc; i++) {
1457                         len1 = snprintf(&buf[len], sizeof(buf) - len,
1458                             i == 1 ? "%s" : " %s", argv[i]);
1459                         if (len1 < 0 || len1 > sizeof(buf) - len)
1460                                 break;
1461                         len += len1;
1462                 }
1463         }
1464         if (command(buf) == PRELIM) {
1465                 while (getreply(0) == PRELIM)
1466                         continue;
1467         }
1468 }
1469
1470 void
1471 do_chmod(argc, argv)
1472         int argc;
1473         char *argv[];
1474 {
1475
1476         if (argc < 2 && !another(&argc, &argv, "mode"))
1477                 goto usage;
1478         if ((argc < 3 && !another(&argc, &argv, "file-name")) || argc > 3) {
1479 usage:
1480                 printf("usage: %s mode file-name\n", argv[0]);
1481                 code = -1;
1482                 return;
1483         }
1484         (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1485 }
1486
1487 void
1488 do_umask(argc, argv)
1489         int argc;
1490         char *argv[];
1491 {
1492         int oldverbose = verbose;
1493
1494         verbose = 1;
1495         (void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1496         verbose = oldverbose;
1497 }
1498
1499 void
1500 idle(argc, argv)
1501         int argc;
1502         char *argv[];
1503 {
1504         int oldverbose = verbose;
1505
1506         verbose = 1;
1507         (void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1508         verbose = oldverbose;
1509 }
1510
1511 /*
1512  * Ask the other side for help.
1513  */
1514 void
1515 rmthelp(argc, argv)
1516         int argc;
1517         char *argv[];
1518 {
1519         int oldverbose = verbose;
1520
1521         verbose = 1;
1522         (void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1523         verbose = oldverbose;
1524 }
1525
1526 /*
1527  * Terminate session and exit.
1528  */
1529 /*VARARGS*/
1530 void
1531 quit(argc, argv)
1532         int argc;
1533         char *argv[];
1534 {
1535
1536         if (connected)
1537                 disconnect(0, 0);
1538         pswitch(1);
1539         if (connected) {
1540                 disconnect(0, 0);
1541         }
1542         exit(0);
1543 }
1544
1545 /*
1546  * Terminate session, but don't exit.
1547  */
1548 void
1549 disconnect(argc, argv)
1550         int argc;
1551         char *argv[];
1552 {
1553
1554         if (!connected)
1555                 return;
1556         (void)command("QUIT");
1557         if (cout) {
1558                 (void)fclose(cout);
1559         }
1560         cout = NULL;
1561         connected = 0;
1562         data = -1;
1563         if (!proxy) {
1564                 macnum = 0;
1565         }
1566 }
1567
1568 void
1569 account(argc, argv)
1570         int argc;
1571         char *argv[];
1572 {
1573         char *ap;
1574
1575         if (argc > 2) {
1576                 printf("usage: %s [password]\n", argv[0]);
1577                 code = -1;
1578                 return;
1579         }
1580         else if (argc == 2)
1581                 ap = argv[1];
1582         else
1583                 ap = getpass("Account:");
1584         (void)command("ACCT %s", ap);
1585 }
1586
1587 jmp_buf abortprox;
1588
1589 void
1590 proxabort(notused)
1591         int notused;
1592 {
1593
1594         alarmtimer(0);
1595         if (!proxy) {
1596                 pswitch(1);
1597         }
1598         if (connected) {
1599                 proxflag = 1;
1600         }
1601         else {
1602                 proxflag = 0;
1603         }
1604         pswitch(0);
1605         longjmp(abortprox, 1);
1606 }
1607
1608 void
1609 doproxy(argc, argv)
1610         int argc;
1611         char *argv[];
1612 {
1613         struct cmd *c;
1614         int cmdpos;
1615         sig_t oldintr;
1616
1617         if (argc < 2 && !another(&argc, &argv, "command")) {
1618                 printf("usage: %s command\n", argv[0]);
1619                 code = -1;
1620                 return;
1621         }
1622         c = getcmd(argv[1]);
1623         if (c == (struct cmd *) -1) {
1624                 puts("?Ambiguous command.");
1625                 (void)fflush(stdout);
1626                 code = -1;
1627                 return;
1628         }
1629         if (c == 0) {
1630                 puts("?Invalid command.");
1631                 (void)fflush(stdout);
1632                 code = -1;
1633                 return;
1634         }
1635         if (!c->c_proxy) {
1636                 puts("?Invalid proxy command.");
1637                 (void)fflush(stdout);
1638                 code = -1;
1639                 return;
1640         }
1641         if (setjmp(abortprox)) {
1642                 code = -1;
1643                 return;
1644         }
1645         oldintr = signal(SIGINT, proxabort);
1646         pswitch(1);
1647         if (c->c_conn && !connected) {
1648                 puts("Not connected.");
1649                 (void)fflush(stdout);
1650                 pswitch(0);
1651                 (void)signal(SIGINT, oldintr);
1652                 code = -1;
1653                 return;
1654         }
1655         cmdpos = strcspn(line, " \t");
1656         if (cmdpos > 0)         /* remove leading "proxy " from input buffer */
1657                 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1658         (*c->c_handler)(argc-1, argv+1);
1659         if (connected) {
1660                 proxflag = 1;
1661         }
1662         else {
1663                 proxflag = 0;
1664         }
1665         pswitch(0);
1666         (void)signal(SIGINT, oldintr);
1667 }
1668
1669 void
1670 setcase(argc, argv)
1671         int argc;
1672         char *argv[];
1673 {
1674
1675         code = togglevar(argc, argv, &mcase, "Case mapping");
1676 }
1677
1678 void
1679 setcr(argc, argv)
1680         int argc;
1681         char *argv[];
1682 {
1683
1684         code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1685 }
1686
1687 void
1688 setntrans(argc, argv)
1689         int argc;
1690         char *argv[];
1691 {
1692         if (argc == 1) {
1693                 ntflag = 0;
1694                 puts("Ntrans off.");
1695                 code = ntflag;
1696                 return;
1697         }
1698         ntflag++;
1699         code = ntflag;
1700         (void)strncpy(ntin, argv[1], sizeof(ntin) - 1);
1701         ntin[sizeof(ntin) - 1] = '\0';
1702         if (argc == 2) {
1703                 ntout[0] = '\0';
1704                 return;
1705         }
1706         (void)strncpy(ntout, argv[2], sizeof(ntout) - 1);
1707         ntout[sizeof(ntout) - 1] = '\0';
1708 }
1709
1710 char *
1711 dotrans(name)
1712         char *name;
1713 {
1714         static char new[MAXPATHLEN];
1715         char *cp1, *cp2 = new;
1716         int i, ostop, found;
1717
1718         for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1719                 continue;
1720         for (cp1 = name; *cp1; cp1++) {
1721                 found = 0;
1722                 for (i = 0; *(ntin + i) && i < 16; i++) {
1723                         if (*cp1 == *(ntin + i)) {
1724                                 found++;
1725                                 if (i < ostop) {
1726                                         *cp2++ = *(ntout + i);
1727                                 }
1728                                 break;
1729                         }
1730                 }
1731                 if (!found) {
1732                         *cp2++ = *cp1;
1733                 }
1734         }
1735         *cp2 = '\0';
1736         return (new);
1737 }
1738
1739 void
1740 setnmap(argc, argv)
1741         int argc;
1742         char *argv[];
1743 {
1744         char *cp;
1745
1746         if (argc == 1) {
1747                 mapflag = 0;
1748                 puts("Nmap off.");
1749                 code = mapflag;
1750                 return;
1751         }
1752         if ((argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1753                 printf("usage: %s [mapin mapout]\n", argv[0]);
1754                 code = -1;
1755                 return;
1756         }
1757         mapflag = 1;
1758         code = 1;
1759         cp = strchr(altarg, ' ');
1760         if (proxy) {
1761                 while(*++cp == ' ')
1762                         continue;
1763                 altarg = cp;
1764                 cp = strchr(altarg, ' ');
1765         }
1766         *cp = '\0';
1767         (void)strncpy(mapin, altarg, MAXPATHLEN - 1);
1768         while (*++cp == ' ')
1769                 continue;
1770         (void)strncpy(mapout, cp, MAXPATHLEN - 1);
1771 }
1772
1773 char *
1774 domap(name)
1775         char *name;
1776 {
1777         static char new[MAXPATHLEN];
1778         char *cp1 = name, *cp2 = mapin;
1779         char *tp[9], *te[9];
1780         int i, toks[9], toknum = 0, match = 1;
1781
1782         for (i=0; i < 9; ++i) {
1783                 toks[i] = 0;
1784         }
1785         while (match && *cp1 && *cp2) {
1786                 switch (*cp2) {
1787                         case '\\':
1788                                 if (*++cp2 != *cp1) {
1789                                         match = 0;
1790                                 }
1791                                 break;
1792                         case '$':
1793                                 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1794                                         if (*cp1 != *(++cp2+1)) {
1795                                                 toks[toknum = *cp2 - '1']++;
1796                                                 tp[toknum] = cp1;
1797                                                 while (*++cp1 && *(cp2+1)
1798                                                         != *cp1);
1799                                                 te[toknum] = cp1;
1800                                         }
1801                                         cp2++;
1802                                         break;
1803                                 }
1804                                 /* FALLTHROUGH */
1805                         default:
1806                                 if (*cp2 != *cp1) {
1807                                         match = 0;
1808                                 }
1809                                 break;
1810                 }
1811                 if (match && *cp1) {
1812                         cp1++;
1813                 }
1814                 if (match && *cp2) {
1815                         cp2++;
1816                 }
1817         }
1818         if (!match && *cp1) /* last token mismatch */
1819         {
1820                 toks[toknum] = 0;
1821         }
1822         cp1 = new;
1823         *cp1 = '\0';
1824         cp2 = mapout;
1825         while (*cp2) {
1826                 match = 0;
1827                 switch (*cp2) {
1828                         case '\\':
1829                                 if (*(cp2 + 1)) {
1830                                         *cp1++ = *++cp2;
1831                                 }
1832                                 break;
1833                         case '[':
1834 LOOP:
1835                                 if (*++cp2 == '$' &&
1836                                     isdigit((unsigned char)*(cp2+1))) {
1837                                         if (*++cp2 == '0') {
1838                                                 char *cp3 = name;
1839
1840                                                 while (*cp3) {
1841                                                         *cp1++ = *cp3++;
1842                                                 }
1843                                                 match = 1;
1844                                         }
1845                                         else if (toks[toknum = *cp2 - '1']) {
1846                                                 char *cp3 = tp[toknum];
1847
1848                                                 while (cp3 != te[toknum]) {
1849                                                         *cp1++ = *cp3++;
1850                                                 }
1851                                                 match = 1;
1852                                         }
1853                                 }
1854                                 else {
1855                                         while (*cp2 && *cp2 != ',' &&
1856                                             *cp2 != ']') {
1857                                                 if (*cp2 == '\\') {
1858                                                         cp2++;
1859                                                 }
1860                                                 else if (*cp2 == '$' &&
1861                                                         isdigit((unsigned char)*(cp2+1))) {
1862                                                         if (*++cp2 == '0') {
1863                                                            char *cp3 = name;
1864
1865                                                            while (*cp3) {
1866                                                                 *cp1++ = *cp3++;
1867                                                            }
1868                                                         }
1869                                                         else if (toks[toknum =
1870                                                             *cp2 - '1']) {
1871                                                            char *cp3=tp[toknum];
1872
1873                                                            while (cp3 !=
1874                                                                   te[toknum]) {
1875                                                                 *cp1++ = *cp3++;
1876                                                            }
1877                                                         }
1878                                                 }
1879                                                 else if (*cp2) {
1880                                                         *cp1++ = *cp2++;
1881                                                 }
1882                                         }
1883                                         if (!*cp2) {
1884                                                 puts(
1885 "nmap: unbalanced brackets.");
1886                                                 return (name);
1887                                         }
1888                                         match = 1;
1889                                         cp2--;
1890                                 }
1891                                 if (match) {
1892                                         while (*++cp2 && *cp2 != ']') {
1893                                               if (*cp2 == '\\' && *(cp2 + 1)) {
1894                                                         cp2++;
1895                                               }
1896                                         }
1897                                         if (!*cp2) {
1898                                                 puts(
1899 "nmap: unbalanced brackets.");
1900                                                 return (name);
1901                                         }
1902                                         break;
1903                                 }
1904                                 switch (*++cp2) {
1905                                         case ',':
1906                                                 goto LOOP;
1907                                         case ']':
1908                                                 break;
1909                                         default:
1910                                                 cp2--;
1911                                                 goto LOOP;
1912                                 }
1913                                 break;
1914                         case '$':
1915                                 if (isdigit((unsigned char)*(cp2 + 1))) {
1916                                         if (*++cp2 == '0') {
1917                                                 char *cp3 = name;
1918
1919                                                 while (*cp3) {
1920                                                         *cp1++ = *cp3++;
1921                                                 }
1922                                         }
1923                                         else if (toks[toknum = *cp2 - '1']) {
1924                                                 char *cp3 = tp[toknum];
1925
1926                                                 while (cp3 != te[toknum]) {
1927                                                         *cp1++ = *cp3++;
1928                                                 }
1929                                         }
1930                                         break;
1931                                 }
1932                                 /* intentional drop through */
1933                         default:
1934                                 *cp1++ = *cp2;
1935                                 break;
1936                 }
1937                 cp2++;
1938         }
1939         *cp1 = '\0';
1940         if (!*new) {
1941                 return (name);
1942         }
1943         return (new);
1944 }
1945
1946 void
1947 setpassive(argc, argv)
1948         int argc;
1949         char *argv[];
1950 {
1951
1952         code = togglevar(argc, argv, &passivemode,
1953             verbose ? "Passive mode" : NULL);
1954 }
1955
1956 void
1957 setsunique(argc, argv)
1958         int argc;
1959         char *argv[];
1960 {
1961
1962         code = togglevar(argc, argv, &sunique, "Store unique");
1963 }
1964
1965 void
1966 setrunique(argc, argv)
1967         int argc;
1968         char *argv[];
1969 {
1970
1971         code = togglevar(argc, argv, &runique, "Receive unique");
1972 }
1973
1974 /* change directory to parent directory */
1975 void
1976 cdup(argc, argv)
1977         int argc;
1978         char *argv[];
1979 {
1980         int r;
1981
1982         r = command("CDUP");
1983         if (r == ERROR && code == 500) {
1984                 if (verbose)
1985                         puts("CDUP command not recognized, trying XCUP.");
1986                 r = command("XCUP");
1987         }
1988         if (r == COMPLETE)
1989                 dirchange = 1;
1990 }
1991
1992 /*
1993  * Restart transfer at specific point
1994  */
1995 void
1996 restart(argc, argv)
1997         int argc;
1998         char *argv[];
1999 {
2000
2001         if (argc > 2) {
2002                 printf("usage: %s [restart_point]\n", argv[0]);
2003                 code = -1;
2004                 return;
2005         }
2006         if (argc == 2) {
2007                 quad_t  rp;
2008                 char *ep;
2009
2010                 rp = strtoq(argv[1], &ep, 10);
2011                 if (rp < 0 || *ep != '\0')
2012                         printf("restart: Invalid offset `%s'\n", argv[1]);
2013                 else
2014                         restart_point = rp;
2015         }
2016         if (restart_point == 0)
2017                 puts("No restart point defined");
2018         else
2019                 printf("Restarting at %qd for next get, put or append\n",
2020                     (long long)restart_point);
2021 }
2022
2023 /* 
2024  * Show remote system type
2025  */
2026 void
2027 syst(argc, argv)
2028         int argc;
2029         char *argv[];
2030 {
2031
2032         (void)command("SYST");
2033 }
2034
2035 void
2036 macdef(argc, argv)
2037         int argc;
2038         char *argv[];
2039 {
2040         char *tmp;
2041         int c;
2042
2043         if (macnum == 16) {
2044                 puts("Limit of 16 macros have already been defined.");
2045                 code = -1;
2046                 return;
2047         }
2048         if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2049                 printf("usage: %s macro_name\n", argv[0]);
2050                 code = -1;
2051                 return;
2052         }
2053         if (interactive)
2054                 puts(
2055 "Enter macro line by line, terminating it with a null line.");
2056         (void)strncpy(macros[macnum].mac_name, argv[1],
2057             sizeof(macros[macnum].mac_name) - 1);
2058         macros[macnum].mac_name[sizeof(macros[macnum].mac_name) - 1] = '\0';
2059         if (macnum == 0)
2060                 macros[macnum].mac_start = macbuf;
2061         else
2062                 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2063         tmp = macros[macnum].mac_start;
2064         while (tmp != macbuf+4096) {
2065                 if ((c = getchar()) == EOF) {
2066                         puts("macdef: end of file encountered.");
2067                         code = -1;
2068                         return;
2069                 }
2070                 if ((*tmp = c) == '\n') {
2071                         if (tmp == macros[macnum].mac_start) {
2072                                 macros[macnum++].mac_end = tmp;
2073                                 code = 0;
2074                                 return;
2075                         }
2076                         if (*(tmp-1) == '\0') {
2077                                 macros[macnum++].mac_end = tmp - 1;
2078                                 code = 0;
2079                                 return;
2080                         }
2081                         *tmp = '\0';
2082                 }
2083                 tmp++;
2084         }
2085         while (1) {
2086                 while ((c = getchar()) != '\n' && c != EOF)
2087                         /* LOOP */;
2088                 if (c == EOF || getchar() == '\n') {
2089                         puts("Macro not defined - 4K buffer exceeded.");
2090                         code = -1;
2091                         return;
2092                 }
2093         }
2094 }
2095
2096 /*
2097  * Restrict FTP data port range to a high group of "safe" ports
2098  */
2099 void
2100 setrestrict(argc, argv)
2101         int argc;
2102         char *argv[];
2103 {
2104         code = togglevar(argc, argv, &restricted_data_ports,
2105                          verbose ? "Restricted data ports" : NULL);
2106 }
2107
2108 /*
2109  * Get size of file on remote machine
2110  */
2111 void
2112 sizecmd(argc, argv)
2113         int argc;
2114         char *argv[];
2115 {
2116         off_t size;
2117
2118         if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2119                 printf("usage: %s filename\n", argv[0]);
2120                 code = -1;
2121                 return;
2122         }
2123         size = remotesize(argv[1], 1);
2124         if (size != -1)
2125                 printf("%s\t%qd\n", argv[1], (long long)size);
2126         code = size;
2127 }
2128
2129 /*
2130  * Get last modification time of file on remote machine
2131  */
2132 void
2133 modtime(argc, argv)
2134         int argc;
2135         char *argv[];
2136 {
2137         time_t mtime;
2138
2139         if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2140                 printf("usage: %s filename\n", argv[0]);
2141                 code = -1;
2142                 return;
2143         }
2144         mtime = remotemodtime(argv[1], 1);
2145         if (mtime != -1)
2146                 printf("%s\t%s", argv[1], asctime(localtime(&mtime)));
2147         code = mtime;
2148 }
2149
2150 /*
2151  * Show status on remote machine
2152  */
2153 void
2154 rmtstatus(argc, argv)
2155         int argc;
2156         char *argv[];
2157 {
2158
2159         (void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
2160 }
2161
2162 /*
2163  * Get file if modtime is more recent than current file
2164  */
2165 void
2166 newer(argc, argv)
2167         int argc;
2168         char *argv[];
2169 {
2170
2171         if (getit(argc, argv, -1, "w"))
2172                 printf("Local file \"%s\" is newer than remote file \"%s\".\n",
2173                         argv[2], argv[1]);
2174 }
2175
2176 /*
2177  * Display one file through $PAGER (defaults to "more").
2178  */
2179 void
2180 page(argc, argv)
2181         int argc;
2182         char *argv[];
2183 {
2184         int ohash, overbose;
2185         char *p, *pager, *oldargv1;
2186
2187         if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2188                 printf("usage: %s filename\n", argv[0]);
2189                 code = -1;
2190                 return;
2191         }
2192         oldargv1 = argv[1];
2193         if (!globulize(&argv[1])) {
2194                 code = -1;
2195                 return;
2196         }
2197         p = getenv("PAGER");
2198         if (p == NULL)
2199                 p = PAGER;
2200         if ((pager = malloc(strlen(p) + 2)) == NULL)
2201                 errx(1, "Can't allocate memory for $PAGER");
2202         (void)sprintf(pager, "|%s", p);
2203
2204         ohash = hash;
2205         overbose = verbose;
2206         hash = verbose = 0;
2207         recvrequest("RETR", pager, argv[1], "r+w", 1, 0);
2208         (void)free(pager);
2209         hash = ohash;
2210         verbose = overbose;
2211         if (oldargv1 != argv[1])        /* free up after globulize() */
2212                 free(argv[1]);
2213 }