cmds.c revision 1.105 1 /* $NetBSD: cmds.c,v 1.105 2004/06/06 13:53:28 kleink Exp $ */
2
3 /*-
4 * Copyright (c) 1996-2002 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.105 2004/06/06 13:53:28 kleink 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 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 if ((locfile = globulize(locfile)) == NULL ||
1329 !confirm("output to local-file:", locfile)) {
1330 code = -1;
1331 goto freels;
1332 }
1333 freelocfile = 1;
1334 }
1335 recvrequest(cmd, locfile, remdir, "w", 0, 0);
1336 freels:
1337 if (freelocfile && locfile)
1338 (void)free(locfile);
1339 }
1340
1341 /*
1342 * Get a directory listing of multiple remote files.
1343 */
1344 void
1345 mls(int argc, char *argv[])
1346 {
1347 sigfunc oldintr;
1348 int ointer, i;
1349 int dolist;
1350 char *mode, *dest, *odest;
1351
1352 if (argc == 0)
1353 goto usage;
1354 if (argc < 2 && !another(&argc, &argv, "remote-files"))
1355 goto usage;
1356 if (argc < 3 && !another(&argc, &argv, "local-file")) {
1357 usage:
1358 fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]);
1359 code = -1;
1360 return;
1361 }
1362 odest = dest = argv[argc - 1];
1363 argv[argc - 1] = NULL;
1364 if (strcmp(dest, "-") && *dest != '|')
1365 if (((dest = globulize(dest)) == NULL) ||
1366 !confirm("output to local-file:", dest)) {
1367 code = -1;
1368 return;
1369 }
1370 dolist = strcmp(argv[0], "mls");
1371 mname = argv[0];
1372 mflag = 1;
1373 oldintr = xsignal(SIGINT, mintr);
1374 if (sigsetjmp(jabort, 1))
1375 mabort();
1376 for (i = 1; mflag && i < argc-1 && connected; i++) {
1377 mode = (i == 1) ? "w" : "a";
1378 recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode,
1379 0, 0);
1380 if (!mflag && fromatty) {
1381 ointer = interactive;
1382 interactive = 1;
1383 if (confirm("Continue with", argv[0])) {
1384 mflag ++;
1385 }
1386 interactive = ointer;
1387 }
1388 }
1389 (void)xsignal(SIGINT, oldintr);
1390 mflag = 0;
1391 if (dest != odest) /* free up after globulize() */
1392 free(dest);
1393 }
1394
1395 /*
1396 * Do a shell escape
1397 */
1398 /*ARGSUSED*/
1399 void
1400 shell(int argc, char *argv[])
1401 {
1402 pid_t pid;
1403 sigfunc old1;
1404 char shellnam[MAXPATHLEN], *shell, *namep;
1405 int wait_status;
1406
1407 if (argc == 0) {
1408 fprintf(ttyout, "usage: %s [command [args]]\n", argv[0]);
1409 code = -1;
1410 return;
1411 }
1412 old1 = xsignal(SIGINT, SIG_IGN);
1413 if ((pid = fork()) == 0) {
1414 for (pid = 3; pid < 20; pid++)
1415 (void)close(pid);
1416 (void)xsignal(SIGINT, SIG_DFL);
1417 shell = getenv("SHELL");
1418 if (shell == NULL)
1419 shell = _PATH_BSHELL;
1420 namep = strrchr(shell, '/');
1421 if (namep == NULL)
1422 namep = shell;
1423 else
1424 namep++;
1425 (void)strlcpy(shellnam, namep, sizeof(shellnam));
1426 if (debug) {
1427 fputs(shell, ttyout);
1428 putc('\n', ttyout);
1429 }
1430 if (argc > 1) {
1431 execl(shell, shellnam, "-c", altarg, (char *)0);
1432 }
1433 else {
1434 execl(shell, shellnam, (char *)0);
1435 }
1436 warn("%s", shell);
1437 code = -1;
1438 exit(1);
1439 }
1440 if (pid > 0)
1441 while (wait(&wait_status) != pid)
1442 ;
1443 (void)xsignal(SIGINT, old1);
1444 if (pid == -1) {
1445 warn("Try again later");
1446 code = -1;
1447 } else
1448 code = 0;
1449 }
1450
1451 /*
1452 * Send new user information (re-login)
1453 */
1454 void
1455 user(int argc, char *argv[])
1456 {
1457 char acct[80];
1458 int n, aflag = 0;
1459
1460 if (argc == 0)
1461 goto usage;
1462 if (argc < 2)
1463 (void)another(&argc, &argv, "username");
1464 if (argc < 2 || argc > 4) {
1465 usage:
1466 fprintf(ttyout, "usage: %s username [password [account]]\n",
1467 argv[0]);
1468 code = -1;
1469 return;
1470 }
1471 n = command("USER %s", argv[1]);
1472 if (n == CONTINUE) {
1473 if (argc < 3) {
1474 argv[2] = getpass("Password: ");
1475 argc++;
1476 }
1477 n = command("PASS %s", argv[2]);
1478 }
1479 if (n == CONTINUE) {
1480 if (argc < 4) {
1481 (void)fputs("Account: ", ttyout);
1482 (void)fflush(ttyout);
1483 if (fgets(acct, sizeof(acct) - 1, stdin) == NULL) {
1484 fprintf(ttyout,
1485 "\nEOF received; login aborted.\n");
1486 clearerr(stdin);
1487 code = -1;
1488 return;
1489 }
1490 acct[strlen(acct) - 1] = '\0';
1491 argv[3] = acct; argc++;
1492 }
1493 n = command("ACCT %s", argv[3]);
1494 aflag++;
1495 }
1496 if (n != COMPLETE) {
1497 fputs("Login failed.\n", ttyout);
1498 return;
1499 }
1500 if (!aflag && argc == 4) {
1501 (void)command("ACCT %s", argv[3]);
1502 }
1503 connected = -1;
1504 getremoteinfo();
1505 }
1506
1507 /*
1508 * Print working directory on remote machine.
1509 */
1510 /*VARARGS*/
1511 void
1512 pwd(int argc, char *argv[])
1513 {
1514 int oldverbose = verbose;
1515
1516 if (argc == 0) {
1517 fprintf(ttyout, "usage: %s\n", argv[0]);
1518 code = -1;
1519 return;
1520 }
1521 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
1522 if (command("PWD") == ERROR && code == 500) {
1523 fputs("PWD command not recognized, trying XPWD.\n", ttyout);
1524 (void)command("XPWD");
1525 }
1526 verbose = oldverbose;
1527 }
1528
1529 /*
1530 * Print working directory on local machine.
1531 */
1532 void
1533 lpwd(int argc, char *argv[])
1534 {
1535 char buf[MAXPATHLEN];
1536
1537 if (argc == 0) {
1538 fprintf(ttyout, "usage: %s\n", argv[0]);
1539 code = -1;
1540 return;
1541 }
1542 if (getcwd(buf, sizeof(buf)) != NULL) {
1543 fprintf(ttyout, "Local directory %s\n", buf);
1544 code = 0;
1545 } else {
1546 warn("getcwd");
1547 code = -1;
1548 }
1549 }
1550
1551 /*
1552 * Make a directory.
1553 */
1554 void
1555 makedir(int argc, char *argv[])
1556 {
1557 int r;
1558
1559 if (argc == 0 || argc > 2 ||
1560 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1561 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1562 code = -1;
1563 return;
1564 }
1565 r = command("MKD %s", argv[1]);
1566 if (r == ERROR && code == 500) {
1567 if (verbose)
1568 fputs("MKD command not recognized, trying XMKD.\n",
1569 ttyout);
1570 r = command("XMKD %s", argv[1]);
1571 }
1572 if (r == COMPLETE)
1573 dirchange = 1;
1574 }
1575
1576 /*
1577 * Remove a directory.
1578 */
1579 void
1580 removedir(int argc, char *argv[])
1581 {
1582 int r;
1583
1584 if (argc == 0 || argc > 2 ||
1585 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1586 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1587 code = -1;
1588 return;
1589 }
1590 r = command("RMD %s", argv[1]);
1591 if (r == ERROR && code == 500) {
1592 if (verbose)
1593 fputs("RMD command not recognized, trying XRMD.\n",
1594 ttyout);
1595 r = command("XRMD %s", argv[1]);
1596 }
1597 if (r == COMPLETE)
1598 dirchange = 1;
1599 }
1600
1601 /*
1602 * Send a line, verbatim, to the remote machine.
1603 */
1604 void
1605 quote(int argc, char *argv[])
1606 {
1607
1608 if (argc == 0 ||
1609 (argc == 1 && !another(&argc, &argv, "command line to send"))) {
1610 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1611 code = -1;
1612 return;
1613 }
1614 quote1("", argc, argv);
1615 }
1616
1617 /*
1618 * Send a SITE command to the remote machine. The line
1619 * is sent verbatim to the remote machine, except that the
1620 * word "SITE" is added at the front.
1621 */
1622 void
1623 site(int argc, char *argv[])
1624 {
1625
1626 if (argc == 0 ||
1627 (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){
1628 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1629 code = -1;
1630 return;
1631 }
1632 quote1("SITE ", argc, argv);
1633 }
1634
1635 /*
1636 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1637 * Send the result as a one-line command and get response.
1638 */
1639 void
1640 quote1(const char *initial, int argc, char *argv[])
1641 {
1642 int i;
1643 char buf[BUFSIZ]; /* must be >= sizeof(line) */
1644
1645 (void)strlcpy(buf, initial, sizeof(buf));
1646 for (i = 1; i < argc; i++) {
1647 (void)strlcat(buf, argv[i], sizeof(buf));
1648 if (i < (argc - 1))
1649 (void)strlcat(buf, " ", sizeof(buf));
1650 }
1651 if (command("%s", buf) == PRELIM) {
1652 while (getreply(0) == PRELIM)
1653 continue;
1654 }
1655 dirchange = 1;
1656 }
1657
1658 void
1659 do_chmod(int argc, char *argv[])
1660 {
1661
1662 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode")))
1663 goto usage;
1664 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
1665 usage:
1666 fprintf(ttyout, "usage: %s mode remote-file\n", argv[0]);
1667 code = -1;
1668 return;
1669 }
1670 (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1671 }
1672
1673 #define COMMAND_1ARG(argc, argv, cmd) \
1674 if (argc == 1) \
1675 command(cmd); \
1676 else \
1677 command(cmd " %s", argv[1])
1678
1679 void
1680 do_umask(int argc, char *argv[])
1681 {
1682 int oldverbose = verbose;
1683
1684 if (argc == 0) {
1685 fprintf(ttyout, "usage: %s [umask]\n", argv[0]);
1686 code = -1;
1687 return;
1688 }
1689 verbose = 1;
1690 COMMAND_1ARG(argc, argv, "SITE UMASK");
1691 verbose = oldverbose;
1692 }
1693
1694 void
1695 idlecmd(int argc, char *argv[])
1696 {
1697 int oldverbose = verbose;
1698
1699 if (argc < 1 || argc > 2) {
1700 fprintf(ttyout, "usage: %s [seconds]\n", argv[0]);
1701 code = -1;
1702 return;
1703 }
1704 verbose = 1;
1705 COMMAND_1ARG(argc, argv, "SITE IDLE");
1706 verbose = oldverbose;
1707 }
1708
1709 /*
1710 * Ask the other side for help.
1711 */
1712 void
1713 rmthelp(int argc, char *argv[])
1714 {
1715 int oldverbose = verbose;
1716
1717 if (argc == 0) {
1718 fprintf(ttyout, "usage: %s\n", argv[0]);
1719 code = -1;
1720 return;
1721 }
1722 verbose = 1;
1723 COMMAND_1ARG(argc, argv, "HELP");
1724 verbose = oldverbose;
1725 }
1726
1727 /*
1728 * Terminate session and exit.
1729 * May be called with 0, NULL.
1730 */
1731 /*VARARGS*/
1732 void
1733 quit(int argc, char *argv[])
1734 {
1735
1736 /* this may be called with argc == 0, argv == NULL */
1737 if (argc == 0 && argv != NULL) {
1738 fprintf(ttyout, "usage: %s\n", argv[0]);
1739 code = -1;
1740 return;
1741 }
1742 if (connected)
1743 disconnect(0, NULL);
1744 pswitch(1);
1745 if (connected)
1746 disconnect(0, NULL);
1747 exit(0);
1748 }
1749
1750 /*
1751 * Terminate session, but don't exit.
1752 * May be called with 0, NULL.
1753 */
1754 void
1755 disconnect(int argc, char *argv[])
1756 {
1757
1758 /* this may be called with argc == 0, argv == NULL */
1759 if (argc == 0 && argv != NULL) {
1760 fprintf(ttyout, "usage: %s\n", argv[0]);
1761 code = -1;
1762 return;
1763 }
1764 if (!connected)
1765 return;
1766 (void)command("QUIT");
1767 cleanuppeer();
1768 }
1769
1770 void
1771 account(int argc, char *argv[])
1772 {
1773 char *ap;
1774
1775 if (argc == 0 || argc > 2) {
1776 fprintf(ttyout, "usage: %s [password]\n", argv[0]);
1777 code = -1;
1778 return;
1779 }
1780 else if (argc == 2)
1781 ap = argv[1];
1782 else
1783 ap = getpass("Account:");
1784 (void)command("ACCT %s", ap);
1785 }
1786
1787 sigjmp_buf abortprox;
1788
1789 void
1790 proxabort(int notused)
1791 {
1792
1793 alarmtimer(0);
1794 if (!proxy) {
1795 pswitch(1);
1796 }
1797 if (connected) {
1798 proxflag = 1;
1799 }
1800 else {
1801 proxflag = 0;
1802 }
1803 pswitch(0);
1804 siglongjmp(abortprox, 1);
1805 }
1806
1807 void
1808 doproxy(int argc, char *argv[])
1809 {
1810 struct cmd *c;
1811 int cmdpos;
1812 sigfunc oldintr;
1813
1814 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) {
1815 fprintf(ttyout, "usage: %s command\n", argv[0]);
1816 code = -1;
1817 return;
1818 }
1819 c = getcmd(argv[1]);
1820 if (c == (struct cmd *) -1) {
1821 fputs("?Ambiguous command.\n", ttyout);
1822 code = -1;
1823 return;
1824 }
1825 if (c == 0) {
1826 fputs("?Invalid command.\n", ttyout);
1827 code = -1;
1828 return;
1829 }
1830 if (!c->c_proxy) {
1831 fputs("?Invalid proxy command.\n", ttyout);
1832 code = -1;
1833 return;
1834 }
1835 if (sigsetjmp(abortprox, 1)) {
1836 code = -1;
1837 return;
1838 }
1839 oldintr = xsignal(SIGINT, proxabort);
1840 pswitch(1);
1841 if (c->c_conn && !connected) {
1842 fputs("Not connected.\n", ttyout);
1843 pswitch(0);
1844 (void)xsignal(SIGINT, oldintr);
1845 code = -1;
1846 return;
1847 }
1848 cmdpos = strcspn(line, " \t");
1849 if (cmdpos > 0) /* remove leading "proxy " from input buffer */
1850 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1851 argv[1] = c->c_name;
1852 (*c->c_handler)(argc-1, argv+1);
1853 if (connected) {
1854 proxflag = 1;
1855 }
1856 else {
1857 proxflag = 0;
1858 }
1859 pswitch(0);
1860 (void)xsignal(SIGINT, oldintr);
1861 }
1862
1863 void
1864 setcase(int argc, char *argv[])
1865 {
1866
1867 code = togglevar(argc, argv, &mcase, "Case mapping");
1868 }
1869
1870 /*
1871 * convert the given name to lower case if it's all upper case, into
1872 * a static buffer which is returned to the caller
1873 */
1874 char *
1875 docase(char *name)
1876 {
1877 static char new[MAXPATHLEN];
1878 int i, dochange;
1879
1880 dochange = 1;
1881 for (i = 0; name[i] != '\0' && i < sizeof(new) - 1; i++) {
1882 new[i] = name[i];
1883 if (islower((unsigned char)new[i]))
1884 dochange = 0;
1885 }
1886 new[i] = '\0';
1887
1888 if (dochange) {
1889 for (i = 0; new[i] != '\0'; i++)
1890 if (isupper((unsigned char)new[i]))
1891 new[i] = tolower(new[i]);
1892 }
1893 return (new);
1894 }
1895
1896 void
1897 setcr(int argc, char *argv[])
1898 {
1899
1900 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1901 }
1902
1903 void
1904 setntrans(int argc, char *argv[])
1905 {
1906
1907 if (argc == 0 || argc > 3) {
1908 fprintf(ttyout, "usage: %s [inchars [outchars]]\n", argv[0]);
1909 code = -1;
1910 return;
1911 }
1912 if (argc == 1) {
1913 ntflag = 0;
1914 fputs("Ntrans off.\n", ttyout);
1915 code = ntflag;
1916 return;
1917 }
1918 ntflag++;
1919 code = ntflag;
1920 (void)strlcpy(ntin, argv[1], sizeof(ntin));
1921 if (argc == 2) {
1922 ntout[0] = '\0';
1923 return;
1924 }
1925 (void)strlcpy(ntout, argv[2], sizeof(ntout));
1926 }
1927
1928 char *
1929 dotrans(char *name)
1930 {
1931 static char new[MAXPATHLEN];
1932 char *cp1, *cp2 = new;
1933 int i, ostop, found;
1934
1935 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1936 continue;
1937 for (cp1 = name; *cp1; cp1++) {
1938 found = 0;
1939 for (i = 0; *(ntin + i) && i < 16; i++) {
1940 if (*cp1 == *(ntin + i)) {
1941 found++;
1942 if (i < ostop) {
1943 *cp2++ = *(ntout + i);
1944 }
1945 break;
1946 }
1947 }
1948 if (!found) {
1949 *cp2++ = *cp1;
1950 }
1951 }
1952 *cp2 = '\0';
1953 return (new);
1954 }
1955
1956 void
1957 setnmap(int argc, char *argv[])
1958 {
1959 char *cp;
1960
1961 if (argc == 1) {
1962 mapflag = 0;
1963 fputs("Nmap off.\n", ttyout);
1964 code = mapflag;
1965 return;
1966 }
1967 if (argc == 0 ||
1968 (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1969 fprintf(ttyout, "usage: %s [mapin mapout]\n", argv[0]);
1970 code = -1;
1971 return;
1972 }
1973 mapflag = 1;
1974 code = 1;
1975 cp = strchr(altarg, ' ');
1976 if (proxy) {
1977 while(*++cp == ' ')
1978 continue;
1979 altarg = cp;
1980 cp = strchr(altarg, ' ');
1981 }
1982 *cp = '\0';
1983 (void)strlcpy(mapin, altarg, MAXPATHLEN);
1984 while (*++cp == ' ')
1985 continue;
1986 (void)strlcpy(mapout, cp, MAXPATHLEN);
1987 }
1988
1989 char *
1990 domap(char *name)
1991 {
1992 static char new[MAXPATHLEN];
1993 char *cp1 = name, *cp2 = mapin;
1994 char *tp[9], *te[9];
1995 int i, toks[9], toknum = 0, match = 1;
1996
1997 for (i=0; i < 9; ++i) {
1998 toks[i] = 0;
1999 }
2000 while (match && *cp1 && *cp2) {
2001 switch (*cp2) {
2002 case '\\':
2003 if (*++cp2 != *cp1) {
2004 match = 0;
2005 }
2006 break;
2007 case '$':
2008 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
2009 if (*cp1 != *(++cp2+1)) {
2010 toks[toknum = *cp2 - '1']++;
2011 tp[toknum] = cp1;
2012 while (*++cp1 && *(cp2+1)
2013 != *cp1);
2014 te[toknum] = cp1;
2015 }
2016 cp2++;
2017 break;
2018 }
2019 /* FALLTHROUGH */
2020 default:
2021 if (*cp2 != *cp1) {
2022 match = 0;
2023 }
2024 break;
2025 }
2026 if (match && *cp1) {
2027 cp1++;
2028 }
2029 if (match && *cp2) {
2030 cp2++;
2031 }
2032 }
2033 if (!match && *cp1) /* last token mismatch */
2034 {
2035 toks[toknum] = 0;
2036 }
2037 cp1 = new;
2038 *cp1 = '\0';
2039 cp2 = mapout;
2040 while (*cp2) {
2041 match = 0;
2042 switch (*cp2) {
2043 case '\\':
2044 if (*(cp2 + 1)) {
2045 *cp1++ = *++cp2;
2046 }
2047 break;
2048 case '[':
2049 LOOP:
2050 if (*++cp2 == '$' &&
2051 isdigit((unsigned char)*(cp2+1))) {
2052 if (*++cp2 == '0') {
2053 char *cp3 = name;
2054
2055 while (*cp3) {
2056 *cp1++ = *cp3++;
2057 }
2058 match = 1;
2059 }
2060 else if (toks[toknum = *cp2 - '1']) {
2061 char *cp3 = tp[toknum];
2062
2063 while (cp3 != te[toknum]) {
2064 *cp1++ = *cp3++;
2065 }
2066 match = 1;
2067 }
2068 }
2069 else {
2070 while (*cp2 && *cp2 != ',' &&
2071 *cp2 != ']') {
2072 if (*cp2 == '\\') {
2073 cp2++;
2074 }
2075 else if (*cp2 == '$' &&
2076 isdigit((unsigned char)*(cp2+1))) {
2077 if (*++cp2 == '0') {
2078 char *cp3 = name;
2079
2080 while (*cp3) {
2081 *cp1++ = *cp3++;
2082 }
2083 }
2084 else if (toks[toknum =
2085 *cp2 - '1']) {
2086 char *cp3=tp[toknum];
2087
2088 while (cp3 !=
2089 te[toknum]) {
2090 *cp1++ = *cp3++;
2091 }
2092 }
2093 }
2094 else if (*cp2) {
2095 *cp1++ = *cp2++;
2096 }
2097 }
2098 if (!*cp2) {
2099 fputs(
2100 "nmap: unbalanced brackets.\n",
2101 ttyout);
2102 return (name);
2103 }
2104 match = 1;
2105 cp2--;
2106 }
2107 if (match) {
2108 while (*++cp2 && *cp2 != ']') {
2109 if (*cp2 == '\\' && *(cp2 + 1)) {
2110 cp2++;
2111 }
2112 }
2113 if (!*cp2) {
2114 fputs(
2115 "nmap: unbalanced brackets.\n",
2116 ttyout);
2117 return (name);
2118 }
2119 break;
2120 }
2121 switch (*++cp2) {
2122 case ',':
2123 goto LOOP;
2124 case ']':
2125 break;
2126 default:
2127 cp2--;
2128 goto LOOP;
2129 }
2130 break;
2131 case '$':
2132 if (isdigit((unsigned char)*(cp2 + 1))) {
2133 if (*++cp2 == '0') {
2134 char *cp3 = name;
2135
2136 while (*cp3) {
2137 *cp1++ = *cp3++;
2138 }
2139 }
2140 else if (toks[toknum = *cp2 - '1']) {
2141 char *cp3 = tp[toknum];
2142
2143 while (cp3 != te[toknum]) {
2144 *cp1++ = *cp3++;
2145 }
2146 }
2147 break;
2148 }
2149 /* intentional drop through */
2150 default:
2151 *cp1++ = *cp2;
2152 break;
2153 }
2154 cp2++;
2155 }
2156 *cp1 = '\0';
2157 if (!*new) {
2158 return (name);
2159 }
2160 return (new);
2161 }
2162
2163 void
2164 setpassive(int argc, char *argv[])
2165 {
2166
2167 if (argc == 1) {
2168 passivemode = !passivemode;
2169 activefallback = passivemode;
2170 } else if (argc != 2) {
2171 passiveusage:
2172 fprintf(ttyout, "usage: %s [ on | off | auto ]\n", argv[0]);
2173 code = -1;
2174 return;
2175 } else if (strcasecmp(argv[1], "on") == 0) {
2176 passivemode = 1;
2177 activefallback = 0;
2178 } else if (strcasecmp(argv[1], "off") == 0) {
2179 passivemode = 0;
2180 activefallback = 0;
2181 } else if (strcasecmp(argv[1], "auto") == 0) {
2182 passivemode = 1;
2183 activefallback = 1;
2184 } else
2185 goto passiveusage;
2186 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
2187 onoff(passivemode), onoff(activefallback));
2188 code = passivemode;
2189 }
2190
2191 void
2192 setepsv4(int argc, char *argv[])
2193 {
2194
2195 code = togglevar(argc, argv, &epsv4,
2196 verbose ? "EPSV/EPRT on IPv4" : NULL);
2197 epsv4bad = 0;
2198 }
2199
2200 void
2201 setsunique(int argc, char *argv[])
2202 {
2203
2204 code = togglevar(argc, argv, &sunique, "Store unique");
2205 }
2206
2207 void
2208 setrunique(int argc, char *argv[])
2209 {
2210
2211 code = togglevar(argc, argv, &runique, "Receive unique");
2212 }
2213
2214 int
2215 parserate(int argc, char *argv[], int cmdlineopt)
2216 {
2217 int dir, max, incr, showonly;
2218 sigfunc oldusr1, oldusr2;
2219
2220 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) {
2221 usage:
2222 if (cmdlineopt)
2223 fprintf(ttyout,
2224 "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n",
2225 argv[0]);
2226 else
2227 fprintf(ttyout,
2228 "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n",
2229 argv[0]);
2230 return -1;
2231 }
2232 dir = max = incr = showonly = 0;
2233 #define RATE_GET 1
2234 #define RATE_PUT 2
2235 #define RATE_ALL (RATE_GET | RATE_PUT)
2236
2237 if (strcasecmp(argv[1], "all") == 0)
2238 dir = RATE_ALL;
2239 else if (strcasecmp(argv[1], "get") == 0)
2240 dir = RATE_GET;
2241 else if (strcasecmp(argv[1], "put") == 0)
2242 dir = RATE_PUT;
2243 else
2244 goto usage;
2245
2246 if (argc >= 3) {
2247 if ((max = strsuftoi(argv[2])) < 0)
2248 goto usage;
2249 } else
2250 showonly = 1;
2251
2252 if (argc == 4) {
2253 if ((incr = strsuftoi(argv[3])) <= 0)
2254 goto usage;
2255 } else
2256 incr = DEFAULTINCR;
2257
2258 oldusr1 = xsignal(SIGUSR1, SIG_IGN);
2259 oldusr2 = xsignal(SIGUSR2, SIG_IGN);
2260 if (dir & RATE_GET) {
2261 if (!showonly) {
2262 rate_get = max;
2263 rate_get_incr = incr;
2264 }
2265 if (!cmdlineopt || verbose)
2266 fprintf(ttyout,
2267 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
2268 onoff(rate_get), rate_get, rate_get_incr);
2269 }
2270 if (dir & RATE_PUT) {
2271 if (!showonly) {
2272 rate_put = max;
2273 rate_put_incr = incr;
2274 }
2275 if (!cmdlineopt || verbose)
2276 fprintf(ttyout,
2277 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
2278 onoff(rate_put), rate_put, rate_put_incr);
2279 }
2280 (void)xsignal(SIGUSR1, oldusr1);
2281 (void)xsignal(SIGUSR2, oldusr2);
2282 return 0;
2283 }
2284
2285 void
2286 setrate(int argc, char *argv[])
2287 {
2288
2289 code = parserate(argc, argv, 0);
2290 }
2291
2292 /* change directory to parent directory */
2293 void
2294 cdup(int argc, char *argv[])
2295 {
2296 int r;
2297
2298 if (argc == 0) {
2299 fprintf(ttyout, "usage: %s\n", argv[0]);
2300 code = -1;
2301 return;
2302 }
2303 r = command("CDUP");
2304 if (r == ERROR && code == 500) {
2305 if (verbose)
2306 fputs("CDUP command not recognized, trying XCUP.\n",
2307 ttyout);
2308 r = command("XCUP");
2309 }
2310 if (r == COMPLETE) {
2311 dirchange = 1;
2312 updateremotepwd();
2313 }
2314 }
2315
2316 /*
2317 * Restart transfer at specific point
2318 */
2319 void
2320 restart(int argc, char *argv[])
2321 {
2322
2323 if (argc == 0 || argc > 2) {
2324 fprintf(ttyout, "usage: %s [restart-point]\n", argv[0]);
2325 code = -1;
2326 return;
2327 }
2328 if (! features[FEAT_REST_STREAM]) {
2329 fprintf(ttyout,
2330 "Restart is not supported by the remote server.\n");
2331 return;
2332 }
2333 if (argc == 2) {
2334 off_t rp;
2335 char *ep;
2336
2337 rp = STRTOLL(argv[1], &ep, 10);
2338 if (rp < 0 || *ep != '\0')
2339 fprintf(ttyout, "restart: Invalid offset `%s'\n",
2340 argv[1]);
2341 else
2342 restart_point = rp;
2343 }
2344 if (restart_point == 0)
2345 fputs("No restart point defined.\n", ttyout);
2346 else
2347 fprintf(ttyout,
2348 "Restarting at " LLF " for next get, put or append\n",
2349 (LLT)restart_point);
2350 }
2351
2352 /*
2353 * Show remote system type
2354 */
2355 void
2356 syst(int argc, char *argv[])
2357 {
2358 int oldverbose = verbose;
2359
2360 if (argc == 0) {
2361 fprintf(ttyout, "usage: %s\n", argv[0]);
2362 code = -1;
2363 return;
2364 }
2365 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2366 (void)command("SYST");
2367 verbose = oldverbose;
2368 }
2369
2370 void
2371 macdef(int argc, char *argv[])
2372 {
2373 char *tmp;
2374 int c;
2375
2376 if (argc == 0)
2377 goto usage;
2378 if (macnum == 16) {
2379 fputs("Limit of 16 macros have already been defined.\n",
2380 ttyout);
2381 code = -1;
2382 return;
2383 }
2384 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2385 usage:
2386 fprintf(ttyout, "usage: %s macro_name\n", argv[0]);
2387 code = -1;
2388 return;
2389 }
2390 if (interactive)
2391 fputs(
2392 "Enter macro line by line, terminating it with a null line.\n",
2393 ttyout);
2394 (void)strlcpy(macros[macnum].mac_name, argv[1],
2395 sizeof(macros[macnum].mac_name));
2396 if (macnum == 0)
2397 macros[macnum].mac_start = macbuf;
2398 else
2399 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2400 tmp = macros[macnum].mac_start;
2401 while (tmp != macbuf+4096) {
2402 if ((c = getchar()) == EOF) {
2403 fputs("macdef: end of file encountered.\n", ttyout);
2404 code = -1;
2405 return;
2406 }
2407 if ((*tmp = c) == '\n') {
2408 if (tmp == macros[macnum].mac_start) {
2409 macros[macnum++].mac_end = tmp;
2410 code = 0;
2411 return;
2412 }
2413 if (*(tmp-1) == '\0') {
2414 macros[macnum++].mac_end = tmp - 1;
2415 code = 0;
2416 return;
2417 }
2418 *tmp = '\0';
2419 }
2420 tmp++;
2421 }
2422 while (1) {
2423 while ((c = getchar()) != '\n' && c != EOF)
2424 /* LOOP */;
2425 if (c == EOF || getchar() == '\n') {
2426 fputs("Macro not defined - 4K buffer exceeded.\n",
2427 ttyout);
2428 code = -1;
2429 return;
2430 }
2431 }
2432 }
2433
2434 /*
2435 * Get size of file on remote machine
2436 */
2437 void
2438 sizecmd(int argc, char *argv[])
2439 {
2440 off_t size;
2441
2442 if (argc == 0 || argc > 2 ||
2443 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2444 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2445 code = -1;
2446 return;
2447 }
2448 size = remotesize(argv[1], 1);
2449 if (size != -1)
2450 fprintf(ttyout,
2451 "%s\t" LLF "\n", argv[1], (LLT)size);
2452 code = (size > 0);
2453 }
2454
2455 /*
2456 * Get last modification time of file on remote machine
2457 */
2458 void
2459 modtime(int argc, char *argv[])
2460 {
2461 time_t mtime;
2462
2463 if (argc == 0 || argc > 2 ||
2464 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2465 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2466 code = -1;
2467 return;
2468 }
2469 mtime = remotemodtime(argv[1], 1);
2470 if (mtime != -1)
2471 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
2472 code = (mtime > 0);
2473 }
2474
2475 /*
2476 * Show status on remote machine
2477 */
2478 void
2479 rmtstatus(int argc, char *argv[])
2480 {
2481
2482 if (argc == 0) {
2483 fprintf(ttyout, "usage: %s [remote-file]\n", argv[0]);
2484 code = -1;
2485 return;
2486 }
2487 COMMAND_1ARG(argc, argv, "STAT");
2488 }
2489
2490 /*
2491 * Get file if modtime is more recent than current file
2492 */
2493 void
2494 newer(int argc, char *argv[])
2495 {
2496
2497 if (getit(argc, argv, -1, "w"))
2498 fprintf(ttyout,
2499 "Local file \"%s\" is newer than remote file \"%s\".\n",
2500 argv[2], argv[1]);
2501 }
2502
2503 /*
2504 * Display one local file through $PAGER.
2505 */
2506 void
2507 lpage(int argc, char *argv[])
2508 {
2509 int len;
2510 char *p, *pager, *locfile;
2511
2512 if (argc == 0 || argc > 2 ||
2513 (argc == 1 && !another(&argc, &argv, "local-file"))) {
2514 fprintf(ttyout, "usage: %s local-file\n", argv[0]);
2515 code = -1;
2516 return;
2517 }
2518 if ((locfile = globulize(argv[1])) == NULL) {
2519 code = -1;
2520 return;
2521 }
2522 p = getoptionvalue("pager");
2523 if (EMPTYSTRING(p))
2524 p = DEFAULTPAGER;
2525 len = strlen(p) + strlen(locfile) + 2;
2526 pager = xmalloc(len);
2527 (void)strlcpy(pager, p, len);
2528 (void)strlcat(pager, " ", len);
2529 (void)strlcat(pager, locfile, len);
2530 system(pager);
2531 code = 0;
2532 (void)free(pager);
2533 (void)free(locfile);
2534 }
2535
2536 /*
2537 * Display one remote file through $PAGER.
2538 */
2539 void
2540 page(int argc, char *argv[])
2541 {
2542 int ohash, orestart_point, overbose, len;
2543 char *p, *pager;
2544
2545 if (argc == 0 || argc > 2 ||
2546 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2547 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2548 code = -1;
2549 return;
2550 }
2551 p = getoptionvalue("pager");
2552 if (EMPTYSTRING(p))
2553 p = DEFAULTPAGER;
2554 len = strlen(p) + 2;
2555 pager = xmalloc(len);
2556 pager[0] = '|';
2557 (void)strlcpy(pager + 1, p, len - 1);
2558
2559 ohash = hash;
2560 orestart_point = restart_point;
2561 overbose = verbose;
2562 hash = restart_point = verbose = 0;
2563 recvrequest("RETR", pager, argv[1], "r+", 1, 0);
2564 hash = ohash;
2565 restart_point = orestart_point;
2566 verbose = overbose;
2567 (void)free(pager);
2568 }
2569
2570 /*
2571 * Set the socket send or receive buffer size.
2572 */
2573 void
2574 setxferbuf(int argc, char *argv[])
2575 {
2576 int size, dir;
2577
2578 if (argc != 2) {
2579 usage:
2580 fprintf(ttyout, "usage: %s size\n", argv[0]);
2581 code = -1;
2582 return;
2583 }
2584 if (strcasecmp(argv[0], "sndbuf") == 0)
2585 dir = RATE_PUT;
2586 else if (strcasecmp(argv[0], "rcvbuf") == 0)
2587 dir = RATE_GET;
2588 else if (strcasecmp(argv[0], "xferbuf") == 0)
2589 dir = RATE_ALL;
2590 else
2591 goto usage;
2592
2593 if ((size = strsuftoi(argv[1])) == -1)
2594 goto usage;
2595
2596 if (size == 0) {
2597 fprintf(ttyout, "%s: size must be positive.\n", argv[0]);
2598 goto usage;
2599 }
2600
2601 if (dir & RATE_PUT)
2602 sndbuf_size = size;
2603 if (dir & RATE_GET)
2604 rcvbuf_size = size;
2605 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n",
2606 sndbuf_size, rcvbuf_size);
2607 code = 0;
2608 }
2609
2610 /*
2611 * Set or display options (defaults are provided by various env vars)
2612 */
2613 void
2614 setoption(int argc, char *argv[])
2615 {
2616 struct option *o;
2617
2618 code = -1;
2619 if (argc == 0 || (argc != 1 && argc != 3)) {
2620 fprintf(ttyout, "usage: %s [option value]\n", argv[0]);
2621 return;
2622 }
2623
2624 #define OPTIONINDENT ((int) sizeof("http_proxy"))
2625 if (argc == 1) {
2626 for (o = optiontab; o->name != NULL; o++) {
2627 fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT,
2628 o->name, o->value ? o->value : "");
2629 }
2630 } else {
2631 o = getoption(argv[1]);
2632 if (o == NULL) {
2633 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2634 return;
2635 }
2636 FREEPTR(o->value);
2637 o->value = xstrdup(argv[2]);
2638 if (verbose)
2639 fprintf(ttyout, "Setting `%s' to `%s'.\n",
2640 o->name, o->value);
2641 }
2642 code = 0;
2643 }
2644
2645 /*
2646 * Unset an option
2647 */
2648 void
2649 unsetoption(int argc, char *argv[])
2650 {
2651 struct option *o;
2652
2653 code = -1;
2654 if (argc == 0 || argc != 2) {
2655 fprintf(ttyout, "usage: %s option\n", argv[0]);
2656 return;
2657 }
2658
2659 o = getoption(argv[1]);
2660 if (o == NULL) {
2661 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2662 return;
2663 }
2664 FREEPTR(o->value);
2665 fprintf(ttyout, "Unsetting `%s'.\n", o->name);
2666 code = 0;
2667 }
2668
2669 /*
2670 * Display features supported by the remote host.
2671 */
2672 void
2673 feat(int argc, char *argv[])
2674 {
2675 int oldverbose = verbose;
2676
2677 if (argc == 0) {
2678 fprintf(ttyout, "usage: %s\n", argv[0]);
2679 code = -1;
2680 return;
2681 }
2682 if (! features[FEAT_FEAT]) {
2683 fprintf(ttyout,
2684 "FEAT is not supported by the remote server.\n");
2685 return;
2686 }
2687 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2688 (void)command("FEAT");
2689 verbose = oldverbose;
2690 }
2691
2692 void
2693 mlst(int argc, char *argv[])
2694 {
2695 int oldverbose = verbose;
2696
2697 if (argc < 1 || argc > 2) {
2698 fprintf(ttyout, "usage: %s [remote-path]\n", argv[0]);
2699 code = -1;
2700 return;
2701 }
2702 if (! features[FEAT_MLST]) {
2703 fprintf(ttyout,
2704 "MLST is not supported by the remote server.\n");
2705 return;
2706 }
2707 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2708 COMMAND_1ARG(argc, argv, "MLST");
2709 verbose = oldverbose;
2710 }
2711
2712 void
2713 opts(int argc, char *argv[])
2714 {
2715 int oldverbose = verbose;
2716
2717 if (argc < 2 || argc > 3) {
2718 fprintf(ttyout, "usage: %s command [options]\n", argv[0]);
2719 code = -1;
2720 return;
2721 }
2722 if (! features[FEAT_FEAT]) {
2723 fprintf(ttyout,
2724 "OPTS is not supported by the remote server.\n");
2725 return;
2726 }
2727 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2728 if (argc == 2)
2729 command("OPTS %s", argv[1]);
2730 else
2731 command("OPTS %s %s", argv[1], argv[2]);
2732 verbose = oldverbose;
2733 }
2734