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