1 /* $OpenBSD: cmds.c,v 1.85 2023/03/08 04:43:11 guenther Exp $ */
2 /* $NetBSD: cmds.c,v 1.27 1997/08/18 10:20:15 lukem Exp $ */
3
4 /*
5 * Copyright (C) 1997 and 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 /*
34 * Copyright (c) 1985, 1989, 1993, 1994
35 * The Regents of the University of California. All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 * 3. Neither the name of the University nor the names of its contributors
46 * may be used to endorse or promote products derived from this software
47 * without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 */
61
62 #ifndef SMALL
63
64 /*
65 * FTP User Program -- Command Routines.
66 */
67 #include <sys/types.h>
68 #include <sys/socket.h>
69 #include <sys/stat.h>
70 #include <sys/wait.h>
71 #include <arpa/ftp.h>
72
73 #include <ctype.h>
74 #include <err.h>
75 #include <fnmatch.h>
76 #include <glob.h>
77 #include <netdb.h>
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <unistd.h>
82 #include <errno.h>
83
84 #include "ftp_var.h"
85 #include "pathnames.h"
86 #include "cmds.h"
87
88 /*
89 * Set ascii transfer type.
90 */
91 void
setascii(int argc,char * argv[])92 setascii(int argc, char *argv[])
93 {
94
95 stype[1] = "ascii";
96 settype(2, stype);
97 }
98
99 /*
100 * Set file transfer mode.
101 */
102 void
setftmode(int argc,char * argv[])103 setftmode(int argc, char *argv[])
104 {
105
106 fprintf(ttyout, "We only support %s mode, sorry.\n", modename);
107 code = -1;
108 }
109
110 /*
111 * Set file transfer format.
112 */
113 void
setform(int argc,char * argv[])114 setform(int argc, char *argv[])
115 {
116
117 fprintf(ttyout, "We only support %s format, sorry.\n", formname);
118 code = -1;
119 }
120
121 /*
122 * Set file transfer structure.
123 */
124 void
setstruct(int argc,char * argv[])125 setstruct(int argc, char *argv[])
126 {
127
128 fprintf(ttyout, "We only support %s structure, sorry.\n", structname);
129 code = -1;
130 }
131
132 void
reput(int argc,char * argv[])133 reput(int argc, char *argv[])
134 {
135
136 (void)putit(argc, argv, 1);
137 }
138
139 void
put(int argc,char * argv[])140 put(int argc, char *argv[])
141 {
142
143 (void)putit(argc, argv, 0);
144 }
145
146 /*
147 * Send a single file.
148 */
149 void
putit(int argc,char * argv[],int restartit)150 putit(int argc, char *argv[], int restartit)
151 {
152 char *cmd;
153 int loc = 0;
154 char *oldargv1, *oldargv2;
155
156 if (argc == 2) {
157 argc++;
158 argv[2] = argv[1];
159 loc++;
160 }
161 if (argc < 2 && !another(&argc, &argv, "local-file"))
162 goto usage;
163 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
164 usage:
165 fprintf(ttyout, "usage: %s local-file [remote-file]\n",
166 argv[0]);
167 code = -1;
168 return;
169 }
170 oldargv1 = argv[1];
171 oldargv2 = argv[2];
172 if (!globulize(&argv[1])) {
173 code = -1;
174 return;
175 }
176 /*
177 * If "globulize" modifies argv[1], and argv[2] is a copy of
178 * the old argv[1], make it a copy of the new argv[1].
179 */
180 if (argv[1] != oldargv1 && argv[2] == oldargv1) {
181 argv[2] = argv[1];
182 }
183 if (restartit == 1) {
184 if (curtype != type)
185 changetype(type, 0);
186 restart_point = remotesize(argv[2], 1);
187 if (restart_point < 0) {
188 restart_point = 0;
189 code = -1;
190 return;
191 }
192 }
193 if (strcmp(argv[0], "append") == 0) {
194 restartit = 1;
195 }
196 cmd = restartit ? "APPE" : ((sunique) ? "STOU" : "STOR");
197 if (loc && ntflag) {
198 argv[2] = dotrans(argv[2]);
199 }
200 if (loc && mapflag) {
201 argv[2] = domap(argv[2]);
202 }
203 sendrequest(cmd, argv[1], argv[2],
204 argv[1] != oldargv1 || argv[2] != oldargv2);
205 restart_point = 0;
206 if (oldargv1 != argv[1]) /* free up after globulize() */
207 free(argv[1]);
208 }
209
210 /*
211 * Send multiple files.
212 */
213 void
mput(int argc,char * argv[])214 mput(int argc, char *argv[])
215 {
216 extern int optind, optreset;
217 int ch, i, restartit = 0;
218 sig_t oldintr;
219 char *cmd, *tp, *xargv[] = { argv[0], NULL, NULL };
220 const char *errstr;
221 static int depth = 0, max_depth = 0;
222
223 optind = optreset = 1;
224
225 if (depth)
226 depth++;
227
228 while ((ch = getopt(argc, argv, "cd:r")) != -1) {
229 switch(ch) {
230 case 'c':
231 restartit = 1;
232 break;
233 case 'd':
234 max_depth = strtonum(optarg, 0, INT_MAX, &errstr);
235 if (errstr != NULL) {
236 fprintf(ttyout, "bad depth value, %s: %s\n",
237 errstr, optarg);
238 code = -1;
239 return;
240 }
241 break;
242 case 'r':
243 depth = 1;
244 break;
245 default:
246 goto usage;
247 }
248 }
249
250 if (argc - optind < 1 && !another(&argc, &argv, "local-files")) {
251 usage:
252 fprintf(ttyout, "usage: %s [-cr] [-d depth] local-files\n",
253 argv[0]);
254 code = -1;
255 return;
256 }
257
258 argv[optind - 1] = argv[0];
259 argc -= optind - 1;
260 argv += optind - 1;
261
262 mname = argv[0];
263 mflag = 1;
264
265 oldintr = signal(SIGINT, mabort);
266 (void)setjmp(jabort);
267 if (proxy) {
268 char *cp, *tp2, tmpbuf[PATH_MAX];
269
270 while ((cp = remglob(argv, 0, NULL)) != NULL) {
271 if (*cp == '\0') {
272 mflag = 0;
273 continue;
274 }
275 if (mflag && confirm(argv[0], cp)) {
276 tp = cp;
277 if (mcase) {
278 while (*tp && !islower((unsigned char)*tp)) {
279 tp++;
280 }
281 if (!*tp) {
282 tp = cp;
283 tp2 = tmpbuf;
284 while ((*tp2 = *tp) != '\0') {
285 if (isupper((unsigned char)*tp2)) {
286 *tp2 =
287 tolower((unsigned char)*tp2);
288 }
289 tp++;
290 tp2++;
291 }
292 }
293 tp = tmpbuf;
294 }
295 if (ntflag) {
296 tp = dotrans(tp);
297 }
298 if (mapflag) {
299 tp = domap(tp);
300 }
301 if (restartit == 1) {
302 off_t ret;
303
304 if (curtype != type)
305 changetype(type, 0);
306 ret = remotesize(tp, 0);
307 restart_point = (ret < 0) ? 0 : ret;
308 }
309 cmd = restartit ? "APPE" : ((sunique) ?
310 "STOU" : "STOR");
311 sendrequest(cmd, cp, tp,
312 cp != tp || !interactive);
313 restart_point = 0;
314 if (!mflag && fromatty) {
315 if (confirm(argv[0], NULL))
316 mflag = 1;
317 }
318 }
319 }
320 (void)signal(SIGINT, oldintr);
321 mflag = 0;
322 return;
323 }
324
325 for (i = 1; i < argc; i++) {
326 char **cpp;
327 glob_t gl;
328 int flags;
329
330 /* Copy files without word expansion */
331 if (!doglob) {
332 if (mflag && confirm(argv[0], argv[i])) {
333 tp = (ntflag) ? dotrans(argv[i]) : argv[i];
334 tp = (mapflag) ? domap(tp) : tp;
335 if (restartit == 1) {
336 off_t ret;
337
338 if (curtype != type)
339 changetype(type, 0);
340 ret = remotesize(tp, 0);
341 restart_point = (ret < 0) ? 0 : ret;
342 }
343 cmd = restartit ? "APPE" : ((sunique) ?
344 "STOU" : "STOR");
345 sendrequest(cmd, argv[i], tp,
346 tp != argv[i] || !interactive);
347 restart_point = 0;
348 if (!mflag && fromatty) {
349 if (confirm(argv[0], NULL))
350 mflag = 1;
351 }
352 }
353 continue;
354 }
355
356 /* expanding file names */
357 memset(&gl, 0, sizeof(gl));
358 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
359 if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
360 warnx("%s: not found", argv[i]);
361 globfree(&gl);
362 continue;
363 }
364
365 /* traverse all expanded file names */
366 for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) {
367 struct stat filestat;
368
369 if (!mflag)
370 continue;
371 if (stat(*cpp, &filestat) != 0) {
372 warn("local: %s", *cpp);
373 continue;
374 }
375 if (S_ISDIR(filestat.st_mode) && depth == max_depth)
376 continue;
377 if (!confirm(argv[0], *cpp))
378 continue;
379
380 /*
381 * If file is a directory then create a new one
382 * at the remote machine.
383 */
384 if (S_ISDIR(filestat.st_mode)) {
385 xargv[1] = *cpp;
386 makedir(2, xargv);
387 cd(2, xargv);
388 if (dirchange != 1) {
389 warnx("remote: %s", *cpp);
390 continue;
391 }
392
393 if (chdir(*cpp) != 0) {
394 warn("local: %s", *cpp);
395 goto out;
396 }
397
398 /* Copy the whole directory recursively. */
399 xargv[1] = "*";
400 mput(2, xargv);
401
402 if (chdir("..") != 0) {
403 mflag = 0;
404 warn("local: %s", *cpp);
405 goto out;
406 }
407
408 out:
409 xargv[1] = "..";
410 cd(2, xargv);
411 if (dirchange != 1) {
412 warnx("remote: %s", *cpp);
413 mflag = 0;
414 }
415 continue;
416 }
417
418 tp = (ntflag) ? dotrans(*cpp) : *cpp;
419 tp = (mapflag) ? domap(tp) : tp;
420 if (restartit == 1) {
421 off_t ret;
422
423 if (curtype != type)
424 changetype(type, 0);
425 ret = remotesize(tp, 0);
426 restart_point = (ret < 0) ? 0 : ret;
427 }
428 cmd = restartit ? "APPE" : ((sunique) ?
429 "STOU" : "STOR");
430 sendrequest(cmd, *cpp, tp,
431 *cpp != tp || !interactive);
432 restart_point = 0;
433 if (!mflag && fromatty) {
434 if (confirm(argv[0], NULL))
435 mflag = 1;
436 }
437 }
438 globfree(&gl);
439 }
440
441 (void)signal(SIGINT, oldintr);
442
443 if (depth)
444 depth--;
445 if (depth == 0 || mflag == 0)
446 depth = max_depth = mflag = 0;
447 }
448
449 void
reget(int argc,char * argv[])450 reget(int argc, char *argv[])
451 {
452
453 (void)getit(argc, argv, 1, "a+w");
454 }
455
456 char *
onoff(int bool)457 onoff(int bool)
458 {
459
460 return (bool ? "on" : "off");
461 }
462
463 /*
464 * Show status.
465 */
466 void
status(int argc,char * argv[])467 status(int argc, char *argv[])
468 {
469 int i;
470
471 if (connected)
472 fprintf(ttyout, "Connected %sto %s.\n",
473 connected == -1 ? "and logged in" : "", hostname);
474 else
475 fputs("Not connected.\n", ttyout);
476 if (!proxy) {
477 pswitch(1);
478 if (connected) {
479 fprintf(ttyout, "Connected for proxy commands to %s.\n",
480 hostname);
481 }
482 else {
483 fputs("No proxy connection.\n", ttyout);
484 }
485 pswitch(0);
486 }
487 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
488 *gateserver ? gateserver : "(none)", gateport);
489 fprintf(ttyout, "Passive mode: %s.\n", onoff(passivemode));
490 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
491 modename, typename, formname, structname);
492 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
493 onoff(verbose), onoff(bell), onoff(interactive),
494 onoff(doglob));
495 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n", onoff(sunique),
496 onoff(runique));
497 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve));
498 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase), onoff(crflag));
499 if (ntflag) {
500 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout);
501 }
502 else {
503 fputs("Ntrans: off.\n", ttyout);
504 }
505 if (mapflag) {
506 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout);
507 }
508 else {
509 fputs("Nmap: off.\n", ttyout);
510 }
511 fprintf(ttyout, "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
512 onoff(hash), mark, onoff(progress));
513 fprintf(ttyout, "Use of PORT/LPRT cmds: %s.\n", onoff(sendport));
514 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4),
515 epsv4bad ? " (disabled for this connection)" : "");
516 fprintf(ttyout, "Command line editing: %s.\n", onoff(editing));
517 if (macnum > 0) {
518 fputs("Macros:\n", ttyout);
519 for (i=0; i<macnum; i++) {
520 fprintf(ttyout, "\t%s\n", macros[i].mac_name);
521 }
522 }
523 code = 0;
524 }
525
526 /*
527 * Toggle a variable
528 */
529 int
togglevar(int argc,char * argv[],int * var,const char * mesg)530 togglevar(int argc, char *argv[], int *var, const char *mesg)
531 {
532 if (argc < 2) {
533 *var = !*var;
534 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
535 *var = 1;
536 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
537 *var = 0;
538 } else {
539 fprintf(ttyout, "usage: %s [on | off]\n", argv[0]);
540 return (-1);
541 }
542 if (mesg)
543 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var));
544 return (*var);
545 }
546
547 /*
548 * Set beep on cmd completed mode.
549 */
550 void
setbell(int argc,char * argv[])551 setbell(int argc, char *argv[])
552 {
553
554 code = togglevar(argc, argv, &bell, "Bell mode");
555 }
556
557 /*
558 * Set command line editing
559 */
560 void
setedit(int argc,char * argv[])561 setedit(int argc, char *argv[])
562 {
563
564 code = togglevar(argc, argv, &editing, "Editing mode");
565 controlediting();
566 }
567
568 /*
569 * Toggle use of IPv4 EPSV/EPRT
570 */
571 void
setepsv4(int argc,char * argv[])572 setepsv4(int argc, char *argv[])
573 {
574
575 code = togglevar(argc, argv, &epsv4, "EPSV/EPRT on IPv4");
576 epsv4bad = 0;
577 }
578
579 /*
580 * Turn on packet tracing.
581 */
582 void
settrace(int argc,char * argv[])583 settrace(int argc, char *argv[])
584 {
585
586 code = togglevar(argc, argv, &trace, "Packet tracing");
587 }
588
589 /*
590 * Toggle hash mark printing during transfers, or set hash mark bytecount.
591 */
592 void
sethash(int argc,char * argv[])593 sethash(int argc, char *argv[])
594 {
595 if (argc == 1)
596 hash = !hash;
597 else if (argc != 2) {
598 fprintf(ttyout, "usage: %s [on | off | size]\n", argv[0]);
599 code = -1;
600 return;
601 } else if (strcasecmp(argv[1], "on") == 0)
602 hash = 1;
603 else if (strcasecmp(argv[1], "off") == 0)
604 hash = 0;
605 else {
606 int nmark;
607 const char *errstr;
608
609 nmark = strtonum(argv[1], 1, INT_MAX, &errstr);
610 if (errstr) {
611 fprintf(ttyout, "bytecount value is %s: %s\n",
612 errstr, argv[1]);
613 code = -1;
614 return;
615 }
616 mark = nmark;
617 hash = 1;
618 }
619 fprintf(ttyout, "Hash mark printing %s", onoff(hash));
620 if (hash)
621 fprintf(ttyout, " (%d bytes/hash mark)", mark);
622 fputs(".\n", ttyout);
623 code = hash;
624 }
625
626 /*
627 * Turn on printing of server echo's.
628 */
629 void
setverbose(int argc,char * argv[])630 setverbose(int argc, char *argv[])
631 {
632
633 code = togglevar(argc, argv, &verbose, "Verbose mode");
634 }
635
636 /*
637 * Toggle PORT/LPRT cmd use before each data connection.
638 */
639 void
setport(int argc,char * argv[])640 setport(int argc, char *argv[])
641 {
642
643 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds");
644 }
645
646 /*
647 * Toggle transfer progress bar.
648 */
649 void
setprogress(int argc,char * argv[])650 setprogress(int argc, char *argv[])
651 {
652
653 code = togglevar(argc, argv, &progress, "Progress bar");
654 }
655
656 /*
657 * Turn on interactive prompting during mget, mput, and mdelete.
658 */
659 void
setprompt(int argc,char * argv[])660 setprompt(int argc, char *argv[])
661 {
662
663 code = togglevar(argc, argv, &interactive, "Interactive mode");
664 }
665
666 /*
667 * Toggle gate-ftp mode, or set gate-ftp server
668 */
669 void
setgate(int argc,char * argv[])670 setgate(int argc, char *argv[])
671 {
672 static char gsbuf[HOST_NAME_MAX+1];
673
674 if (argc > 3) {
675 fprintf(ttyout, "usage: %s [on | off | host [port]]\n",
676 argv[0]);
677 code = -1;
678 return;
679 } else if (argc < 2) {
680 gatemode = !gatemode;
681 } else {
682 if (argc == 2 && strcasecmp(argv[1], "on") == 0)
683 gatemode = 1;
684 else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
685 gatemode = 0;
686 else {
687 if (argc == 3) {
688 gateport = strdup(argv[2]);
689 if (gateport == NULL)
690 err(1, NULL);
691 }
692 strlcpy(gsbuf, argv[1], sizeof(gsbuf));
693 gateserver = gsbuf;
694 gatemode = 1;
695 }
696 }
697 if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
698 fprintf(ttyout,
699 "Disabling gate-ftp mode - no gate-ftp server defined.\n");
700 gatemode = 0;
701 } else {
702 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n",
703 onoff(gatemode),
704 *gateserver ? gateserver : "(none)", gateport);
705 }
706 code = gatemode;
707 }
708
709 /*
710 * Toggle metacharacter interpretation on local file names.
711 */
712 void
setglob(int argc,char * argv[])713 setglob(int argc, char *argv[])
714 {
715
716 code = togglevar(argc, argv, &doglob, "Globbing");
717 }
718
719 /*
720 * Toggle preserving modification times on retrieved files.
721 */
722 void
setpreserve(int argc,char * argv[])723 setpreserve(int argc, char *argv[])
724 {
725
726 code = togglevar(argc, argv, &preserve, "Preserve modification times");
727 }
728
729 /*
730 * Set debugging mode on/off and/or set level of debugging.
731 */
732 void
setdebug(int argc,char * argv[])733 setdebug(int argc, char *argv[])
734 {
735 if (argc > 2) {
736 fprintf(ttyout, "usage: %s [on | off | debuglevel]\n", argv[0]);
737 code = -1;
738 return;
739 } else if (argc == 2) {
740 if (strcasecmp(argv[1], "on") == 0)
741 debug = 1;
742 else if (strcasecmp(argv[1], "off") == 0)
743 debug = 0;
744 else {
745 const char *errstr;
746 int val;
747
748 val = strtonum(argv[1], 0, INT_MAX, &errstr);
749 if (errstr) {
750 fprintf(ttyout, "debugging value is %s: %s\n",
751 errstr, argv[1]);
752 code = -1;
753 return;
754 }
755 debug = val;
756 }
757 } else
758 debug = !debug;
759 if (debug)
760 options |= SO_DEBUG;
761 else
762 options &= ~SO_DEBUG;
763 fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug);
764 code = debug > 0;
765 }
766
767 /*
768 * Set current working directory on local machine.
769 */
770 void
lcd(int argc,char * argv[])771 lcd(int argc, char *argv[])
772 {
773 char buf[PATH_MAX];
774 char *oldargv1;
775
776 if (argc < 2)
777 argc++, argv[1] = home;
778 if (argc != 2) {
779 fprintf(ttyout, "usage: %s [local-directory]\n", argv[0]);
780 code = -1;
781 return;
782 }
783 oldargv1 = argv[1];
784 if (!globulize(&argv[1])) {
785 code = -1;
786 return;
787 }
788 if (chdir(argv[1]) == -1) {
789 warn("local: %s", argv[1]);
790 code = -1;
791 } else {
792 if (getcwd(buf, sizeof(buf)) != NULL)
793 fprintf(ttyout, "Local directory now %s\n", buf);
794 else
795 warn("getcwd: %s", argv[1]);
796 code = 0;
797 }
798 if (oldargv1 != argv[1]) /* free up after globulize() */
799 free(argv[1]);
800 }
801
802 /*
803 * Delete a single file.
804 */
805 void
deletecmd(int argc,char * argv[])806 deletecmd(int argc, char *argv[])
807 {
808
809 if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) {
810 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
811 code = -1;
812 return;
813 }
814 (void)command("DELE %s", argv[1]);
815 }
816
817 /*
818 * Delete multiple files.
819 */
820 void
mdelete(int argc,char * argv[])821 mdelete(int argc, char *argv[])
822 {
823 sig_t oldintr;
824 char *cp;
825
826 if (argc < 2 && !another(&argc, &argv, "remote-files")) {
827 fprintf(ttyout, "usage: %s remote-files\n", argv[0]);
828 code = -1;
829 return;
830 }
831 mname = argv[0];
832 mflag = 1;
833 oldintr = signal(SIGINT, mabort);
834 (void)setjmp(jabort);
835 while ((cp = remglob(argv, 0, NULL)) != NULL) {
836 if (*cp == '\0') {
837 mflag = 0;
838 continue;
839 }
840 if (mflag && confirm(argv[0], cp)) {
841 (void)command("DELE %s", cp);
842 if (!mflag && fromatty) {
843 if (confirm(argv[0], NULL))
844 mflag = 1;
845 }
846 }
847 }
848 (void)signal(SIGINT, oldintr);
849 mflag = 0;
850 }
851
852 /*
853 * Rename a remote file.
854 */
855 void
renamefile(int argc,char * argv[])856 renamefile(int argc, char *argv[])
857 {
858
859 if (argc < 2 && !another(&argc, &argv, "from-name"))
860 goto usage;
861 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
862 usage:
863 fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]);
864 code = -1;
865 return;
866 }
867 if (command("RNFR %s", argv[1]) == CONTINUE)
868 (void)command("RNTO %s", argv[2]);
869 }
870
871 /*
872 * Get a directory listing of remote files.
873 */
874 void
ls(int argc,char * argv[])875 ls(int argc, char *argv[])
876 {
877 const char *cmd;
878 char *oldargv2, *globargv2;
879
880 if (argc < 2)
881 argc++, argv[1] = NULL;
882 if (argc < 3)
883 argc++, argv[2] = "-";
884 if (argc > 3) {
885 fprintf(ttyout, "usage: %s [remote-directory [local-file]]\n",
886 argv[0]);
887 code = -1;
888 return;
889 }
890 cmd = strcmp(argv[0], "nlist") == 0 ? "NLST" : "LIST";
891 oldargv2 = argv[2];
892 if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
893 code = -1;
894 return;
895 }
896 globargv2 = argv[2];
897 if (strcmp(argv[2], "-") && *argv[2] != '|' && (!globulize(&argv[2]) ||
898 !confirm("output to local-file:", argv[2]))) {
899 code = -1;
900 goto freels;
901 }
902 recvrequest(cmd, argv[2], argv[1], "w", 0, 0);
903
904 /* flush results in case commands are coming from a pipe */
905 fflush(ttyout);
906 freels:
907 if (argv[2] != globargv2) /* free up after globulize() */
908 free(argv[2]);
909 if (globargv2 != oldargv2)
910 free(globargv2);
911 }
912
913 /*
914 * Get a directory listing of multiple remote files.
915 */
916 void
mls(int argc,char * argv[])917 mls(int argc, char *argv[])
918 {
919 sig_t oldintr;
920 int i;
921 char lmode[1], *dest, *odest;
922
923 if (argc < 2 && !another(&argc, &argv, "remote-files"))
924 goto usage;
925 if (argc < 3 && !another(&argc, &argv, "local-file")) {
926 usage:
927 fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]);
928 code = -1;
929 return;
930 }
931 odest = dest = argv[argc - 1];
932 argv[argc - 1] = NULL;
933 if (strcmp(dest, "-") && *dest != '|')
934 if (!globulize(&dest) ||
935 !confirm("output to local-file:", dest)) {
936 code = -1;
937 return;
938 }
939 mname = argv[0];
940 mflag = 1;
941 oldintr = signal(SIGINT, mabort);
942 (void)setjmp(jabort);
943 for (i = 1; mflag && i < argc-1; ++i) {
944 *lmode = (i == 1) ? 'w' : 'a';
945 recvrequest("LIST", dest, argv[i], lmode, 0, 0);
946 if (!mflag && fromatty) {
947 if (confirm(argv[0], NULL))
948 mflag ++;
949 }
950 }
951 (void)signal(SIGINT, oldintr);
952 mflag = 0;
953 if (dest != odest) /* free up after globulize() */
954 free(dest);
955 }
956
957 /*
958 * Do a shell escape
959 */
960 void
shell(int argc,char * argv[])961 shell(int argc, char *argv[])
962 {
963 pid_t pid;
964 sig_t old1, old2;
965 char shellnam[PATH_MAX], *shellp, *namep;
966 int wait_status;
967
968 old1 = signal (SIGINT, SIG_IGN);
969 old2 = signal (SIGQUIT, SIG_IGN);
970 if ((pid = fork()) == 0) {
971 (void)closefrom(3);
972 (void)signal(SIGINT, SIG_DFL);
973 (void)signal(SIGQUIT, SIG_DFL);
974 shellp = getenv("SHELL");
975 if (shellp == NULL || *shellp == '\0')
976 shellp = _PATH_BSHELL;
977 namep = strrchr(shellp, '/');
978 if (namep == NULL)
979 namep = shellp;
980 shellnam[0] = '-';
981 (void)strlcpy(shellnam + 1, ++namep, sizeof(shellnam) - 1);
982 if (strcmp(namep, "sh") != 0)
983 shellnam[0] = '+';
984 if (debug) {
985 fputs(shellp, ttyout);
986 fputc('\n', ttyout);
987 (void)fflush(ttyout);
988 }
989 if (argc > 1) {
990 execl(shellp, shellnam, "-c", altarg, (char *)NULL);
991 }
992 else {
993 execl(shellp, shellnam, (char *)NULL);
994 }
995 warn("%s", shellp);
996 code = -1;
997 exit(1);
998 }
999 if (pid > 0)
1000 while (wait(&wait_status) != pid)
1001 ;
1002 (void)signal(SIGINT, old1);
1003 (void)signal(SIGQUIT, old2);
1004 if (pid == -1) {
1005 warn("Try again later");
1006 code = -1;
1007 }
1008 else {
1009 code = 0;
1010 }
1011 }
1012
1013 /*
1014 * Send new user information (re-login)
1015 */
1016 void
user(int argc,char * argv[])1017 user(int argc, char *argv[])
1018 {
1019 char acctname[80];
1020 int n, aflag = 0;
1021
1022 if (argc < 2)
1023 (void)another(&argc, &argv, "username");
1024 if (argc < 2 || argc > 4) {
1025 fprintf(ttyout, "usage: %s username [password [account]]\n",
1026 argv[0]);
1027 code = -1;
1028 return;
1029 }
1030 n = command("USER %s", argv[1]);
1031 if (n == CONTINUE) {
1032 if (argc < 3 )
1033 argv[2] = getpass("Password:"), argc++;
1034 n = command("PASS %s", argv[2]);
1035 }
1036 if (n == CONTINUE) {
1037 if (argc < 4) {
1038 (void)fputs("Account: ", ttyout);
1039 (void)fflush(ttyout);
1040 if (fgets(acctname, sizeof(acctname), stdin) == NULL) {
1041 clearerr(stdin);
1042 goto fail;
1043 }
1044
1045 acctname[strcspn(acctname, "\n")] = '\0';
1046
1047 argv[3] = acctname;
1048 argc++;
1049 }
1050 n = command("ACCT %s", argv[3]);
1051 aflag++;
1052 }
1053 if (n != COMPLETE) {
1054 fail:
1055 fputs("Login failed.\n", ttyout);
1056 return;
1057 }
1058 if (!aflag && argc == 4) {
1059 (void)command("ACCT %s", argv[3]);
1060 }
1061 connected = -1;
1062 }
1063
1064 /*
1065 * Print working directory on remote machine.
1066 */
1067 void
pwd(int argc,char * argv[])1068 pwd(int argc, char *argv[])
1069 {
1070 int oldverbose = verbose;
1071
1072 /*
1073 * If we aren't verbose, this doesn't do anything!
1074 */
1075 verbose = 1;
1076 if (command("PWD") == ERROR && code == 500) {
1077 fputs("PWD command not recognized, trying XPWD.\n", ttyout);
1078 (void)command("XPWD");
1079 }
1080 verbose = oldverbose;
1081 }
1082
1083 /*
1084 * Print working directory on local machine.
1085 */
1086 void
lpwd(int argc,char * argv[])1087 lpwd(int argc, char *argv[])
1088 {
1089 char buf[PATH_MAX];
1090
1091 if (getcwd(buf, sizeof(buf)) != NULL)
1092 fprintf(ttyout, "Local directory %s\n", buf);
1093 else
1094 warn("getcwd");
1095 code = 0;
1096 }
1097
1098 /*
1099 * Make a directory.
1100 */
1101 void
makedir(int argc,char * argv[])1102 makedir(int argc, char *argv[])
1103 {
1104
1105 if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1106 argc > 2) {
1107 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1108 code = -1;
1109 return;
1110 }
1111 if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1112 if (verbose)
1113 fputs("MKD command not recognized, trying XMKD.\n", ttyout);
1114 (void)command("XMKD %s", argv[1]);
1115 }
1116 }
1117
1118 /*
1119 * Remove a directory.
1120 */
1121 void
removedir(int argc,char * argv[])1122 removedir(int argc, char *argv[])
1123 {
1124
1125 if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1126 argc > 2) {
1127 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1128 code = -1;
1129 return;
1130 }
1131 if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1132 if (verbose)
1133 fputs("RMD command not recognized, trying XRMD.\n", ttyout);
1134 (void)command("XRMD %s", argv[1]);
1135 }
1136 }
1137
1138 /*
1139 * Send a line, verbatim, to the remote machine.
1140 */
1141 void
quote(int argc,char * argv[])1142 quote(int argc, char *argv[])
1143 {
1144
1145 if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1146 fprintf(ttyout, "usage: %s arg ...\n", argv[0]);
1147 code = -1;
1148 return;
1149 }
1150 quote1("", argc, argv);
1151 }
1152
1153 /*
1154 * Send a SITE command to the remote machine. The line
1155 * is sent verbatim to the remote machine, except that the
1156 * word "SITE" is added at the front.
1157 */
1158 void
site(int argc,char * argv[])1159 site(int argc, char *argv[])
1160 {
1161
1162 if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1163 fprintf(ttyout, "usage: %s arg ...\n", argv[0]);
1164 code = -1;
1165 return;
1166 }
1167 quote1("SITE", argc, argv);
1168 }
1169
1170 /*
1171 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1172 * Send the result as a one-line command and get response.
1173 */
1174 void
quote1(const char * initial,int argc,char * argv[])1175 quote1(const char *initial, int argc, char *argv[])
1176 {
1177 int i, len;
1178 char buf[BUFSIZ]; /* must be >= sizeof(line) */
1179
1180 (void)strlcpy(buf, initial, sizeof(buf));
1181 if (argc > 1) {
1182 for (i = 1, len = strlen(buf); i < argc && len < sizeof(buf)-1; i++) {
1183 /* Space for next arg */
1184 if (len > 1)
1185 buf[len++] = ' ';
1186
1187 /* Sanity check */
1188 if (len >= sizeof(buf) - 1)
1189 break;
1190
1191 /* Copy next argument, NUL terminate always */
1192 strlcpy(&buf[len], argv[i], sizeof(buf) - len);
1193
1194 /* Update string length */
1195 len = strlen(buf);
1196 }
1197 }
1198
1199 /* Make double (triple?) sure the sucker is NUL terminated */
1200 buf[sizeof(buf) - 1] = '\0';
1201
1202 if (command("%s", buf) == PRELIM) {
1203 while (getreply(0) == PRELIM)
1204 continue;
1205 }
1206 }
1207
1208 void
do_chmod(int argc,char * argv[])1209 do_chmod(int argc, char *argv[])
1210 {
1211
1212 if (argc < 2 && !another(&argc, &argv, "mode"))
1213 goto usage;
1214 if ((argc < 3 && !another(&argc, &argv, "file")) || argc > 3) {
1215 usage:
1216 fprintf(ttyout, "usage: %s mode file\n", argv[0]);
1217 code = -1;
1218 return;
1219 }
1220 (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1221 }
1222
1223 void
do_umask(int argc,char * argv[])1224 do_umask(int argc, char *argv[])
1225 {
1226 int oldverbose = verbose;
1227
1228 verbose = 1;
1229 (void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1230 verbose = oldverbose;
1231 }
1232
1233 void
idle(int argc,char * argv[])1234 idle(int argc, char *argv[])
1235 {
1236 int oldverbose = verbose;
1237
1238 verbose = 1;
1239 (void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1240 verbose = oldverbose;
1241 }
1242
1243 /*
1244 * Ask the other side for help.
1245 */
1246 void
rmthelp(int argc,char * argv[])1247 rmthelp(int argc, char *argv[])
1248 {
1249 int oldverbose = verbose;
1250
1251 verbose = 1;
1252 (void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1253 verbose = oldverbose;
1254 }
1255
1256 /*
1257 * Terminate session and exit.
1258 */
1259 void
quit(int argc,char * argv[])1260 quit(int argc, char *argv[])
1261 {
1262
1263 if (connected)
1264 disconnect(0, 0);
1265 pswitch(1);
1266 if (connected) {
1267 disconnect(0, 0);
1268 }
1269 exit(0);
1270 }
1271
1272 void
account(int argc,char * argv[])1273 account(int argc, char *argv[])
1274 {
1275 char *ap;
1276
1277 if (argc > 2) {
1278 fprintf(ttyout, "usage: %s [password]\n", argv[0]);
1279 code = -1;
1280 return;
1281 }
1282 else if (argc == 2)
1283 ap = argv[1];
1284 else
1285 ap = getpass("Account:");
1286 (void)command("ACCT %s", ap);
1287 }
1288
1289 jmp_buf abortprox;
1290
1291 void
proxabort(int signo)1292 proxabort(int signo)
1293 {
1294 int save_errno = errno;
1295
1296 alarmtimer(0);
1297 if (!proxy) {
1298 pswitch(1);
1299 }
1300 if (connected) {
1301 proxflag = 1;
1302 }
1303 else {
1304 proxflag = 0;
1305 }
1306 pswitch(0);
1307 errno = save_errno;
1308 longjmp(abortprox, 1);
1309 }
1310
1311 void
doproxy(int argc,char * argv[])1312 doproxy(int argc, char *argv[])
1313 {
1314 struct cmd *c;
1315 int cmdpos;
1316 sig_t oldintr;
1317
1318 if (argc < 2 && !another(&argc, &argv, "command")) {
1319 fprintf(ttyout, "usage: %s command\n", argv[0]);
1320 code = -1;
1321 return;
1322 }
1323 c = getcmd(argv[1]);
1324 if (c == (struct cmd *) -1) {
1325 fputs("?Ambiguous command.\n", ttyout);
1326 (void)fflush(ttyout);
1327 code = -1;
1328 return;
1329 }
1330 if (c == 0) {
1331 fputs("?Invalid command.\n", ttyout);
1332 (void)fflush(ttyout);
1333 code = -1;
1334 return;
1335 }
1336 if (!c->c_proxy) {
1337 fputs("?Invalid proxy command.\n", ttyout);
1338 (void)fflush(ttyout);
1339 code = -1;
1340 return;
1341 }
1342 if (setjmp(abortprox)) {
1343 code = -1;
1344 return;
1345 }
1346 oldintr = signal(SIGINT, proxabort);
1347 pswitch(1);
1348 if (c->c_conn && !connected) {
1349 fputs("Not connected.\n", ttyout);
1350 (void)fflush(ttyout);
1351 pswitch(0);
1352 (void)signal(SIGINT, oldintr);
1353 code = -1;
1354 return;
1355 }
1356 cmdpos = strcspn(line, " \t");
1357 if (cmdpos > 0) /* remove leading "proxy " from input buffer */
1358 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1359 (*c->c_handler)(argc-1, argv+1);
1360 if (connected) {
1361 proxflag = 1;
1362 }
1363 else {
1364 proxflag = 0;
1365 }
1366 pswitch(0);
1367 (void)signal(SIGINT, oldintr);
1368 }
1369
1370 void
setcase(int argc,char * argv[])1371 setcase(int argc, char *argv[])
1372 {
1373
1374 code = togglevar(argc, argv, &mcase, "Case mapping");
1375 }
1376
1377 void
setcr(int argc,char * argv[])1378 setcr(int argc, char *argv[])
1379 {
1380
1381 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1382 }
1383
1384 void
setntrans(int argc,char * argv[])1385 setntrans(int argc, char *argv[])
1386 {
1387 if (argc == 1) {
1388 ntflag = 0;
1389 fputs("Ntrans off.\n", ttyout);
1390 code = ntflag;
1391 return;
1392 }
1393 ntflag++;
1394 code = ntflag;
1395 (void)strlcpy(ntin, argv[1], sizeof(ntin));
1396 if (argc == 2) {
1397 ntout[0] = '\0';
1398 return;
1399 }
1400 (void)strlcpy(ntout, argv[2], sizeof(ntout));
1401 }
1402
1403 void
setnmap(int argc,char * argv[])1404 setnmap(int argc, char *argv[])
1405 {
1406 char *cp;
1407
1408 if (argc == 1) {
1409 mapflag = 0;
1410 fputs("Nmap off.\n", ttyout);
1411 code = mapflag;
1412 return;
1413 }
1414 if ((argc < 3 && !another(&argc, &argv, "outpattern")) || argc > 3) {
1415 fprintf(ttyout, "usage: %s [inpattern outpattern]\n", argv[0]);
1416 code = -1;
1417 return;
1418 }
1419 mapflag = 1;
1420 code = 1;
1421 cp = strchr(altarg, ' ');
1422 if (proxy) {
1423 while(*++cp == ' ')
1424 continue;
1425 altarg = cp;
1426 cp = strchr(altarg, ' ');
1427 }
1428 *cp = '\0';
1429 (void)strncpy(mapin, altarg, PATH_MAX - 1);
1430 while (*++cp == ' ')
1431 continue;
1432 (void)strncpy(mapout, cp, PATH_MAX - 1);
1433 }
1434
1435 void
setpassive(int argc,char * argv[])1436 setpassive(int argc, char *argv[])
1437 {
1438
1439 code = togglevar(argc, argv, &passivemode,
1440 verbose ? "Passive mode" : NULL);
1441 }
1442
1443 void
setsunique(int argc,char * argv[])1444 setsunique(int argc, char *argv[])
1445 {
1446
1447 code = togglevar(argc, argv, &sunique, "Store unique");
1448 }
1449
1450 void
setrunique(int argc,char * argv[])1451 setrunique(int argc, char *argv[])
1452 {
1453
1454 code = togglevar(argc, argv, &runique, "Receive unique");
1455 }
1456
1457 /* change directory to parent directory */
1458 void
cdup(int argc,char * argv[])1459 cdup(int argc, char *argv[])
1460 {
1461 int r;
1462
1463 r = command("CDUP");
1464 if (r == ERROR && code == 500) {
1465 if (verbose)
1466 fputs("CDUP command not recognized, trying XCUP.\n", ttyout);
1467 r = command("XCUP");
1468 }
1469 if (r == COMPLETE)
1470 dirchange = 1;
1471 }
1472
1473 /*
1474 * Restart transfer at specific point
1475 */
1476 void
restart(int argc,char * argv[])1477 restart(int argc, char *argv[])
1478 {
1479 off_t nrestart_point;
1480 char *ep;
1481
1482 if (argc != 2)
1483 fputs("restart: offset not specified.\n", ttyout);
1484 else {
1485 nrestart_point = strtoll(argv[1], &ep, 10);
1486 if (nrestart_point == LLONG_MAX || *ep != '\0')
1487 fputs("restart: invalid offset.\n", ttyout);
1488 else {
1489 fprintf(ttyout, "Restarting at %lld. Execute get, put "
1490 "or append to initiate transfer\n",
1491 (long long)nrestart_point);
1492 restart_point = nrestart_point;
1493 }
1494 }
1495 }
1496
1497 /*
1498 * Show remote system type
1499 */
1500 void
syst(int argc,char * argv[])1501 syst(int argc, char *argv[])
1502 {
1503
1504 (void)command("SYST");
1505 }
1506
1507 void
macdef(int argc,char * argv[])1508 macdef(int argc, char *argv[])
1509 {
1510 char *tmp;
1511 int c;
1512
1513 if (macnum == 16) {
1514 fputs("Limit of 16 macros have already been defined.\n", ttyout);
1515 code = -1;
1516 return;
1517 }
1518 if ((argc < 2 && !another(&argc, &argv, "macro-name")) || argc > 2) {
1519 fprintf(ttyout, "usage: %s macro-name\n", argv[0]);
1520 code = -1;
1521 return;
1522 }
1523 if (interactive)
1524 fputs(
1525 "Enter macro line by line, terminating it with a null line.\n", ttyout);
1526 (void)strlcpy(macros[macnum].mac_name, argv[1],
1527 sizeof(macros[macnum].mac_name));
1528 if (macnum == 0)
1529 macros[macnum].mac_start = macbuf;
1530 else
1531 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
1532 tmp = macros[macnum].mac_start;
1533 while (tmp != macbuf+4096) {
1534 if ((c = getchar()) == EOF) {
1535 fputs("macdef: end of file encountered.\n", ttyout);
1536 code = -1;
1537 return;
1538 }
1539 if ((*tmp = c) == '\n') {
1540 if (tmp == macros[macnum].mac_start) {
1541 macros[macnum++].mac_end = tmp;
1542 code = 0;
1543 return;
1544 }
1545 if (*(tmp-1) == '\0') {
1546 macros[macnum++].mac_end = tmp - 1;
1547 code = 0;
1548 return;
1549 }
1550 *tmp = '\0';
1551 }
1552 tmp++;
1553 }
1554 while (1) {
1555 while ((c = getchar()) != '\n' && c != EOF)
1556 /* LOOP */;
1557 if (c == EOF || getchar() == '\n') {
1558 fputs("Macro not defined - 4K buffer exceeded.\n", ttyout);
1559 code = -1;
1560 return;
1561 }
1562 }
1563 }
1564
1565 /*
1566 * Get size of file on remote machine
1567 */
1568 void
sizecmd(int argc,char * argv[])1569 sizecmd(int argc, char *argv[])
1570 {
1571 off_t size;
1572
1573 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) {
1574 fprintf(ttyout, "usage: %s file\n", argv[0]);
1575 code = -1;
1576 return;
1577 }
1578 size = remotesize(argv[1], 1);
1579 if (size != -1)
1580 fprintf(ttyout, "%s\t%lld\n", argv[1], (long long)size);
1581 code = size;
1582 }
1583
1584 /*
1585 * Get last modification time of file on remote machine
1586 */
1587 void
modtime(int argc,char * argv[])1588 modtime(int argc, char *argv[])
1589 {
1590 time_t mtime;
1591
1592 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) {
1593 fprintf(ttyout, "usage: %s file\n", argv[0]);
1594 code = -1;
1595 return;
1596 }
1597 mtime = remotemodtime(argv[1], 1);
1598 if (mtime != -1)
1599 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
1600 code = mtime;
1601 }
1602
1603 /*
1604 * Show status on remote machine
1605 */
1606 void
rmtstatus(int argc,char * argv[])1607 rmtstatus(int argc, char *argv[])
1608 {
1609
1610 (void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
1611 }
1612
1613 /*
1614 * Get file if modtime is more recent than current file
1615 */
1616 void
newer(int argc,char * argv[])1617 newer(int argc, char *argv[])
1618 {
1619
1620 (void)getit(argc, argv, -1, "w");
1621 }
1622
1623 /*
1624 * Display one file through $PAGER (defaults to "more").
1625 */
1626 void
page(int argc,char * argv[])1627 page(int argc, char *argv[])
1628 {
1629 off_t orestart_point;
1630 int ohash, overbose;
1631 char *p, *pager, *oldargv1;
1632
1633 if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) {
1634 fprintf(ttyout, "usage: %s file\n", argv[0]);
1635 code = -1;
1636 return;
1637 }
1638 oldargv1 = argv[1];
1639 if (!globulize(&argv[1])) {
1640 code = -1;
1641 return;
1642 }
1643 p = getenv("PAGER");
1644 if (p == NULL || (*p == '\0'))
1645 p = PAGER;
1646 if (asprintf(&pager, "|%s", p) == -1)
1647 errx(1, "Can't allocate memory for $PAGER");
1648
1649 orestart_point = restart_point;
1650 ohash = hash;
1651 overbose = verbose;
1652 restart_point = hash = verbose = 0;
1653 recvrequest("RETR", pager, argv[1], "r+w", 1, 0);
1654 (void)free(pager);
1655 restart_point = orestart_point;
1656 hash = ohash;
1657 verbose = overbose;
1658 if (oldargv1 != argv[1]) /* free up after globulize() */
1659 free(argv[1]);
1660 }
1661
1662 #endif /* !SMALL */
1663
1664