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