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