cmds.c revision 1.107 1 /* $NetBSD: cmds.c,v 1.107 2004/07/20 11:05:20 lukem Exp $ */
2
3 /*-
4 * Copyright (c) 1996-2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
9 *
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
12 * NASA Ames Research Center.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the NetBSD
25 * Foundation, Inc. and its contributors.
26 * 4. Neither the name of The NetBSD Foundation nor the names of its
27 * contributors may be used to endorse or promote products derived
28 * from this software without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
31 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
32 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
34 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 * POSSIBILITY OF SUCH DAMAGE.
41 */
42
43 /*
44 * Copyright (c) 1985, 1989, 1993, 1994
45 * The Regents of the University of California. All rights reserved.
46 *
47 * Redistribution and use in source and binary forms, with or without
48 * modification, are permitted provided that the following conditions
49 * are met:
50 * 1. Redistributions of source code must retain the above copyright
51 * notice, this list of conditions and the following disclaimer.
52 * 2. Redistributions in binary form must reproduce the above copyright
53 * notice, this list of conditions and the following disclaimer in the
54 * documentation and/or other materials provided with the distribution.
55 * 3. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
70 */
71
72 /*
73 * Copyright (C) 1997 and 1998 WIDE Project.
74 * All rights reserved.
75 *
76 * Redistribution and use in source and binary forms, with or without
77 * modification, are permitted provided that the following conditions
78 * are met:
79 * 1. Redistributions of source code must retain the above copyright
80 * notice, this list of conditions and the following disclaimer.
81 * 2. Redistributions in binary form must reproduce the above copyright
82 * notice, this list of conditions and the following disclaimer in the
83 * documentation and/or other materials provided with the distribution.
84 * 3. Neither the name of the project nor the names of its contributors
85 * may be used to endorse or promote products derived from this software
86 * without specific prior written permission.
87 *
88 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
89 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
90 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
91 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
92 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
93 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
94 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
95 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
96 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
97 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
98 * SUCH DAMAGE.
99 */
100
101 #include <sys/cdefs.h>
102 #ifndef lint
103 #if 0
104 static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94";
105 #else
106 __RCSID("$NetBSD: cmds.c,v 1.107 2004/07/20 11:05:20 lukem Exp $");
107 #endif
108 #endif /* not lint */
109
110 /*
111 * FTP User Program -- Command Routines.
112 */
113 #include <sys/types.h>
114 #include <sys/socket.h>
115 #include <sys/stat.h>
116 #include <sys/wait.h>
117 #include <arpa/ftp.h>
118
119 #include <ctype.h>
120 #include <err.h>
121 #include <glob.h>
122 #include <limits.h>
123 #include <netdb.h>
124 #include <paths.h>
125 #include <stdio.h>
126 #include <stdlib.h>
127 #include <string.h>
128 #include <time.h>
129 #include <unistd.h>
130
131 #include "ftp_var.h"
132 #include "version.h"
133
134 struct types {
135 char *t_name;
136 char *t_mode;
137 int t_type;
138 char *t_arg;
139 } types[] = {
140 { "ascii", "A", TYPE_A, 0 },
141 { "binary", "I", TYPE_I, 0 },
142 { "image", "I", TYPE_I, 0 },
143 { "ebcdic", "E", TYPE_E, 0 },
144 { "tenex", "L", TYPE_L, bytename },
145 { NULL }
146 };
147
148 sigjmp_buf jabort;
149 const char *mname;
150
151 static int confirm(const char *, const char *);
152
153 static int
154 confirm(const char *cmd, const char *file)
155 {
156 char line[BUFSIZ];
157
158 if (!interactive || confirmrest)
159 return (1);
160 while (1) {
161 fprintf(ttyout, "%s %s [anpqy?]? ", cmd, file);
162 (void)fflush(ttyout);
163 if (fgets(line, sizeof(line), stdin) == NULL) {
164 mflag = 0;
165 fprintf(ttyout, "\nEOF received; %s aborted\n", mname);
166 clearerr(stdin);
167 return (0);
168 }
169 switch (tolower(*line)) {
170 case 'a':
171 confirmrest = 1;
172 fprintf(ttyout,
173 "Prompting off for duration of %s.\n", cmd);
174 break;
175 case 'p':
176 interactive = 0;
177 fputs("Interactive mode: off.\n", ttyout);
178 break;
179 case 'q':
180 mflag = 0;
181 fprintf(ttyout, "%s aborted.\n", mname);
182 /* FALLTHROUGH */
183 case 'n':
184 return (0);
185 case '?':
186 fprintf(ttyout,
187 " confirmation options:\n"
188 "\ta answer `yes' for the duration of %s\n"
189 "\tn answer `no' for this file\n"
190 "\tp turn off `prompt' mode\n"
191 "\tq stop the current %s\n"
192 "\ty answer `yes' for this file\n"
193 "\t? this help list\n",
194 cmd, cmd);
195 continue; /* back to while(1) */
196 }
197 return (1);
198 }
199 /* NOTREACHED */
200 }
201
202 /*
203 * Set transfer type.
204 */
205 void
206 settype(int argc, char *argv[])
207 {
208 struct types *p;
209 int comret;
210
211 if (argc == 0 || argc > 2) {
212 char *sep;
213
214 fprintf(ttyout, "usage: %s [", argv[0]);
215 sep = " ";
216 for (p = types; p->t_name; p++) {
217 fprintf(ttyout, "%s%s", sep, p->t_name);
218 sep = " | ";
219 }
220 fputs(" ]\n", ttyout);
221 code = -1;
222 return;
223 }
224 if (argc < 2) {
225 fprintf(ttyout, "Using %s mode to transfer files.\n", typename);
226 code = 0;
227 return;
228 }
229 for (p = types; p->t_name; p++)
230 if (strcmp(argv[1], p->t_name) == 0)
231 break;
232 if (p->t_name == 0) {
233 fprintf(ttyout, "%s: unknown mode.\n", argv[1]);
234 code = -1;
235 return;
236 }
237 if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
238 comret = command("TYPE %s %s", p->t_mode, p->t_arg);
239 else
240 comret = command("TYPE %s", p->t_mode);
241 if (comret == COMPLETE) {
242 (void)strlcpy(typename, p->t_name, sizeof(typename));
243 curtype = type = p->t_type;
244 }
245 }
246
247 /*
248 * Internal form of settype; changes current type in use with server
249 * without changing our notion of the type for data transfers.
250 * Used to change to and from ascii for listings.
251 */
252 void
253 changetype(int newtype, int show)
254 {
255 struct types *p;
256 int comret, oldverbose = verbose;
257
258 if (newtype == 0)
259 newtype = TYPE_I;
260 if (newtype == curtype)
261 return;
262 if (debug == 0 && show == 0)
263 verbose = 0;
264 for (p = types; p->t_name; p++)
265 if (newtype == p->t_type)
266 break;
267 if (p->t_name == 0) {
268 warnx("internal error: unknown type %d.", newtype);
269 return;
270 }
271 if (newtype == TYPE_L && bytename[0] != '\0')
272 comret = command("TYPE %s %s", p->t_mode, bytename);
273 else
274 comret = command("TYPE %s", p->t_mode);
275 if (comret == COMPLETE)
276 curtype = newtype;
277 verbose = oldverbose;
278 }
279
280 char *stype[] = {
281 "type",
282 "",
283 0
284 };
285
286 /*
287 * Set binary transfer type.
288 */
289 /*VARARGS*/
290 void
291 setbinary(int argc, char *argv[])
292 {
293
294 if (argc == 0) {
295 fprintf(ttyout, "usage: %s\n", argv[0]);
296 code = -1;
297 return;
298 }
299 stype[1] = "binary";
300 settype(2, stype);
301 }
302
303 /*
304 * Set ascii transfer type.
305 */
306 /*VARARGS*/
307 void
308 setascii(int argc, char *argv[])
309 {
310
311 if (argc == 0) {
312 fprintf(ttyout, "usage: %s\n", argv[0]);
313 code = -1;
314 return;
315 }
316 stype[1] = "ascii";
317 settype(2, stype);
318 }
319
320 /*
321 * Set tenex transfer type.
322 */
323 /*VARARGS*/
324 void
325 settenex(int argc, char *argv[])
326 {
327
328 if (argc == 0) {
329 fprintf(ttyout, "usage: %s\n", argv[0]);
330 code = -1;
331 return;
332 }
333 stype[1] = "tenex";
334 settype(2, stype);
335 }
336
337 /*
338 * Set file transfer mode.
339 */
340 /*ARGSUSED*/
341 void
342 setftmode(int argc, char *argv[])
343 {
344
345 if (argc != 2) {
346 fprintf(ttyout, "usage: %s mode-name\n", argv[0]);
347 code = -1;
348 return;
349 }
350 fprintf(ttyout, "We only support %s mode, sorry.\n", modename);
351 code = -1;
352 }
353
354 /*
355 * Set file transfer format.
356 */
357 /*ARGSUSED*/
358 void
359 setform(int argc, char *argv[])
360 {
361
362 if (argc != 2) {
363 fprintf(ttyout, "usage: %s format\n", argv[0]);
364 code = -1;
365 return;
366 }
367 fprintf(ttyout, "We only support %s format, sorry.\n", formname);
368 code = -1;
369 }
370
371 /*
372 * Set file transfer structure.
373 */
374 /*ARGSUSED*/
375 void
376 setstruct(int argc, char *argv[])
377 {
378
379 if (argc != 2) {
380 fprintf(ttyout, "usage: %s struct-mode\n", argv[0]);
381 code = -1;
382 return;
383 }
384 fprintf(ttyout, "We only support %s structure, sorry.\n", structname);
385 code = -1;
386 }
387
388 /*
389 * Send a single file.
390 */
391 void
392 put(int argc, char *argv[])
393 {
394 char *cmd;
395 int loc = 0;
396 char *locfile, *remfile;
397
398 if (argc == 2) {
399 argc++;
400 argv[2] = argv[1];
401 loc++;
402 }
403 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-file")))
404 goto usage;
405 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
406 usage:
407 fprintf(ttyout, "usage: %s local-file [remote-file]\n",
408 argv[0]);
409 code = -1;
410 return;
411 }
412 if ((locfile = globulize(argv[1])) == NULL) {
413 code = -1;
414 return;
415 }
416 remfile = argv[2];
417 if (loc) /* If argv[2] is a copy of the old argv[1], update it */
418 remfile = locfile;
419 cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
420 if (loc && ntflag)
421 remfile = dotrans(remfile);
422 if (loc && mapflag)
423 remfile = domap(remfile);
424 sendrequest(cmd, locfile, remfile,
425 locfile != argv[1] || remfile != argv[2]);
426 free(locfile);
427 }
428
429 /*
430 * Send multiple files.
431 */
432 void
433 mput(int argc, char *argv[])
434 {
435 int i;
436 sigfunc oldintr;
437 int ointer;
438 char *tp;
439
440 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-files"))) {
441 fprintf(ttyout, "usage: %s local-files\n", argv[0]);
442 code = -1;
443 return;
444 }
445 mname = argv[0];
446 mflag = 1;
447 oldintr = xsignal(SIGINT, mintr);
448 if (sigsetjmp(jabort, 1))
449 mabort();
450 if (proxy) {
451 char *cp;
452
453 while ((cp = remglob(argv, 0, NULL)) != NULL) {
454 if (*cp == '\0' || !connected) {
455 mflag = 0;
456 continue;
457 }
458 if (mflag && confirm(argv[0], cp)) {
459 tp = cp;
460 if (mcase)
461 tp = docase(tp);
462 if (ntflag)
463 tp = dotrans(tp);
464 if (mapflag)
465 tp = domap(tp);
466 sendrequest((sunique) ? "STOU" : "STOR",
467 cp, tp, cp != tp || !interactive);
468 if (!mflag && fromatty) {
469 ointer = interactive;
470 interactive = 1;
471 if (confirm("Continue with", "mput")) {
472 mflag++;
473 }
474 interactive = ointer;
475 }
476 }
477 }
478 goto cleanupmput;
479 }
480 for (i = 1; i < argc && connected; i++) {
481 char **cpp;
482 glob_t gl;
483 int flags;
484
485 if (!doglob) {
486 if (mflag && confirm(argv[0], argv[i])) {
487 tp = (ntflag) ? dotrans(argv[i]) : argv[i];
488 tp = (mapflag) ? domap(tp) : tp;
489 sendrequest((sunique) ? "STOU" : "STOR",
490 argv[i], tp, tp != argv[i] || !interactive);
491 if (!mflag && fromatty) {
492 ointer = interactive;
493 interactive = 1;
494 if (confirm("Continue with", "mput")) {
495 mflag++;
496 }
497 interactive = ointer;
498 }
499 }
500 continue;
501 }
502
503 memset(&gl, 0, sizeof(gl));
504 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
505 if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
506 warnx("%s: not found", argv[i]);
507 globfree(&gl);
508 continue;
509 }
510 for (cpp = gl.gl_pathv; cpp && *cpp != NULL && connected;
511 cpp++) {
512 if (mflag && confirm(argv[0], *cpp)) {
513 tp = (ntflag) ? dotrans(*cpp) : *cpp;
514 tp = (mapflag) ? domap(tp) : tp;
515 sendrequest((sunique) ? "STOU" : "STOR",
516 *cpp, tp, *cpp != tp || !interactive);
517 if (!mflag && fromatty) {
518 ointer = interactive;
519 interactive = 1;
520 if (confirm("Continue with", "mput")) {
521 mflag++;
522 }
523 interactive = ointer;
524 }
525 }
526 }
527 globfree(&gl);
528 }
529 cleanupmput:
530 (void)xsignal(SIGINT, oldintr);
531 mflag = 0;
532 }
533
534 void
535 reget(int argc, char *argv[])
536 {
537
538 (void)getit(argc, argv, 1, "r+");
539 }
540
541 void
542 get(int argc, char *argv[])
543 {
544
545 (void)getit(argc, argv, 0, restart_point ? "r+" : "w" );
546 }
547
548 /*
549 * Receive one file.
550 * If restartit is 1, restart the xfer always.
551 * If restartit is -1, restart the xfer only if the remote file is newer.
552 */
553 int
554 getit(int argc, char *argv[], int restartit, const char *mode)
555 {
556 int loc, rval;
557 char *remfile, *locfile, *olocfile;
558
559 loc = rval = 0;
560 if (argc == 2) {
561 argc++;
562 argv[2] = argv[1];
563 loc++;
564 }
565 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "remote-file")))
566 goto usage;
567 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
568 usage:
569 fprintf(ttyout, "usage: %s remote-file [local-file]\n",
570 argv[0]);
571 code = -1;
572 return (0);
573 }
574 remfile = argv[1];
575 if ((olocfile = globulize(argv[2])) == NULL) {
576 code = -1;
577 return (0);
578 }
579 locfile = olocfile;
580 if (loc && mcase)
581 locfile = docase(locfile);
582 if (loc && ntflag)
583 locfile = dotrans(locfile);
584 if (loc && mapflag)
585 locfile = domap(locfile);
586 if (restartit) {
587 struct stat stbuf;
588 int ret;
589
590 if (! features[FEAT_REST_STREAM]) {
591 fprintf(ttyout,
592 "Restart is not supported by the remote server.\n");
593 return (0);
594 }
595 ret = stat(locfile, &stbuf);
596 if (restartit == 1) {
597 if (ret < 0) {
598 warn("local: %s", locfile);
599 goto freegetit;
600 }
601 restart_point = stbuf.st_size;
602 } else {
603 if (ret == 0) {
604 time_t mtime;
605
606 mtime = remotemodtime(argv[1], 0);
607 if (mtime == -1)
608 goto freegetit;
609 if (stbuf.st_mtime >= mtime) {
610 rval = 1;
611 goto freegetit;
612 }
613 }
614 }
615 }
616
617 recvrequest("RETR", locfile, remfile, mode,
618 remfile != argv[1] || locfile != argv[2], loc);
619 restart_point = 0;
620 freegetit:
621 (void)free(olocfile);
622 return (rval);
623 }
624
625 /* ARGSUSED */
626 void
627 mintr(int signo)
628 {
629
630 alarmtimer(0);
631 if (fromatty)
632 write(fileno(ttyout), "\n", 1);
633 siglongjmp(jabort, 1);
634 }
635
636 void
637 mabort(void)
638 {
639 int ointer, oconf;
640
641 if (mflag && fromatty) {
642 ointer = interactive;
643 oconf = confirmrest;
644 interactive = 1;
645 confirmrest = 0;
646 if (confirm("Continue with", mname)) {
647 interactive = ointer;
648 confirmrest = oconf;
649 return;
650 }
651 interactive = ointer;
652 confirmrest = oconf;
653 }
654 mflag = 0;
655 }
656
657 /*
658 * Get multiple files.
659 */
660 void
661 mget(int argc, char *argv[])
662 {
663 sigfunc oldintr;
664 int ointer;
665 char *cp, *tp;
666 int restartit;
667
668 if (argc == 0 ||
669 (argc == 1 && !another(&argc, &argv, "remote-files"))) {
670 fprintf(ttyout, "usage: %s remote-files\n", argv[0]);
671 code = -1;
672 return;
673 }
674 mname = argv[0];
675 mflag = 1;
676 restart_point = 0;
677 restartit = 0;
678 if (strcmp(argv[0], "mreget") == 0) {
679 if (! features[FEAT_REST_STREAM]) {
680 fprintf(ttyout,
681 "Restart is not supported by the remote server.\n");
682 return;
683 }
684 restartit = 1;
685 }
686 oldintr = xsignal(SIGINT, mintr);
687 if (sigsetjmp(jabort, 1))
688 mabort();
689 while ((cp = remglob(argv, proxy, NULL)) != NULL) {
690 if (*cp == '\0' || !connected) {
691 mflag = 0;
692 continue;
693 }
694 if (! mflag || !confirm(argv[0], cp))
695 continue;
696 tp = cp;
697 if (mcase)
698 tp = docase(tp);
699 if (ntflag)
700 tp = dotrans(tp);
701 if (mapflag)
702 tp = domap(tp);
703 if (restartit) {
704 struct stat stbuf;
705
706 if (stat(tp, &stbuf) == 0)
707 restart_point = stbuf.st_size;
708 else
709 warn("stat %s", tp);
710 }
711 recvrequest("RETR", tp, cp, restart_point ? "r+" : "w",
712 tp != cp || !interactive, 1);
713 restart_point = 0;
714 if (!mflag && fromatty) {
715 ointer = interactive;
716 interactive = 1;
717 if (confirm("Continue with", "mget"))
718 mflag++;
719 interactive = ointer;
720 }
721 }
722 (void)xsignal(SIGINT, oldintr);
723 mflag = 0;
724 }
725
726 /*
727 * Read list of filenames from a local file and get those
728 */
729 void
730 fget(int argc, char *argv[])
731 {
732 char *buf, *mode;
733 FILE *fp;
734
735 if (argc != 2) {
736 fprintf(ttyout, "usage: %s localfile\n", argv[0]);
737 code = -1;
738 return;
739 }
740
741 fp = fopen(argv[1], "r");
742 if (fp == NULL) {
743 fprintf(ttyout, "Cannot open source file %s\n", argv[1]);
744 code = -1;
745 return;
746 }
747
748 argv[0] = "get";
749 mode = restart_point ? "r+" : "w";
750
751 for (;
752 (buf = fparseln(fp, NULL, NULL, "\0\0\0", 0)) != NULL;
753 free(buf)) {
754 if (buf[0] == '\0')
755 continue;
756 argv[1] = buf;
757 (void)getit(argc, argv, 0, mode);
758 }
759 fclose(fp);
760 }
761
762 char *
763 onoff(int bool)
764 {
765
766 return (bool ? "on" : "off");
767 }
768
769 /*
770 * Show status.
771 */
772 /*ARGSUSED*/
773 void
774 status(int argc, char *argv[])
775 {
776
777 if (argc == 0) {
778 fprintf(ttyout, "usage: %s\n", argv[0]);
779 code = -1;
780 return;
781 }
782 #ifndef NO_STATUS
783 if (connected)
784 fprintf(ttyout, "Connected %sto %s.\n",
785 connected == -1 ? "and logged in" : "", hostname);
786 else
787 fputs("Not connected.\n", ttyout);
788 if (!proxy) {
789 pswitch(1);
790 if (connected) {
791 fprintf(ttyout, "Connected for proxy commands to %s.\n",
792 hostname);
793 }
794 else {
795 fputs("No proxy connection.\n", ttyout);
796 }
797 pswitch(0);
798 }
799 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
800 *gateserver ? gateserver : "(none)", gateport);
801 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
802 onoff(passivemode), onoff(activefallback));
803 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
804 modename, typename, formname, structname);
805 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
806 onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob));
807 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n",
808 onoff(sunique), onoff(runique));
809 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve));
810 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase),
811 onoff(crflag));
812 if (ntflag) {
813 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout);
814 }
815 else {
816 fputs("Ntrans: off.\n", ttyout);
817 }
818 if (mapflag) {
819 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout);
820 }
821 else {
822 fputs("Nmap: off.\n", ttyout);
823 }
824 fprintf(ttyout,
825 "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
826 onoff(hash), mark, onoff(progress));
827 fprintf(ttyout,
828 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
829 onoff(rate_get), rate_get, rate_get_incr);
830 fprintf(ttyout,
831 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
832 onoff(rate_put), rate_put, rate_put_incr);
833 fprintf(ttyout,
834 "Socket buffer sizes: send %d, receive %d.\n",
835 sndbuf_size, rcvbuf_size);
836 fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport));
837 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4),
838 epsv4bad ? " (disabled for this connection)" : "");
839 fprintf(ttyout, "Command line editing: %s.\n",
840 #ifdef NO_EDITCOMPLETE
841 "support not compiled in"
842 #else /* !def NO_EDITCOMPLETE */
843 onoff(editing)
844 #endif /* !def NO_EDITCOMPLETE */
845 );
846 if (macnum > 0) {
847 int i;
848
849 fputs("Macros:\n", ttyout);
850 for (i=0; i<macnum; i++) {
851 fprintf(ttyout, "\t%s\n", macros[i].mac_name);
852 }
853 }
854 #endif /* !def NO_STATUS */
855 fprintf(ttyout, "Version: %s %s\n", FTP_PRODUCT, FTP_VERSION);
856 code = 0;
857 }
858
859 /*
860 * Toggle a variable
861 */
862 int
863 togglevar(int argc, char *argv[], int *var, const char *mesg)
864 {
865 if (argc == 1) {
866 *var = !*var;
867 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
868 *var = 1;
869 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
870 *var = 0;
871 } else {
872 fprintf(ttyout, "usage: %s [ on | off ]\n", argv[0]);
873 return (-1);
874 }
875 if (mesg)
876 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var));
877 return (*var);
878 }
879
880 /*
881 * Set beep on cmd completed mode.
882 */
883 /*VARARGS*/
884 void
885 setbell(int argc, char *argv[])
886 {
887
888 code = togglevar(argc, argv, &bell, "Bell mode");
889 }
890
891 /*
892 * Set command line editing
893 */
894 /*VARARGS*/
895 void
896 setedit(int argc, char *argv[])
897 {
898
899 #ifdef NO_EDITCOMPLETE
900 if (argc == 0) {
901 fprintf(ttyout, "usage: %s\n", argv[0]);
902 code = -1;
903 return;
904 }
905 if (verbose)
906 fputs("Editing support not compiled in; ignoring command.\n",
907 ttyout);
908 #else /* !def NO_EDITCOMPLETE */
909 code = togglevar(argc, argv, &editing, "Editing mode");
910 controlediting();
911 #endif /* !def NO_EDITCOMPLETE */
912 }
913
914 /*
915 * Turn on packet tracing.
916 */
917 /*VARARGS*/
918 void
919 settrace(int argc, char *argv[])
920 {
921
922 code = togglevar(argc, argv, &trace, "Packet tracing");
923 }
924
925 /*
926 * Toggle hash mark printing during transfers, or set hash mark bytecount.
927 */
928 /*VARARGS*/
929 void
930 sethash(int argc, char *argv[])
931 {
932 if (argc == 1)
933 hash = !hash;
934 else if (argc != 2) {
935 fprintf(ttyout, "usage: %s [ on | off | bytecount ]\n",
936 argv[0]);
937 code = -1;
938 return;
939 } else if (strcasecmp(argv[1], "on") == 0)
940 hash = 1;
941 else if (strcasecmp(argv[1], "off") == 0)
942 hash = 0;
943 else {
944 int nmark;
945
946 nmark = strsuftoi(argv[1]);
947 if (nmark < 1) {
948 fprintf(ttyout, "mark: bad bytecount value `%s'.\n",
949 argv[1]);
950 code = -1;
951 return;
952 }
953 mark = nmark;
954 hash = 1;
955 }
956 fprintf(ttyout, "Hash mark printing %s", onoff(hash));
957 if (hash)
958 fprintf(ttyout, " (%d bytes/hash mark)", mark);
959 fputs(".\n", ttyout);
960 if (hash)
961 progress = 0;
962 code = hash;
963 }
964
965 /*
966 * Turn on printing of server echo's.
967 */
968 /*VARARGS*/
969 void
970 setverbose(int argc, char *argv[])
971 {
972
973 code = togglevar(argc, argv, &verbose, "Verbose mode");
974 }
975
976 /*
977 * Toggle PORT/LPRT cmd use before each data connection.
978 */
979 /*VARARGS*/
980 void
981 setport(int argc, char *argv[])
982 {
983
984 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds");
985 }
986
987 /*
988 * Toggle transfer progress bar.
989 */
990 /*VARARGS*/
991 void
992 setprogress(int argc, char *argv[])
993 {
994
995 code = togglevar(argc, argv, &progress, "Progress bar");
996 if (progress)
997 hash = 0;
998 }
999
1000 /*
1001 * Turn on interactive prompting during mget, mput, and mdelete.
1002 */
1003 /*VARARGS*/
1004 void
1005 setprompt(int argc, char *argv[])
1006 {
1007
1008 code = togglevar(argc, argv, &interactive, "Interactive mode");
1009 }
1010
1011 /*
1012 * Toggle gate-ftp mode, or set gate-ftp server
1013 */
1014 /*VARARGS*/
1015 void
1016 setgate(int argc, char *argv[])
1017 {
1018 static char gsbuf[MAXHOSTNAMELEN];
1019
1020 if (argc == 0 || argc > 3) {
1021 fprintf(ttyout,
1022 "usage: %s [ on | off | gateserver [port] ]\n", argv[0]);
1023 code = -1;
1024 return;
1025 } else if (argc < 2) {
1026 gatemode = !gatemode;
1027 } else {
1028 if (argc == 2 && strcasecmp(argv[1], "on") == 0)
1029 gatemode = 1;
1030 else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
1031 gatemode = 0;
1032 else {
1033 if (argc == 3)
1034 gateport = xstrdup(argv[2]);
1035 (void)strlcpy(gsbuf, argv[1], sizeof(gsbuf));
1036 gateserver = gsbuf;
1037 gatemode = 1;
1038 }
1039 }
1040 if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
1041 fprintf(ttyout,
1042 "Disabling gate-ftp mode - no gate-ftp server defined.\n");
1043 gatemode = 0;
1044 } else {
1045 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n",
1046 onoff(gatemode), *gateserver ? gateserver : "(none)",
1047 gateport);
1048 }
1049 code = gatemode;
1050 }
1051
1052 /*
1053 * Toggle metacharacter interpretation on local file names.
1054 */
1055 /*VARARGS*/
1056 void
1057 setglob(int argc, char *argv[])
1058 {
1059
1060 code = togglevar(argc, argv, &doglob, "Globbing");
1061 }
1062
1063 /*
1064 * Toggle preserving modification times on retrieved files.
1065 */
1066 /*VARARGS*/
1067 void
1068 setpreserve(int argc, char *argv[])
1069 {
1070
1071 code = togglevar(argc, argv, &preserve, "Preserve modification times");
1072 }
1073
1074 /*
1075 * Set debugging mode on/off and/or set level of debugging.
1076 */
1077 /*VARARGS*/
1078 void
1079 setdebug(int argc, char *argv[])
1080 {
1081 if (argc == 0 || argc > 2) {
1082 fprintf(ttyout, "usage: %s [ on | off | debuglevel ]\n",
1083 argv[0]);
1084 code = -1;
1085 return;
1086 } else if (argc == 2) {
1087 if (strcasecmp(argv[1], "on") == 0)
1088 debug = 1;
1089 else if (strcasecmp(argv[1], "off") == 0)
1090 debug = 0;
1091 else {
1092 int val;
1093
1094 val = strsuftoi(argv[1]);
1095 if (val < 0) {
1096 fprintf(ttyout, "%s: bad debugging value.\n",
1097 argv[1]);
1098 code = -1;
1099 return;
1100 }
1101 debug = val;
1102 }
1103 } else
1104 debug = !debug;
1105 if (debug)
1106 options |= SO_DEBUG;
1107 else
1108 options &= ~SO_DEBUG;
1109 fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug);
1110 code = debug > 0;
1111 }
1112
1113 /*
1114 * Set current working directory on remote machine.
1115 */
1116 void
1117 cd(int argc, char *argv[])
1118 {
1119 int r;
1120
1121 if (argc == 0 || argc > 2 ||
1122 (argc == 1 && !another(&argc, &argv, "remote-directory"))) {
1123 fprintf(ttyout, "usage: %s remote-directory\n", argv[0]);
1124 code = -1;
1125 return;
1126 }
1127 r = command("CWD %s", argv[1]);
1128 if (r == ERROR && code == 500) {
1129 if (verbose)
1130 fputs("CWD command not recognized, trying XCWD.\n",
1131 ttyout);
1132 r = command("XCWD %s", argv[1]);
1133 }
1134 if (r == COMPLETE) {
1135 dirchange = 1;
1136 updateremotepwd();
1137 }
1138 }
1139
1140 /*
1141 * Set current working directory on local machine.
1142 */
1143 void
1144 lcd(int argc, char *argv[])
1145 {
1146 char buf[MAXPATHLEN];
1147 char *locdir;
1148
1149 code = -1;
1150 if (argc == 1) {
1151 argc++;
1152 argv[1] = localhome;
1153 }
1154 if (argc != 2) {
1155 fprintf(ttyout, "usage: %s [local-directory]\n", argv[0]);
1156 return;
1157 }
1158 if ((locdir = globulize(argv[1])) == NULL)
1159 return;
1160 if (chdir(locdir) < 0)
1161 warn("local: %s", locdir);
1162 else {
1163 if (getcwd(buf, sizeof(buf)) != NULL) {
1164 fprintf(ttyout, "Local directory now %s\n", buf);
1165 code = 0;
1166 } else
1167 warn("getcwd: %s", locdir);
1168 }
1169 (void)free(locdir);
1170 }
1171
1172 /*
1173 * Delete a single file.
1174 */
1175 void
1176 delete(int argc, char *argv[])
1177 {
1178
1179
1180 if (argc == 0 || argc > 2 ||
1181 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
1182 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
1183 code = -1;
1184 return;
1185 }
1186 if (command("DELE %s", argv[1]) == COMPLETE)
1187 dirchange = 1;
1188 }
1189
1190 /*
1191 * Delete multiple files.
1192 */
1193 void
1194 mdelete(int argc, char *argv[])
1195 {
1196 sigfunc oldintr;
1197 int ointer;
1198 char *cp;
1199
1200 if (argc == 0 ||
1201 (argc == 1 && !another(&argc, &argv, "remote-files"))) {
1202 fprintf(ttyout, "usage: %s [remote-files]\n", argv[0]);
1203 code = -1;
1204 return;
1205 }
1206 mname = argv[0];
1207 mflag = 1;
1208 oldintr = xsignal(SIGINT, mintr);
1209 if (sigsetjmp(jabort, 1))
1210 mabort();
1211 while ((cp = remglob(argv, 0, NULL)) != NULL) {
1212 if (*cp == '\0') {
1213 mflag = 0;
1214 continue;
1215 }
1216 if (mflag && confirm(argv[0], cp)) {
1217 if (command("DELE %s", cp) == COMPLETE)
1218 dirchange = 1;
1219 if (!mflag && fromatty) {
1220 ointer = interactive;
1221 interactive = 1;
1222 if (confirm("Continue with", "mdelete")) {
1223 mflag++;
1224 }
1225 interactive = ointer;
1226 }
1227 }
1228 }
1229 (void)xsignal(SIGINT, oldintr);
1230 mflag = 0;
1231 }
1232
1233 /*
1234 * Rename a remote file.
1235 */
1236 void
1237 renamefile(int argc, char *argv[])
1238 {
1239
1240 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "from-name")))
1241 goto usage;
1242 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1243 usage:
1244 fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]);
1245 code = -1;
1246 return;
1247 }
1248 if (command("RNFR %s", argv[1]) == CONTINUE &&
1249 command("RNTO %s", argv[2]) == COMPLETE)
1250 dirchange = 1;
1251 }
1252
1253 /*
1254 * Get a directory listing of remote files.
1255 * Supports being invoked as:
1256 * cmd runs
1257 * --- ----
1258 * dir, ls LIST
1259 * mlsd MLSD
1260 * nlist NLST
1261 * pdir, pls LIST |$PAGER
1262 * mmlsd MLSD |$PAGER
1263 */
1264 void
1265 ls(int argc, char *argv[])
1266 {
1267 const char *cmd;
1268 char *remdir, *locfile;
1269 int freelocfile, pagecmd, mlsdcmd;
1270
1271 remdir = NULL;
1272 locfile = "-";
1273 freelocfile = pagecmd = mlsdcmd = 0;
1274 /*
1275 * the only commands that start with `p' are
1276 * the `pager' versions.
1277 */
1278 if (argv[0][0] == 'p')
1279 pagecmd = 1;
1280 if (strcmp(argv[0] + pagecmd , "mlsd") == 0) {
1281 if (! features[FEAT_MLST]) {
1282 fprintf(ttyout,
1283 "MLSD is not supported by the remote server.\n");
1284 return;
1285 }
1286 mlsdcmd = 1;
1287 }
1288 if (argc == 0)
1289 goto usage;
1290
1291 if (mlsdcmd)
1292 cmd = "MLSD";
1293 else if (strcmp(argv[0] + pagecmd, "nlist") == 0)
1294 cmd = "NLST";
1295 else
1296 cmd = "LIST";
1297
1298 if (argc > 1)
1299 remdir = argv[1];
1300 if (argc > 2)
1301 locfile = argv[2];
1302 if (argc > 3 || ((pagecmd | mlsdcmd) && argc > 2)) {
1303 usage:
1304 if (pagecmd || mlsdcmd)
1305 fprintf(ttyout,
1306 "usage: %s [remote-path]\n", argv[0]);
1307 else
1308 fprintf(ttyout,
1309 "usage: %s [remote-path [local-file]]\n",
1310 argv[0]);
1311 code = -1;
1312 goto freels;
1313 }
1314
1315 if (pagecmd) {
1316 char *p;
1317 int len;
1318
1319 p = getoptionvalue("pager");
1320 if (EMPTYSTRING(p))
1321 p = DEFAULTPAGER;
1322 len = strlen(p) + 2;
1323 locfile = xmalloc(len);
1324 locfile[0] = '|';
1325 (void)strlcpy(locfile + 1, p, len - 1);
1326 freelocfile = 1;
1327 } else if ((strcmp(locfile, "-") != 0) && *locfile != '|') {
1328 mname = argv[0];
1329 if ((locfile = globulize(locfile)) == NULL ||
1330 !confirm("output to local-file:", locfile)) {
1331 code = -1;
1332 goto freels;
1333 }
1334 freelocfile = 1;
1335 }
1336 recvrequest(cmd, locfile, remdir, "w", 0, 0);
1337 freels:
1338 if (freelocfile && locfile)
1339 (void)free(locfile);
1340 }
1341
1342 /*
1343 * Get a directory listing of multiple remote files.
1344 */
1345 void
1346 mls(int argc, char *argv[])
1347 {
1348 sigfunc oldintr;
1349 int ointer, i;
1350 int dolist;
1351 char *mode, *dest, *odest;
1352
1353 if (argc == 0)
1354 goto usage;
1355 if (argc < 2 && !another(&argc, &argv, "remote-files"))
1356 goto usage;
1357 if (argc < 3 && !another(&argc, &argv, "local-file")) {
1358 usage:
1359 fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]);
1360 code = -1;
1361 return;
1362 }
1363 odest = dest = argv[argc - 1];
1364 argv[argc - 1] = NULL;
1365 mname = argv[0];
1366 if (strcmp(dest, "-") && *dest != '|')
1367 if (((dest = globulize(dest)) == NULL) ||
1368 !confirm("output to local-file:", dest)) {
1369 code = -1;
1370 return;
1371 }
1372 dolist = strcmp(argv[0], "mls");
1373 mflag = 1;
1374 oldintr = xsignal(SIGINT, mintr);
1375 if (sigsetjmp(jabort, 1))
1376 mabort();
1377 for (i = 1; mflag && i < argc-1 && connected; i++) {
1378 mode = (i == 1) ? "w" : "a";
1379 recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode,
1380 0, 0);
1381 if (!mflag && fromatty) {
1382 ointer = interactive;
1383 interactive = 1;
1384 if (confirm("Continue with", argv[0])) {
1385 mflag ++;
1386 }
1387 interactive = ointer;
1388 }
1389 }
1390 (void)xsignal(SIGINT, oldintr);
1391 mflag = 0;
1392 if (dest != odest) /* free up after globulize() */
1393 free(dest);
1394 }
1395
1396 /*
1397 * Do a shell escape
1398 */
1399 /*ARGSUSED*/
1400 void
1401 shell(int argc, char *argv[])
1402 {
1403 pid_t pid;
1404 sigfunc oldintr;
1405 char shellnam[MAXPATHLEN], *shell, *namep;
1406 int wait_status;
1407
1408 if (argc == 0) {
1409 fprintf(ttyout, "usage: %s [command [args]]\n", argv[0]);
1410 code = -1;
1411 return;
1412 }
1413 oldintr = xsignal(SIGINT, SIG_IGN);
1414 if ((pid = fork()) == 0) {
1415 for (pid = 3; pid < 20; pid++)
1416 (void)close(pid);
1417 (void)xsignal(SIGINT, SIG_DFL);
1418 shell = getenv("SHELL");
1419 if (shell == NULL)
1420 shell = _PATH_BSHELL;
1421 namep = strrchr(shell, '/');
1422 if (namep == NULL)
1423 namep = shell;
1424 else
1425 namep++;
1426 (void)strlcpy(shellnam, namep, sizeof(shellnam));
1427 if (debug) {
1428 fputs(shell, ttyout);
1429 putc('\n', ttyout);
1430 }
1431 if (argc > 1) {
1432 execl(shell, shellnam, "-c", altarg, (char *)0);
1433 }
1434 else {
1435 execl(shell, shellnam, (char *)0);
1436 }
1437 warn("%s", shell);
1438 code = -1;
1439 exit(1);
1440 }
1441 if (pid > 0)
1442 while (wait(&wait_status) != pid)
1443 ;
1444 (void)xsignal(SIGINT, oldintr);
1445 if (pid == -1) {
1446 warn("Try again later");
1447 code = -1;
1448 } else
1449 code = 0;
1450 }
1451
1452 /*
1453 * Send new user information (re-login)
1454 */
1455 void
1456 user(int argc, char *argv[])
1457 {
1458 char acct[80];
1459 int n, aflag = 0;
1460
1461 if (argc == 0)
1462 goto usage;
1463 if (argc < 2)
1464 (void)another(&argc, &argv, "username");
1465 if (argc < 2 || argc > 4) {
1466 usage:
1467 fprintf(ttyout, "usage: %s username [password [account]]\n",
1468 argv[0]);
1469 code = -1;
1470 return;
1471 }
1472 n = command("USER %s", argv[1]);
1473 if (n == CONTINUE) {
1474 if (argc < 3) {
1475 argv[2] = getpass("Password: ");
1476 argc++;
1477 }
1478 n = command("PASS %s", argv[2]);
1479 }
1480 if (n == CONTINUE) {
1481 if (argc < 4) {
1482 (void)fputs("Account: ", ttyout);
1483 (void)fflush(ttyout);
1484 if (fgets(acct, sizeof(acct) - 1, stdin) == NULL) {
1485 fprintf(ttyout,
1486 "\nEOF received; login aborted.\n");
1487 clearerr(stdin);
1488 code = -1;
1489 return;
1490 }
1491 acct[strlen(acct) - 1] = '\0';
1492 argv[3] = acct; argc++;
1493 }
1494 n = command("ACCT %s", argv[3]);
1495 aflag++;
1496 }
1497 if (n != COMPLETE) {
1498 fputs("Login failed.\n", ttyout);
1499 return;
1500 }
1501 if (!aflag && argc == 4) {
1502 (void)command("ACCT %s", argv[3]);
1503 }
1504 connected = -1;
1505 getremoteinfo();
1506 }
1507
1508 /*
1509 * Print working directory on remote machine.
1510 */
1511 /*VARARGS*/
1512 void
1513 pwd(int argc, char *argv[])
1514 {
1515 int oldverbose = verbose;
1516
1517 if (argc == 0) {
1518 fprintf(ttyout, "usage: %s\n", argv[0]);
1519 code = -1;
1520 return;
1521 }
1522 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
1523 if (command("PWD") == ERROR && code == 500) {
1524 fputs("PWD command not recognized, trying XPWD.\n", ttyout);
1525 (void)command("XPWD");
1526 }
1527 verbose = oldverbose;
1528 }
1529
1530 /*
1531 * Print working directory on local machine.
1532 */
1533 void
1534 lpwd(int argc, char *argv[])
1535 {
1536 char buf[MAXPATHLEN];
1537
1538 if (argc == 0) {
1539 fprintf(ttyout, "usage: %s\n", argv[0]);
1540 code = -1;
1541 return;
1542 }
1543 if (getcwd(buf, sizeof(buf)) != NULL) {
1544 fprintf(ttyout, "Local directory %s\n", buf);
1545 code = 0;
1546 } else {
1547 warn("getcwd");
1548 code = -1;
1549 }
1550 }
1551
1552 /*
1553 * Make a directory.
1554 */
1555 void
1556 makedir(int argc, char *argv[])
1557 {
1558 int r;
1559
1560 if (argc == 0 || argc > 2 ||
1561 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1562 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1563 code = -1;
1564 return;
1565 }
1566 r = command("MKD %s", argv[1]);
1567 if (r == ERROR && code == 500) {
1568 if (verbose)
1569 fputs("MKD command not recognized, trying XMKD.\n",
1570 ttyout);
1571 r = command("XMKD %s", argv[1]);
1572 }
1573 if (r == COMPLETE)
1574 dirchange = 1;
1575 }
1576
1577 /*
1578 * Remove a directory.
1579 */
1580 void
1581 removedir(int argc, char *argv[])
1582 {
1583 int r;
1584
1585 if (argc == 0 || argc > 2 ||
1586 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1587 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1588 code = -1;
1589 return;
1590 }
1591 r = command("RMD %s", argv[1]);
1592 if (r == ERROR && code == 500) {
1593 if (verbose)
1594 fputs("RMD command not recognized, trying XRMD.\n",
1595 ttyout);
1596 r = command("XRMD %s", argv[1]);
1597 }
1598 if (r == COMPLETE)
1599 dirchange = 1;
1600 }
1601
1602 /*
1603 * Send a line, verbatim, to the remote machine.
1604 */
1605 void
1606 quote(int argc, char *argv[])
1607 {
1608
1609 if (argc == 0 ||
1610 (argc == 1 && !another(&argc, &argv, "command line to send"))) {
1611 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1612 code = -1;
1613 return;
1614 }
1615 quote1("", argc, argv);
1616 }
1617
1618 /*
1619 * Send a SITE command to the remote machine. The line
1620 * is sent verbatim to the remote machine, except that the
1621 * word "SITE" is added at the front.
1622 */
1623 void
1624 site(int argc, char *argv[])
1625 {
1626
1627 if (argc == 0 ||
1628 (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){
1629 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1630 code = -1;
1631 return;
1632 }
1633 quote1("SITE ", argc, argv);
1634 }
1635
1636 /*
1637 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1638 * Send the result as a one-line command and get response.
1639 */
1640 void
1641 quote1(const char *initial, int argc, char *argv[])
1642 {
1643 int i;
1644 char buf[BUFSIZ]; /* must be >= sizeof(line) */
1645
1646 (void)strlcpy(buf, initial, sizeof(buf));
1647 for (i = 1; i < argc; i++) {
1648 (void)strlcat(buf, argv[i], sizeof(buf));
1649 if (i < (argc - 1))
1650 (void)strlcat(buf, " ", sizeof(buf));
1651 }
1652 if (command("%s", buf) == PRELIM) {
1653 while (getreply(0) == PRELIM)
1654 continue;
1655 }
1656 dirchange = 1;
1657 }
1658
1659 void
1660 do_chmod(int argc, char *argv[])
1661 {
1662
1663 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode")))
1664 goto usage;
1665 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
1666 usage:
1667 fprintf(ttyout, "usage: %s mode remote-file\n", argv[0]);
1668 code = -1;
1669 return;
1670 }
1671 (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1672 }
1673
1674 #define COMMAND_1ARG(argc, argv, cmd) \
1675 if (argc == 1) \
1676 command(cmd); \
1677 else \
1678 command(cmd " %s", argv[1])
1679
1680 void
1681 do_umask(int argc, char *argv[])
1682 {
1683 int oldverbose = verbose;
1684
1685 if (argc == 0) {
1686 fprintf(ttyout, "usage: %s [umask]\n", argv[0]);
1687 code = -1;
1688 return;
1689 }
1690 verbose = 1;
1691 COMMAND_1ARG(argc, argv, "SITE UMASK");
1692 verbose = oldverbose;
1693 }
1694
1695 void
1696 idlecmd(int argc, char *argv[])
1697 {
1698 int oldverbose = verbose;
1699
1700 if (argc < 1 || argc > 2) {
1701 fprintf(ttyout, "usage: %s [seconds]\n", argv[0]);
1702 code = -1;
1703 return;
1704 }
1705 verbose = 1;
1706 COMMAND_1ARG(argc, argv, "SITE IDLE");
1707 verbose = oldverbose;
1708 }
1709
1710 /*
1711 * Ask the other side for help.
1712 */
1713 void
1714 rmthelp(int argc, char *argv[])
1715 {
1716 int oldverbose = verbose;
1717
1718 if (argc == 0) {
1719 fprintf(ttyout, "usage: %s\n", argv[0]);
1720 code = -1;
1721 return;
1722 }
1723 verbose = 1;
1724 COMMAND_1ARG(argc, argv, "HELP");
1725 verbose = oldverbose;
1726 }
1727
1728 /*
1729 * Terminate session and exit.
1730 * May be called with 0, NULL.
1731 */
1732 /*VARARGS*/
1733 void
1734 quit(int argc, char *argv[])
1735 {
1736
1737 /* this may be called with argc == 0, argv == NULL */
1738 if (argc == 0 && argv != NULL) {
1739 fprintf(ttyout, "usage: %s\n", argv[0]);
1740 code = -1;
1741 return;
1742 }
1743 if (connected)
1744 disconnect(0, NULL);
1745 pswitch(1);
1746 if (connected)
1747 disconnect(0, NULL);
1748 exit(0);
1749 }
1750
1751 /*
1752 * Terminate session, but don't exit.
1753 * May be called with 0, NULL.
1754 */
1755 void
1756 disconnect(int argc, char *argv[])
1757 {
1758
1759 /* this may be called with argc == 0, argv == NULL */
1760 if (argc == 0 && argv != NULL) {
1761 fprintf(ttyout, "usage: %s\n", argv[0]);
1762 code = -1;
1763 return;
1764 }
1765 if (!connected)
1766 return;
1767 (void)command("QUIT");
1768 cleanuppeer();
1769 }
1770
1771 void
1772 account(int argc, char *argv[])
1773 {
1774 char *ap;
1775
1776 if (argc == 0 || argc > 2) {
1777 fprintf(ttyout, "usage: %s [password]\n", argv[0]);
1778 code = -1;
1779 return;
1780 }
1781 else if (argc == 2)
1782 ap = argv[1];
1783 else
1784 ap = getpass("Account:");
1785 (void)command("ACCT %s", ap);
1786 }
1787
1788 sigjmp_buf abortprox;
1789
1790 void
1791 proxabort(int notused)
1792 {
1793
1794 sigint_raised = 1;
1795 alarmtimer(0);
1796 if (!proxy) {
1797 pswitch(1);
1798 }
1799 if (connected) {
1800 proxflag = 1;
1801 }
1802 else {
1803 proxflag = 0;
1804 }
1805 pswitch(0);
1806 siglongjmp(abortprox, 1);
1807 }
1808
1809 void
1810 doproxy(int argc, char *argv[])
1811 {
1812 struct cmd *c;
1813 int cmdpos;
1814 sigfunc oldintr;
1815
1816 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) {
1817 fprintf(ttyout, "usage: %s command\n", argv[0]);
1818 code = -1;
1819 return;
1820 }
1821 c = getcmd(argv[1]);
1822 if (c == (struct cmd *) -1) {
1823 fputs("?Ambiguous command.\n", ttyout);
1824 code = -1;
1825 return;
1826 }
1827 if (c == 0) {
1828 fputs("?Invalid command.\n", ttyout);
1829 code = -1;
1830 return;
1831 }
1832 if (!c->c_proxy) {
1833 fputs("?Invalid proxy command.\n", ttyout);
1834 code = -1;
1835 return;
1836 }
1837 if (sigsetjmp(abortprox, 1)) {
1838 code = -1;
1839 return;
1840 }
1841 oldintr = xsignal(SIGINT, proxabort);
1842 pswitch(1);
1843 if (c->c_conn && !connected) {
1844 fputs("Not connected.\n", ttyout);
1845 pswitch(0);
1846 (void)xsignal(SIGINT, oldintr);
1847 code = -1;
1848 return;
1849 }
1850 cmdpos = strcspn(line, " \t");
1851 if (cmdpos > 0) /* remove leading "proxy " from input buffer */
1852 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1853 argv[1] = c->c_name;
1854 (*c->c_handler)(argc-1, argv+1);
1855 if (connected) {
1856 proxflag = 1;
1857 }
1858 else {
1859 proxflag = 0;
1860 }
1861 pswitch(0);
1862 (void)xsignal(SIGINT, oldintr);
1863 }
1864
1865 void
1866 setcase(int argc, char *argv[])
1867 {
1868
1869 code = togglevar(argc, argv, &mcase, "Case mapping");
1870 }
1871
1872 /*
1873 * convert the given name to lower case if it's all upper case, into
1874 * a static buffer which is returned to the caller
1875 */
1876 char *
1877 docase(char *name)
1878 {
1879 static char new[MAXPATHLEN];
1880 int i, dochange;
1881
1882 dochange = 1;
1883 for (i = 0; name[i] != '\0' && i < sizeof(new) - 1; i++) {
1884 new[i] = name[i];
1885 if (islower((unsigned char)new[i]))
1886 dochange = 0;
1887 }
1888 new[i] = '\0';
1889
1890 if (dochange) {
1891 for (i = 0; new[i] != '\0'; i++)
1892 if (isupper((unsigned char)new[i]))
1893 new[i] = tolower(new[i]);
1894 }
1895 return (new);
1896 }
1897
1898 void
1899 setcr(int argc, char *argv[])
1900 {
1901
1902 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1903 }
1904
1905 void
1906 setntrans(int argc, char *argv[])
1907 {
1908
1909 if (argc == 0 || argc > 3) {
1910 fprintf(ttyout, "usage: %s [inchars [outchars]]\n", argv[0]);
1911 code = -1;
1912 return;
1913 }
1914 if (argc == 1) {
1915 ntflag = 0;
1916 fputs("Ntrans off.\n", ttyout);
1917 code = ntflag;
1918 return;
1919 }
1920 ntflag++;
1921 code = ntflag;
1922 (void)strlcpy(ntin, argv[1], sizeof(ntin));
1923 if (argc == 2) {
1924 ntout[0] = '\0';
1925 return;
1926 }
1927 (void)strlcpy(ntout, argv[2], sizeof(ntout));
1928 }
1929
1930 char *
1931 dotrans(char *name)
1932 {
1933 static char new[MAXPATHLEN];
1934 char *cp1, *cp2 = new;
1935 int i, ostop, found;
1936
1937 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1938 continue;
1939 for (cp1 = name; *cp1; cp1++) {
1940 found = 0;
1941 for (i = 0; *(ntin + i) && i < 16; i++) {
1942 if (*cp1 == *(ntin + i)) {
1943 found++;
1944 if (i < ostop) {
1945 *cp2++ = *(ntout + i);
1946 }
1947 break;
1948 }
1949 }
1950 if (!found) {
1951 *cp2++ = *cp1;
1952 }
1953 }
1954 *cp2 = '\0';
1955 return (new);
1956 }
1957
1958 void
1959 setnmap(int argc, char *argv[])
1960 {
1961 char *cp;
1962
1963 if (argc == 1) {
1964 mapflag = 0;
1965 fputs("Nmap off.\n", ttyout);
1966 code = mapflag;
1967 return;
1968 }
1969 if (argc == 0 ||
1970 (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1971 fprintf(ttyout, "usage: %s [mapin mapout]\n", argv[0]);
1972 code = -1;
1973 return;
1974 }
1975 mapflag = 1;
1976 code = 1;
1977 cp = strchr(altarg, ' ');
1978 if (proxy) {
1979 while(*++cp == ' ')
1980 continue;
1981 altarg = cp;
1982 cp = strchr(altarg, ' ');
1983 }
1984 *cp = '\0';
1985 (void)strlcpy(mapin, altarg, MAXPATHLEN);
1986 while (*++cp == ' ')
1987 continue;
1988 (void)strlcpy(mapout, cp, MAXPATHLEN);
1989 }
1990
1991 char *
1992 domap(char *name)
1993 {
1994 static char new[MAXPATHLEN];
1995 char *cp1 = name, *cp2 = mapin;
1996 char *tp[9], *te[9];
1997 int i, toks[9], toknum = 0, match = 1;
1998
1999 for (i=0; i < 9; ++i) {
2000 toks[i] = 0;
2001 }
2002 while (match && *cp1 && *cp2) {
2003 switch (*cp2) {
2004 case '\\':
2005 if (*++cp2 != *cp1) {
2006 match = 0;
2007 }
2008 break;
2009 case '$':
2010 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
2011 if (*cp1 != *(++cp2+1)) {
2012 toks[toknum = *cp2 - '1']++;
2013 tp[toknum] = cp1;
2014 while (*++cp1 && *(cp2+1)
2015 != *cp1);
2016 te[toknum] = cp1;
2017 }
2018 cp2++;
2019 break;
2020 }
2021 /* FALLTHROUGH */
2022 default:
2023 if (*cp2 != *cp1) {
2024 match = 0;
2025 }
2026 break;
2027 }
2028 if (match && *cp1) {
2029 cp1++;
2030 }
2031 if (match && *cp2) {
2032 cp2++;
2033 }
2034 }
2035 if (!match && *cp1) /* last token mismatch */
2036 {
2037 toks[toknum] = 0;
2038 }
2039 cp1 = new;
2040 *cp1 = '\0';
2041 cp2 = mapout;
2042 while (*cp2) {
2043 match = 0;
2044 switch (*cp2) {
2045 case '\\':
2046 if (*(cp2 + 1)) {
2047 *cp1++ = *++cp2;
2048 }
2049 break;
2050 case '[':
2051 LOOP:
2052 if (*++cp2 == '$' &&
2053 isdigit((unsigned char)*(cp2+1))) {
2054 if (*++cp2 == '0') {
2055 char *cp3 = name;
2056
2057 while (*cp3) {
2058 *cp1++ = *cp3++;
2059 }
2060 match = 1;
2061 }
2062 else if (toks[toknum = *cp2 - '1']) {
2063 char *cp3 = tp[toknum];
2064
2065 while (cp3 != te[toknum]) {
2066 *cp1++ = *cp3++;
2067 }
2068 match = 1;
2069 }
2070 }
2071 else {
2072 while (*cp2 && *cp2 != ',' &&
2073 *cp2 != ']') {
2074 if (*cp2 == '\\') {
2075 cp2++;
2076 }
2077 else if (*cp2 == '$' &&
2078 isdigit((unsigned char)*(cp2+1))) {
2079 if (*++cp2 == '0') {
2080 char *cp3 = name;
2081
2082 while (*cp3) {
2083 *cp1++ = *cp3++;
2084 }
2085 }
2086 else if (toks[toknum =
2087 *cp2 - '1']) {
2088 char *cp3=tp[toknum];
2089
2090 while (cp3 !=
2091 te[toknum]) {
2092 *cp1++ = *cp3++;
2093 }
2094 }
2095 }
2096 else if (*cp2) {
2097 *cp1++ = *cp2++;
2098 }
2099 }
2100 if (!*cp2) {
2101 fputs(
2102 "nmap: unbalanced brackets.\n",
2103 ttyout);
2104 return (name);
2105 }
2106 match = 1;
2107 cp2--;
2108 }
2109 if (match) {
2110 while (*++cp2 && *cp2 != ']') {
2111 if (*cp2 == '\\' && *(cp2 + 1)) {
2112 cp2++;
2113 }
2114 }
2115 if (!*cp2) {
2116 fputs(
2117 "nmap: unbalanced brackets.\n",
2118 ttyout);
2119 return (name);
2120 }
2121 break;
2122 }
2123 switch (*++cp2) {
2124 case ',':
2125 goto LOOP;
2126 case ']':
2127 break;
2128 default:
2129 cp2--;
2130 goto LOOP;
2131 }
2132 break;
2133 case '$':
2134 if (isdigit((unsigned char)*(cp2 + 1))) {
2135 if (*++cp2 == '0') {
2136 char *cp3 = name;
2137
2138 while (*cp3) {
2139 *cp1++ = *cp3++;
2140 }
2141 }
2142 else if (toks[toknum = *cp2 - '1']) {
2143 char *cp3 = tp[toknum];
2144
2145 while (cp3 != te[toknum]) {
2146 *cp1++ = *cp3++;
2147 }
2148 }
2149 break;
2150 }
2151 /* intentional drop through */
2152 default:
2153 *cp1++ = *cp2;
2154 break;
2155 }
2156 cp2++;
2157 }
2158 *cp1 = '\0';
2159 if (!*new) {
2160 return (name);
2161 }
2162 return (new);
2163 }
2164
2165 void
2166 setpassive(int argc, char *argv[])
2167 {
2168
2169 if (argc == 1) {
2170 passivemode = !passivemode;
2171 activefallback = passivemode;
2172 } else if (argc != 2) {
2173 passiveusage:
2174 fprintf(ttyout, "usage: %s [ on | off | auto ]\n", argv[0]);
2175 code = -1;
2176 return;
2177 } else if (strcasecmp(argv[1], "on") == 0) {
2178 passivemode = 1;
2179 activefallback = 0;
2180 } else if (strcasecmp(argv[1], "off") == 0) {
2181 passivemode = 0;
2182 activefallback = 0;
2183 } else if (strcasecmp(argv[1], "auto") == 0) {
2184 passivemode = 1;
2185 activefallback = 1;
2186 } else
2187 goto passiveusage;
2188 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
2189 onoff(passivemode), onoff(activefallback));
2190 code = passivemode;
2191 }
2192
2193 void
2194 setepsv4(int argc, char *argv[])
2195 {
2196
2197 code = togglevar(argc, argv, &epsv4,
2198 verbose ? "EPSV/EPRT on IPv4" : NULL);
2199 epsv4bad = 0;
2200 }
2201
2202 void
2203 setsunique(int argc, char *argv[])
2204 {
2205
2206 code = togglevar(argc, argv, &sunique, "Store unique");
2207 }
2208
2209 void
2210 setrunique(int argc, char *argv[])
2211 {
2212
2213 code = togglevar(argc, argv, &runique, "Receive unique");
2214 }
2215
2216 int
2217 parserate(int argc, char *argv[], int cmdlineopt)
2218 {
2219 int dir, max, incr, showonly;
2220 sigfunc oldusr1, oldusr2;
2221
2222 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) {
2223 usage:
2224 if (cmdlineopt)
2225 fprintf(ttyout,
2226 "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n",
2227 argv[0]);
2228 else
2229 fprintf(ttyout,
2230 "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n",
2231 argv[0]);
2232 return -1;
2233 }
2234 dir = max = incr = showonly = 0;
2235 #define RATE_GET 1
2236 #define RATE_PUT 2
2237 #define RATE_ALL (RATE_GET | RATE_PUT)
2238
2239 if (strcasecmp(argv[1], "all") == 0)
2240 dir = RATE_ALL;
2241 else if (strcasecmp(argv[1], "get") == 0)
2242 dir = RATE_GET;
2243 else if (strcasecmp(argv[1], "put") == 0)
2244 dir = RATE_PUT;
2245 else
2246 goto usage;
2247
2248 if (argc >= 3) {
2249 if ((max = strsuftoi(argv[2])) < 0)
2250 goto usage;
2251 } else
2252 showonly = 1;
2253
2254 if (argc == 4) {
2255 if ((incr = strsuftoi(argv[3])) <= 0)
2256 goto usage;
2257 } else
2258 incr = DEFAULTINCR;
2259
2260 oldusr1 = xsignal(SIGUSR1, SIG_IGN);
2261 oldusr2 = xsignal(SIGUSR2, SIG_IGN);
2262 if (dir & RATE_GET) {
2263 if (!showonly) {
2264 rate_get = max;
2265 rate_get_incr = incr;
2266 }
2267 if (!cmdlineopt || verbose)
2268 fprintf(ttyout,
2269 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
2270 onoff(rate_get), rate_get, rate_get_incr);
2271 }
2272 if (dir & RATE_PUT) {
2273 if (!showonly) {
2274 rate_put = max;
2275 rate_put_incr = incr;
2276 }
2277 if (!cmdlineopt || verbose)
2278 fprintf(ttyout,
2279 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
2280 onoff(rate_put), rate_put, rate_put_incr);
2281 }
2282 (void)xsignal(SIGUSR1, oldusr1);
2283 (void)xsignal(SIGUSR2, oldusr2);
2284 return 0;
2285 }
2286
2287 void
2288 setrate(int argc, char *argv[])
2289 {
2290
2291 code = parserate(argc, argv, 0);
2292 }
2293
2294 /* change directory to parent directory */
2295 void
2296 cdup(int argc, char *argv[])
2297 {
2298 int r;
2299
2300 if (argc == 0) {
2301 fprintf(ttyout, "usage: %s\n", argv[0]);
2302 code = -1;
2303 return;
2304 }
2305 r = command("CDUP");
2306 if (r == ERROR && code == 500) {
2307 if (verbose)
2308 fputs("CDUP command not recognized, trying XCUP.\n",
2309 ttyout);
2310 r = command("XCUP");
2311 }
2312 if (r == COMPLETE) {
2313 dirchange = 1;
2314 updateremotepwd();
2315 }
2316 }
2317
2318 /*
2319 * Restart transfer at specific point
2320 */
2321 void
2322 restart(int argc, char *argv[])
2323 {
2324
2325 if (argc == 0 || argc > 2) {
2326 fprintf(ttyout, "usage: %s [restart-point]\n", argv[0]);
2327 code = -1;
2328 return;
2329 }
2330 if (! features[FEAT_REST_STREAM]) {
2331 fprintf(ttyout,
2332 "Restart is not supported by the remote server.\n");
2333 return;
2334 }
2335 if (argc == 2) {
2336 off_t rp;
2337 char *ep;
2338
2339 rp = STRTOLL(argv[1], &ep, 10);
2340 if (rp < 0 || *ep != '\0')
2341 fprintf(ttyout, "restart: Invalid offset `%s'\n",
2342 argv[1]);
2343 else
2344 restart_point = rp;
2345 }
2346 if (restart_point == 0)
2347 fputs("No restart point defined.\n", ttyout);
2348 else
2349 fprintf(ttyout,
2350 "Restarting at " LLF " for next get, put or append\n",
2351 (LLT)restart_point);
2352 }
2353
2354 /*
2355 * Show remote system type
2356 */
2357 void
2358 syst(int argc, char *argv[])
2359 {
2360 int oldverbose = verbose;
2361
2362 if (argc == 0) {
2363 fprintf(ttyout, "usage: %s\n", argv[0]);
2364 code = -1;
2365 return;
2366 }
2367 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2368 (void)command("SYST");
2369 verbose = oldverbose;
2370 }
2371
2372 void
2373 macdef(int argc, char *argv[])
2374 {
2375 char *tmp;
2376 int c;
2377
2378 if (argc == 0)
2379 goto usage;
2380 if (macnum == 16) {
2381 fputs("Limit of 16 macros have already been defined.\n",
2382 ttyout);
2383 code = -1;
2384 return;
2385 }
2386 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2387 usage:
2388 fprintf(ttyout, "usage: %s macro_name\n", argv[0]);
2389 code = -1;
2390 return;
2391 }
2392 if (interactive)
2393 fputs(
2394 "Enter macro line by line, terminating it with a null line.\n",
2395 ttyout);
2396 (void)strlcpy(macros[macnum].mac_name, argv[1],
2397 sizeof(macros[macnum].mac_name));
2398 if (macnum == 0)
2399 macros[macnum].mac_start = macbuf;
2400 else
2401 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2402 tmp = macros[macnum].mac_start;
2403 while (tmp != macbuf+4096) {
2404 if ((c = getchar()) == EOF) {
2405 fputs("macdef: end of file encountered.\n", ttyout);
2406 code = -1;
2407 return;
2408 }
2409 if ((*tmp = c) == '\n') {
2410 if (tmp == macros[macnum].mac_start) {
2411 macros[macnum++].mac_end = tmp;
2412 code = 0;
2413 return;
2414 }
2415 if (*(tmp-1) == '\0') {
2416 macros[macnum++].mac_end = tmp - 1;
2417 code = 0;
2418 return;
2419 }
2420 *tmp = '\0';
2421 }
2422 tmp++;
2423 }
2424 while (1) {
2425 while ((c = getchar()) != '\n' && c != EOF)
2426 /* LOOP */;
2427 if (c == EOF || getchar() == '\n') {
2428 fputs("Macro not defined - 4K buffer exceeded.\n",
2429 ttyout);
2430 code = -1;
2431 return;
2432 }
2433 }
2434 }
2435
2436 /*
2437 * Get size of file on remote machine
2438 */
2439 void
2440 sizecmd(int argc, char *argv[])
2441 {
2442 off_t size;
2443
2444 if (argc == 0 || argc > 2 ||
2445 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2446 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2447 code = -1;
2448 return;
2449 }
2450 size = remotesize(argv[1], 1);
2451 if (size != -1)
2452 fprintf(ttyout,
2453 "%s\t" LLF "\n", argv[1], (LLT)size);
2454 code = (size > 0);
2455 }
2456
2457 /*
2458 * Get last modification time of file on remote machine
2459 */
2460 void
2461 modtime(int argc, char *argv[])
2462 {
2463 time_t mtime;
2464
2465 if (argc == 0 || argc > 2 ||
2466 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2467 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2468 code = -1;
2469 return;
2470 }
2471 mtime = remotemodtime(argv[1], 1);
2472 if (mtime != -1)
2473 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
2474 code = (mtime > 0);
2475 }
2476
2477 /*
2478 * Show status on remote machine
2479 */
2480 void
2481 rmtstatus(int argc, char *argv[])
2482 {
2483
2484 if (argc == 0) {
2485 fprintf(ttyout, "usage: %s [remote-file]\n", argv[0]);
2486 code = -1;
2487 return;
2488 }
2489 COMMAND_1ARG(argc, argv, "STAT");
2490 }
2491
2492 /*
2493 * Get file if modtime is more recent than current file
2494 */
2495 void
2496 newer(int argc, char *argv[])
2497 {
2498
2499 if (getit(argc, argv, -1, "w"))
2500 fprintf(ttyout,
2501 "Local file \"%s\" is newer than remote file \"%s\".\n",
2502 argv[2], argv[1]);
2503 }
2504
2505 /*
2506 * Display one local file through $PAGER.
2507 */
2508 void
2509 lpage(int argc, char *argv[])
2510 {
2511 int len;
2512 char *p, *pager, *locfile;
2513
2514 if (argc == 0 || argc > 2 ||
2515 (argc == 1 && !another(&argc, &argv, "local-file"))) {
2516 fprintf(ttyout, "usage: %s local-file\n", argv[0]);
2517 code = -1;
2518 return;
2519 }
2520 if ((locfile = globulize(argv[1])) == NULL) {
2521 code = -1;
2522 return;
2523 }
2524 p = getoptionvalue("pager");
2525 if (EMPTYSTRING(p))
2526 p = DEFAULTPAGER;
2527 len = strlen(p) + strlen(locfile) + 2;
2528 pager = xmalloc(len);
2529 (void)strlcpy(pager, p, len);
2530 (void)strlcat(pager, " ", len);
2531 (void)strlcat(pager, locfile, len);
2532 system(pager);
2533 code = 0;
2534 (void)free(pager);
2535 (void)free(locfile);
2536 }
2537
2538 /*
2539 * Display one remote file through $PAGER.
2540 */
2541 void
2542 page(int argc, char *argv[])
2543 {
2544 int ohash, orestart_point, overbose, len;
2545 char *p, *pager;
2546
2547 if (argc == 0 || argc > 2 ||
2548 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2549 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2550 code = -1;
2551 return;
2552 }
2553 p = getoptionvalue("pager");
2554 if (EMPTYSTRING(p))
2555 p = DEFAULTPAGER;
2556 len = strlen(p) + 2;
2557 pager = xmalloc(len);
2558 pager[0] = '|';
2559 (void)strlcpy(pager + 1, p, len - 1);
2560
2561 ohash = hash;
2562 orestart_point = restart_point;
2563 overbose = verbose;
2564 hash = restart_point = verbose = 0;
2565 recvrequest("RETR", pager, argv[1], "r+", 1, 0);
2566 hash = ohash;
2567 restart_point = orestart_point;
2568 verbose = overbose;
2569 (void)free(pager);
2570 }
2571
2572 /*
2573 * Set the socket send or receive buffer size.
2574 */
2575 void
2576 setxferbuf(int argc, char *argv[])
2577 {
2578 int size, dir;
2579
2580 if (argc != 2) {
2581 usage:
2582 fprintf(ttyout, "usage: %s size\n", argv[0]);
2583 code = -1;
2584 return;
2585 }
2586 if (strcasecmp(argv[0], "sndbuf") == 0)
2587 dir = RATE_PUT;
2588 else if (strcasecmp(argv[0], "rcvbuf") == 0)
2589 dir = RATE_GET;
2590 else if (strcasecmp(argv[0], "xferbuf") == 0)
2591 dir = RATE_ALL;
2592 else
2593 goto usage;
2594
2595 if ((size = strsuftoi(argv[1])) == -1)
2596 goto usage;
2597
2598 if (size == 0) {
2599 fprintf(ttyout, "%s: size must be positive.\n", argv[0]);
2600 goto usage;
2601 }
2602
2603 if (dir & RATE_PUT)
2604 sndbuf_size = size;
2605 if (dir & RATE_GET)
2606 rcvbuf_size = size;
2607 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n",
2608 sndbuf_size, rcvbuf_size);
2609 code = 0;
2610 }
2611
2612 /*
2613 * Set or display options (defaults are provided by various env vars)
2614 */
2615 void
2616 setoption(int argc, char *argv[])
2617 {
2618 struct option *o;
2619
2620 code = -1;
2621 if (argc == 0 || (argc != 1 && argc != 3)) {
2622 fprintf(ttyout, "usage: %s [option value]\n", argv[0]);
2623 return;
2624 }
2625
2626 #define OPTIONINDENT ((int) sizeof("http_proxy"))
2627 if (argc == 1) {
2628 for (o = optiontab; o->name != NULL; o++) {
2629 fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT,
2630 o->name, o->value ? o->value : "");
2631 }
2632 } else {
2633 o = getoption(argv[1]);
2634 if (o == NULL) {
2635 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2636 return;
2637 }
2638 FREEPTR(o->value);
2639 o->value = xstrdup(argv[2]);
2640 if (verbose)
2641 fprintf(ttyout, "Setting `%s' to `%s'.\n",
2642 o->name, o->value);
2643 }
2644 code = 0;
2645 }
2646
2647 /*
2648 * Unset an option
2649 */
2650 void
2651 unsetoption(int argc, char *argv[])
2652 {
2653 struct option *o;
2654
2655 code = -1;
2656 if (argc == 0 || argc != 2) {
2657 fprintf(ttyout, "usage: %s option\n", argv[0]);
2658 return;
2659 }
2660
2661 o = getoption(argv[1]);
2662 if (o == NULL) {
2663 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2664 return;
2665 }
2666 FREEPTR(o->value);
2667 fprintf(ttyout, "Unsetting `%s'.\n", o->name);
2668 code = 0;
2669 }
2670
2671 /*
2672 * Display features supported by the remote host.
2673 */
2674 void
2675 feat(int argc, char *argv[])
2676 {
2677 int oldverbose = verbose;
2678
2679 if (argc == 0) {
2680 fprintf(ttyout, "usage: %s\n", argv[0]);
2681 code = -1;
2682 return;
2683 }
2684 if (! features[FEAT_FEAT]) {
2685 fprintf(ttyout,
2686 "FEAT is not supported by the remote server.\n");
2687 return;
2688 }
2689 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2690 (void)command("FEAT");
2691 verbose = oldverbose;
2692 }
2693
2694 void
2695 mlst(int argc, char *argv[])
2696 {
2697 int oldverbose = verbose;
2698
2699 if (argc < 1 || argc > 2) {
2700 fprintf(ttyout, "usage: %s [remote-path]\n", argv[0]);
2701 code = -1;
2702 return;
2703 }
2704 if (! features[FEAT_MLST]) {
2705 fprintf(ttyout,
2706 "MLST is not supported by the remote server.\n");
2707 return;
2708 }
2709 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2710 COMMAND_1ARG(argc, argv, "MLST");
2711 verbose = oldverbose;
2712 }
2713
2714 void
2715 opts(int argc, char *argv[])
2716 {
2717 int oldverbose = verbose;
2718
2719 if (argc < 2 || argc > 3) {
2720 fprintf(ttyout, "usage: %s command [options]\n", argv[0]);
2721 code = -1;
2722 return;
2723 }
2724 if (! features[FEAT_FEAT]) {
2725 fprintf(ttyout,
2726 "OPTS is not supported by the remote server.\n");
2727 return;
2728 }
2729 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2730 if (argc == 2)
2731 command("OPTS %s", argv[1]);
2732 else
2733 command("OPTS %s %s", argv[1], argv[2]);
2734 verbose = oldverbose;
2735 }
2736