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