cmds.c revision 90926
1/*
2 * Copyright (c) 1985, 1989, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/*
35 * FTP User Program -- Command Routines.
36 */
37
38#include "ftp_locl.h"
39RCSID("$Id: cmds.c,v 1.44 2001/08/05 06:39:14 assar Exp $");
40
41typedef void (*sighand)(int);
42
43jmp_buf	jabort;
44char   *mname;
45char   *home = "/";
46
47/*
48 * `Another' gets another argument, and stores the new argc and argv.
49 * It reverts to the top level (via main.c's intr()) on EOF/error.
50 *
51 * Returns false if no new arguments have been added.
52 */
53int
54another(int *pargc, char ***pargv, char *prompt)
55{
56	int len = strlen(line), ret;
57
58	if (len >= sizeof(line) - 3) {
59		printf("sorry, arguments too long\n");
60		intr(0);
61	}
62	printf("(%s) ", prompt);
63	line[len++] = ' ';
64	if (fgets(&line[len], sizeof(line) - len, stdin) == NULL)
65		intr(0);
66	len += strlen(&line[len]);
67	if (len > 0 && line[len - 1] == '\n')
68		line[len - 1] = '\0';
69	makeargv();
70	ret = margc > *pargc;
71	*pargc = margc;
72	*pargv = margv;
73	return (ret);
74}
75
76/*
77 * Connect to peer server and
78 * auto-login, if possible.
79 */
80void
81setpeer(int argc, char **argv)
82{
83	char *host;
84	u_short port;
85	struct servent *sp;
86
87	if (connected) {
88		printf("Already connected to %s, use close first.\n",
89			hostname);
90		code = -1;
91		return;
92	}
93	if (argc < 2)
94		another(&argc, &argv, "to");
95	if (argc < 2 || argc > 3) {
96		printf("usage: %s host-name [port]\n", argv[0]);
97		code = -1;
98		return;
99	}
100	sp = getservbyname("ftp", "tcp");
101	if (sp == NULL)
102		errx(1, "You bastard. You removed ftp/tcp from services");
103	port = sp->s_port;
104	if (argc > 2) {
105		sp = getservbyname(argv[2], "tcp");
106		if (sp != NULL) {
107			port = sp->s_port;
108		} else {
109			char *ep;
110
111			port = strtol(argv[2], &ep, 0);
112			if (argv[2] == ep) {
113				printf("%s: bad port number-- %s\n",
114				       argv[1], argv[2]);
115				printf ("usage: %s host-name [port]\n",
116					argv[0]);
117				code = -1;
118				return;
119			}
120			port = htons(port);
121		}
122	}
123	host = hookup(argv[1], port);
124	if (host) {
125		int overbose;
126
127		connected = 1;
128		/*
129		 * Set up defaults for FTP.
130		 */
131		strlcpy(typename, "ascii", sizeof(typename));
132		type = TYPE_A;
133		curtype = TYPE_A;
134		strlcpy(formname, "non-print", sizeof(formname));
135		form = FORM_N;
136		strlcpy(modename, "stream", sizeof(modename));
137		mode = MODE_S;
138		strlcpy(structname, "file", sizeof(structname));
139		stru = STRU_F;
140		strlcpy(bytename, "8", sizeof(bytename));
141		bytesize = 8;
142		if (autologin)
143			login(argv[1]);
144
145#if (defined(unix) || defined(__unix__) || defined(__unix) || defined(_AIX) || defined(_CRAY) || defined(__NetBSD__)) && NBBY == 8
146/*
147 * this ifdef is to keep someone form "porting" this to an incompatible
148 * system and not checking this out. This way they have to think about it.
149 */
150		overbose = verbose;
151		if (debug == 0)
152			verbose = -1;
153		if (command("SYST") == COMPLETE && overbose) {
154			char *cp, c;
155			cp = strchr(reply_string+4, ' ');
156			if (cp == NULL)
157				cp = strchr(reply_string+4, '\r');
158			if (cp) {
159				if (cp[-1] == '.')
160					cp--;
161				c = *cp;
162				*cp = '\0';
163			}
164
165			printf("Remote system type is %s.\n",
166				reply_string+4);
167			if (cp)
168				*cp = c;
169		}
170		if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
171			if (proxy)
172				unix_proxy = 1;
173			else
174				unix_server = 1;
175			/*
176			 * Set type to 0 (not specified by user),
177			 * meaning binary by default, but don't bother
178			 * telling server.  We can use binary
179			 * for text files unless changed by the user.
180			 */
181			type = 0;
182			strlcpy(typename, "binary", sizeof(typename));
183			if (overbose)
184			    printf("Using %s mode to transfer files.\n",
185				typename);
186		} else {
187			if (proxy)
188				unix_proxy = 0;
189			else
190				unix_server = 0;
191			if (overbose &&
192			    !strncmp(reply_string, "215 TOPS20", 10))
193				printf(
194"Remember to set tenex mode when transfering binary files from this machine.\n");
195		}
196		verbose = overbose;
197#endif /* unix */
198	}
199}
200
201struct	types {
202	char	*t_name;
203	char	*t_mode;
204	int	t_type;
205	char	*t_arg;
206} types[] = {
207	{ "ascii",	"A",	TYPE_A,	0 },
208	{ "binary",	"I",	TYPE_I,	0 },
209	{ "image",	"I",	TYPE_I,	0 },
210	{ "ebcdic",	"E",	TYPE_E,	0 },
211	{ "tenex",	"L",	TYPE_L,	bytename },
212	{ NULL }
213};
214
215/*
216 * Set transfer type.
217 */
218void
219settype(int argc, char **argv)
220{
221	struct types *p;
222	int comret;
223
224	if (argc > 2) {
225		char *sep;
226
227		printf("usage: %s [", argv[0]);
228		sep = " ";
229		for (p = types; p->t_name; p++) {
230			printf("%s%s", sep, p->t_name);
231			sep = " | ";
232		}
233		printf(" ]\n");
234		code = -1;
235		return;
236	}
237	if (argc < 2) {
238		printf("Using %s mode to transfer files.\n", typename);
239		code = 0;
240		return;
241	}
242	for (p = types; p->t_name; p++)
243		if (strcmp(argv[1], p->t_name) == 0)
244			break;
245	if (p->t_name == 0) {
246		printf("%s: unknown mode\n", argv[1]);
247		code = -1;
248		return;
249	}
250	if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
251		comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
252	else
253		comret = command("TYPE %s", p->t_mode);
254	if (comret == COMPLETE) {
255		strlcpy(typename, p->t_name, sizeof(typename));
256		curtype = type = p->t_type;
257	}
258}
259
260/*
261 * Internal form of settype; changes current type in use with server
262 * without changing our notion of the type for data transfers.
263 * Used to change to and from ascii for listings.
264 */
265void
266changetype(int newtype, int show)
267{
268	struct types *p;
269	int comret, oldverbose = verbose;
270
271	if (newtype == 0)
272		newtype = TYPE_I;
273	if (newtype == curtype)
274		return;
275	if (debug == 0 && show == 0)
276		verbose = 0;
277	for (p = types; p->t_name; p++)
278		if (newtype == p->t_type)
279			break;
280	if (p->t_name == 0) {
281		printf("ftp: internal error: unknown type %d\n", newtype);
282		return;
283	}
284	if (newtype == TYPE_L && bytename[0] != '\0')
285		comret = command("TYPE %s %s", p->t_mode, bytename);
286	else
287		comret = command("TYPE %s", p->t_mode);
288	if (comret == COMPLETE)
289		curtype = newtype;
290	verbose = oldverbose;
291}
292
293char *stype[] = {
294	"type",
295	"",
296	0
297};
298
299/*
300 * Set binary transfer type.
301 */
302/*VARARGS*/
303void
304setbinary(int argc, char **argv)
305{
306
307	stype[1] = "binary";
308	settype(2, stype);
309}
310
311/*
312 * Set ascii transfer type.
313 */
314/*VARARGS*/
315void
316setascii(int argc, char **argv)
317{
318
319	stype[1] = "ascii";
320	settype(2, stype);
321}
322
323/*
324 * Set tenex transfer type.
325 */
326/*VARARGS*/
327void
328settenex(int argc, char **argv)
329{
330
331	stype[1] = "tenex";
332	settype(2, stype);
333}
334
335/*
336 * Set file transfer mode.
337 */
338/*ARGSUSED*/
339void
340setftmode(int argc, char **argv)
341{
342
343	printf("We only support %s mode, sorry.\n", modename);
344	code = -1;
345}
346
347/*
348 * Set file transfer format.
349 */
350/*ARGSUSED*/
351void
352setform(int argc, char **argv)
353{
354
355	printf("We only support %s format, sorry.\n", formname);
356	code = -1;
357}
358
359/*
360 * Set file transfer structure.
361 */
362/*ARGSUSED*/
363void
364setstruct(int argc, char **argv)
365{
366
367	printf("We only support %s structure, sorry.\n", structname);
368	code = -1;
369}
370
371/*
372 * Send a single file.
373 */
374void
375put(int argc, char **argv)
376{
377	char *cmd;
378	int loc = 0;
379	char *oldargv1, *oldargv2;
380
381	if (argc == 2) {
382		argc++;
383		argv[2] = argv[1];
384		loc++;
385	}
386	if (argc < 2 && !another(&argc, &argv, "local-file"))
387		goto usage;
388	if (argc < 3 && !another(&argc, &argv, "remote-file")) {
389usage:
390		printf("usage: %s local-file remote-file\n", argv[0]);
391		code = -1;
392		return;
393	}
394	oldargv1 = argv[1];
395	oldargv2 = argv[2];
396	if (!globulize(&argv[1])) {
397		code = -1;
398		return;
399	}
400	/*
401	 * If "globulize" modifies argv[1], and argv[2] is a copy of
402	 * the old argv[1], make it a copy of the new argv[1].
403	 */
404	if (argv[1] != oldargv1 && argv[2] == oldargv1) {
405		argv[2] = argv[1];
406	}
407	cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
408	if (loc && ntflag) {
409		argv[2] = dotrans(argv[2]);
410	}
411	if (loc && mapflag) {
412		argv[2] = domap(argv[2]);
413	}
414	sendrequest(cmd, argv[1], argv[2],
415		    curtype == TYPE_I ? "rb" : "r",
416		    argv[1] != oldargv1 || argv[2] != oldargv2);
417}
418
419/* ARGSUSED */
420static RETSIGTYPE
421mabort(int signo)
422{
423	int ointer;
424
425	printf("\n");
426	fflush(stdout);
427	if (mflag && fromatty) {
428		ointer = interactive;
429		interactive = 1;
430		if (confirm("Continue with", mname)) {
431			interactive = ointer;
432			longjmp(jabort,0);
433		}
434		interactive = ointer;
435	}
436	mflag = 0;
437	longjmp(jabort,0);
438}
439
440/*
441 * Send multiple files.
442 */
443void
444mput(int argc, char **argv)
445{
446    int i;
447    RETSIGTYPE (*oldintr)(int);
448    int ointer;
449    char *tp;
450
451    if (argc < 2 && !another(&argc, &argv, "local-files")) {
452	printf("usage: %s local-files\n", argv[0]);
453	code = -1;
454	return;
455    }
456    mname = argv[0];
457    mflag = 1;
458    oldintr = signal(SIGINT, mabort);
459    setjmp(jabort);
460    if (proxy) {
461	char *cp, *tp2, tmpbuf[MaxPathLen];
462
463	while ((cp = remglob(argv,0)) != NULL) {
464	    if (*cp == 0) {
465		mflag = 0;
466		continue;
467	    }
468	    if (mflag && confirm(argv[0], cp)) {
469		tp = cp;
470		if (mcase) {
471		    while (*tp && !islower((unsigned char)*tp)) {
472			tp++;
473		    }
474		    if (!*tp) {
475			tp = cp;
476			tp2 = tmpbuf;
477			while ((*tp2 = *tp) != '\0') {
478			    if (isupper((unsigned char)*tp2)) {
479				*tp2 = 'a' + *tp2 - 'A';
480			    }
481			    tp++;
482			    tp2++;
483			}
484		    }
485		    tp = tmpbuf;
486		}
487		if (ntflag) {
488		    tp = dotrans(tp);
489		}
490		if (mapflag) {
491		    tp = domap(tp);
492		}
493		sendrequest((sunique) ? "STOU" : "STOR",
494			    cp, tp,
495			    curtype == TYPE_I ? "rb" : "r",
496			    cp != tp || !interactive);
497		if (!mflag && fromatty) {
498		    ointer = interactive;
499		    interactive = 1;
500		    if (confirm("Continue with","mput")) {
501			mflag++;
502		    }
503		    interactive = ointer;
504		}
505	    }
506	}
507	signal(SIGINT, oldintr);
508	mflag = 0;
509	return;
510    }
511    for (i = 1; i < argc; i++) {
512	char **cpp;
513	glob_t gl;
514	int flags;
515
516	if (!doglob) {
517	    if (mflag && confirm(argv[0], argv[i])) {
518		tp = (ntflag) ? dotrans(argv[i]) : argv[i];
519		tp = (mapflag) ? domap(tp) : tp;
520		sendrequest((sunique) ? "STOU" : "STOR",
521			    argv[i],
522			    curtype == TYPE_I ? "rb" : "r",
523			    tp, tp != argv[i] || !interactive);
524		if (!mflag && fromatty) {
525		    ointer = interactive;
526		    interactive = 1;
527		    if (confirm("Continue with","mput")) {
528			mflag++;
529		    }
530		    interactive = ointer;
531		}
532	    }
533	    continue;
534	}
535
536	memset(&gl, 0, sizeof(gl));
537	flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
538	if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
539	    warnx("%s: not found", argv[i]);
540	    globfree(&gl);
541	    continue;
542	}
543	for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) {
544	    if (mflag && confirm(argv[0], *cpp)) {
545		tp = (ntflag) ? dotrans(*cpp) : *cpp;
546		tp = (mapflag) ? domap(tp) : tp;
547		sendrequest((sunique) ? "STOU" : "STOR",
548			    *cpp, tp,
549			    curtype == TYPE_I ? "rb" : "r",
550			    *cpp != tp || !interactive);
551		if (!mflag && fromatty) {
552		    ointer = interactive;
553		    interactive = 1;
554		    if (confirm("Continue with","mput")) {
555			mflag++;
556		    }
557		    interactive = ointer;
558		}
559	    }
560	}
561	globfree(&gl);
562    }
563    signal(SIGINT, oldintr);
564    mflag = 0;
565}
566
567void
568reget(int argc, char **argv)
569{
570    getit(argc, argv, 1, curtype == TYPE_I ? "r+wb" : "r+w");
571}
572
573void
574get(int argc, char **argv)
575{
576    char *mode;
577
578    if (restart_point) {
579	if (curtype == TYPE_I)
580	    mode = "r+wb";
581	else
582	    mode = "r+w";
583    } else {
584	if (curtype == TYPE_I)
585	    mode = "wb";
586	else
587	    mode = "w";
588    }
589
590    getit(argc, argv, 0, mode);
591}
592
593/*
594 * Receive one file.
595 */
596int
597getit(int argc, char **argv, int restartit, char *mode)
598{
599	int loc = 0;
600	int local_given = 1;
601	char *oldargv1, *oldargv2;
602
603	if (argc == 2) {
604		argc++;
605		local_given = 0;
606		argv[2] = argv[1];
607		loc++;
608	}
609	if ((argc < 2 && !another(&argc, &argv, "remote-file")) ||
610	    (argc < 3 && !another(&argc, &argv, "local-file"))) {
611		printf("usage: %s remote-file [ local-file ]\n", argv[0]);
612		code = -1;
613		return (0);
614	}
615	oldargv1 = argv[1];
616	oldargv2 = argv[2];
617	if (!globulize(&argv[2])) {
618		code = -1;
619		return (0);
620	}
621	if (loc && mcase) {
622		char *tp = argv[1], *tp2, tmpbuf[MaxPathLen];
623
624		while (*tp && !islower((unsigned char)*tp)) {
625			tp++;
626		}
627		if (!*tp) {
628			tp = argv[2];
629			tp2 = tmpbuf;
630			while ((*tp2 = *tp) != '\0') {
631				if (isupper((unsigned char)*tp2)) {
632					*tp2 = 'a' + *tp2 - 'A';
633				}
634				tp++;
635				tp2++;
636			}
637			argv[2] = tmpbuf;
638		}
639	}
640	if (loc && ntflag)
641		argv[2] = dotrans(argv[2]);
642	if (loc && mapflag)
643		argv[2] = domap(argv[2]);
644	if (restartit) {
645		struct stat stbuf;
646		int ret;
647
648		ret = stat(argv[2], &stbuf);
649		if (restartit == 1) {
650			if (ret < 0) {
651				warn("local: %s", argv[2]);
652				return (0);
653			}
654			restart_point = stbuf.st_size;
655		} else if (ret == 0) {
656			int overbose;
657			int cmdret;
658			int yy, mo, day, hour, min, sec;
659			struct tm *tm;
660			time_t mtime = stbuf.st_mtime;
661
662			overbose = verbose;
663			if (debug == 0)
664				verbose = -1;
665			cmdret = command("MDTM %s", argv[1]);
666			verbose = overbose;
667			if (cmdret != COMPLETE) {
668				printf("%s\n", reply_string);
669				return (0);
670			}
671			if (sscanf(reply_string,
672				   "%*s %04d%02d%02d%02d%02d%02d",
673				   &yy, &mo, &day, &hour, &min, &sec)
674			    != 6) {
675				printf ("bad MDTM result\n");
676				return (0);
677			}
678
679			tm = gmtime(&mtime);
680			tm->tm_mon++;
681			tm->tm_year += 1900;
682
683			if ((tm->tm_year > yy) ||
684			    (tm->tm_year == yy &&
685			     tm->tm_mon > mo) ||
686			    (tm->tm_mon == mo &&
687			     tm->tm_mday > day) ||
688			    (tm->tm_mday == day &&
689			     tm->tm_hour > hour) ||
690			    (tm->tm_hour == hour &&
691			     tm->tm_min > min) ||
692			    (tm->tm_min == min &&
693			     tm->tm_sec > sec))
694				return (1);
695		}
696	}
697
698	recvrequest("RETR", argv[2], argv[1], mode,
699		    argv[1] != oldargv1 || argv[2] != oldargv2, local_given);
700	restart_point = 0;
701	return (0);
702}
703
704static int
705suspicious_filename(const char *fn)
706{
707    return strstr(fn, "../") != NULL || *fn == '/';
708}
709
710/*
711 * Get multiple files.
712 */
713void
714mget(int argc, char **argv)
715{
716	sighand oldintr;
717	int ch, ointer;
718	char *cp, *tp, *tp2, tmpbuf[MaxPathLen];
719
720	if (argc < 2 && !another(&argc, &argv, "remote-files")) {
721		printf("usage: %s remote-files\n", argv[0]);
722		code = -1;
723		return;
724	}
725	mname = argv[0];
726	mflag = 1;
727	oldintr = signal(SIGINT, mabort);
728	setjmp(jabort);
729	while ((cp = remglob(argv,proxy)) != NULL) {
730		if (*cp == '\0') {
731			mflag = 0;
732			continue;
733		}
734		if (mflag && suspicious_filename(cp))
735		    printf("*** Suspicious filename: %s\n", cp);
736		if (mflag && confirm(argv[0], cp)) {
737			tp = cp;
738			if (mcase) {
739				for (tp2 = tmpbuf; (ch = *tp++);)
740					*tp2++ = tolower(ch);
741				*tp2 = '\0';
742				tp = tmpbuf;
743			}
744			if (ntflag) {
745				tp = dotrans(tp);
746			}
747			if (mapflag) {
748				tp = domap(tp);
749			}
750			recvrequest("RETR", tp, cp,
751				    curtype == TYPE_I ? "wb" : "w",
752				    tp != cp || !interactive, 0);
753			if (!mflag && fromatty) {
754				ointer = interactive;
755				interactive = 1;
756				if (confirm("Continue with","mget")) {
757					mflag++;
758				}
759				interactive = ointer;
760			}
761		}
762	}
763	signal(SIGINT,oldintr);
764	mflag = 0;
765}
766
767char *
768remglob(char **argv, int doswitch)
769{
770    char temp[16];
771    static char buf[MaxPathLen];
772    static FILE *ftemp = NULL;
773    static char **args;
774    int oldverbose, oldhash;
775    char *cp, *mode;
776
777    if (!mflag) {
778	if (!doglob) {
779	    args = NULL;
780	}
781	else {
782	    if (ftemp) {
783		fclose(ftemp);
784		ftemp = NULL;
785	    }
786	}
787	return (NULL);
788    }
789    if (!doglob) {
790	if (args == NULL)
791	    args = argv;
792	if ((cp = *++args) == NULL)
793	    args = NULL;
794	return (cp);
795    }
796    if (ftemp == NULL) {
797	int fd;
798	strlcpy(temp, _PATH_TMP_XXX, sizeof(temp));
799	fd = mkstemp(temp);
800	if(fd < 0){
801	    warn("unable to create temporary file %s", temp);
802	    return NULL;
803	}
804	close(fd);
805	oldverbose = verbose, verbose = 0;
806	oldhash = hash, hash = 0;
807	if (doswitch) {
808	    pswitch(!proxy);
809	}
810	for (mode = "w"; *++argv != NULL; mode = "a")
811	    recvrequest ("NLST", temp, *argv, mode, 0, 0);
812	if (doswitch) {
813	    pswitch(!proxy);
814	}
815	verbose = oldverbose; hash = oldhash;
816	ftemp = fopen(temp, "r");
817	unlink(temp);
818	if (ftemp == NULL) {
819	    printf("can't find list of remote files, oops\n");
820	    return (NULL);
821	}
822    }
823    while(fgets(buf, sizeof (buf), ftemp)) {
824	if ((cp = strchr(buf, '\n')) != NULL)
825	    *cp = '\0';
826	if(!interactive && suspicious_filename(buf)){
827	    printf("Ignoring remote globbed file `%s'\n", buf);
828	    continue;
829	}
830	return buf;
831    }
832    fclose(ftemp);
833    ftemp = NULL;
834    return (NULL);
835}
836
837char *
838onoff(int bool)
839{
840
841	return (bool ? "on" : "off");
842}
843
844/*
845 * Show status.
846 */
847/*ARGSUSED*/
848void
849status(int argc, char **argv)
850{
851	int i;
852
853	if (connected)
854		printf("Connected to %s.\n", hostname);
855	else
856		printf("Not connected.\n");
857	if (!proxy) {
858		pswitch(1);
859		if (connected) {
860			printf("Connected for proxy commands to %s.\n", hostname);
861		}
862		else {
863			printf("No proxy connection.\n");
864		}
865		pswitch(0);
866	}
867	sec_status();
868	printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
869		modename, typename, formname, structname);
870	printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
871		onoff(verbose), onoff(bell), onoff(interactive),
872		onoff(doglob));
873	printf("Store unique: %s; Receive unique: %s\n", onoff(sunique),
874		onoff(runique));
875	printf("Case: %s; CR stripping: %s\n",onoff(mcase),onoff(crflag));
876	if (ntflag) {
877		printf("Ntrans: (in) %s (out) %s\n", ntin,ntout);
878	}
879	else {
880		printf("Ntrans: off\n");
881	}
882	if (mapflag) {
883		printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
884	}
885	else {
886		printf("Nmap: off\n");
887	}
888	printf("Hash mark printing: %s; Use of PORT cmds: %s\n",
889		onoff(hash), onoff(sendport));
890	if (macnum > 0) {
891		printf("Macros:\n");
892		for (i=0; i<macnum; i++) {
893			printf("\t%s\n",macros[i].mac_name);
894		}
895	}
896	code = 0;
897}
898
899/*
900 * Set beep on cmd completed mode.
901 */
902/*VARARGS*/
903void
904setbell(int argc, char **argv)
905{
906
907	bell = !bell;
908	printf("Bell mode %s.\n", onoff(bell));
909	code = bell;
910}
911
912/*
913 * Turn on packet tracing.
914 */
915/*VARARGS*/
916void
917settrace(int argc, char **argv)
918{
919
920	trace = !trace;
921	printf("Packet tracing %s.\n", onoff(trace));
922	code = trace;
923}
924
925/*
926 * Toggle hash mark printing during transfers.
927 */
928/*VARARGS*/
929void
930sethash(int argc, char **argv)
931{
932
933	hash = !hash;
934	printf("Hash mark printing %s", onoff(hash));
935	code = hash;
936	if (hash)
937		printf(" (%d bytes/hash mark)", 1024);
938	printf(".\n");
939}
940
941/*
942 * Turn on printing of server echo's.
943 */
944/*VARARGS*/
945void
946setverbose(int argc, char **argv)
947{
948
949	verbose = !verbose;
950	printf("Verbose mode %s.\n", onoff(verbose));
951	code = verbose;
952}
953
954/*
955 * Toggle PORT cmd use before each data connection.
956 */
957/*VARARGS*/
958void
959setport(int argc, char **argv)
960{
961
962	sendport = !sendport;
963	printf("Use of PORT cmds %s.\n", onoff(sendport));
964	code = sendport;
965}
966
967/*
968 * Turn on interactive prompting
969 * during mget, mput, and mdelete.
970 */
971/*VARARGS*/
972void
973setprompt(int argc, char **argv)
974{
975
976	interactive = !interactive;
977	printf("Interactive mode %s.\n", onoff(interactive));
978	code = interactive;
979}
980
981/*
982 * Toggle metacharacter interpretation
983 * on local file names.
984 */
985/*VARARGS*/
986void
987setglob(int argc, char **argv)
988{
989
990	doglob = !doglob;
991	printf("Globbing %s.\n", onoff(doglob));
992	code = doglob;
993}
994
995/*
996 * Set debugging mode on/off and/or
997 * set level of debugging.
998 */
999/*VARARGS*/
1000void
1001setdebug(int argc, char **argv)
1002{
1003	int val;
1004
1005	if (argc > 1) {
1006		val = atoi(argv[1]);
1007		if (val < 0) {
1008			printf("%s: bad debugging value.\n", argv[1]);
1009			code = -1;
1010			return;
1011		}
1012	} else
1013		val = !debug;
1014	debug = val;
1015	if (debug)
1016		options |= SO_DEBUG;
1017	else
1018		options &= ~SO_DEBUG;
1019	printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
1020	code = debug > 0;
1021}
1022
1023/*
1024 * Set current working directory
1025 * on remote machine.
1026 */
1027void
1028cd(int argc, char **argv)
1029{
1030
1031	if (argc < 2 && !another(&argc, &argv, "remote-directory")) {
1032		printf("usage: %s remote-directory\n", argv[0]);
1033		code = -1;
1034		return;
1035	}
1036	if (command("CWD %s", argv[1]) == ERROR && code == 500) {
1037		if (verbose)
1038			printf("CWD command not recognized, trying XCWD\n");
1039		command("XCWD %s", argv[1]);
1040	}
1041}
1042
1043/*
1044 * Set current working directory
1045 * on local machine.
1046 */
1047void
1048lcd(int argc, char **argv)
1049{
1050	char buf[MaxPathLen];
1051
1052	if (argc < 2)
1053		argc++, argv[1] = home;
1054	if (argc != 2) {
1055		printf("usage: %s local-directory\n", argv[0]);
1056		code = -1;
1057		return;
1058	}
1059	if (!globulize(&argv[1])) {
1060		code = -1;
1061		return;
1062	}
1063	if (chdir(argv[1]) < 0) {
1064		warn("local: %s", argv[1]);
1065		code = -1;
1066		return;
1067	}
1068	if (getcwd(buf, sizeof(buf)) != NULL)
1069		printf("Local directory now %s\n", buf);
1070	else
1071		warnx("getwd: %s", buf);
1072	code = 0;
1073}
1074
1075/*
1076 * Delete a single file.
1077 */
1078void
1079delete(int argc, char **argv)
1080{
1081
1082	if (argc < 2 && !another(&argc, &argv, "remote-file")) {
1083		printf("usage: %s remote-file\n", argv[0]);
1084		code = -1;
1085		return;
1086	}
1087	command("DELE %s", argv[1]);
1088}
1089
1090/*
1091 * Delete multiple files.
1092 */
1093void
1094mdelete(int argc, char **argv)
1095{
1096    sighand oldintr;
1097    int ointer;
1098    char *cp;
1099
1100    if (argc < 2 && !another(&argc, &argv, "remote-files")) {
1101	printf("usage: %s remote-files\n", argv[0]);
1102	code = -1;
1103	return;
1104    }
1105    mname = argv[0];
1106    mflag = 1;
1107    oldintr = signal(SIGINT, mabort);
1108    setjmp(jabort);
1109    while ((cp = remglob(argv,0)) != NULL) {
1110	if (*cp == '\0') {
1111	    mflag = 0;
1112	    continue;
1113	}
1114	if (mflag && confirm(argv[0], cp)) {
1115	    command("DELE %s", cp);
1116	    if (!mflag && fromatty) {
1117		ointer = interactive;
1118		interactive = 1;
1119		if (confirm("Continue with", "mdelete")) {
1120		    mflag++;
1121		}
1122		interactive = ointer;
1123	    }
1124	}
1125    }
1126    signal(SIGINT, oldintr);
1127    mflag = 0;
1128}
1129
1130/*
1131 * Rename a remote file.
1132 */
1133void
1134renamefile(int argc, char **argv)
1135{
1136
1137	if (argc < 2 && !another(&argc, &argv, "from-name"))
1138		goto usage;
1139	if (argc < 3 && !another(&argc, &argv, "to-name")) {
1140usage:
1141		printf("%s from-name to-name\n", argv[0]);
1142		code = -1;
1143		return;
1144	}
1145	if (command("RNFR %s", argv[1]) == CONTINUE)
1146		command("RNTO %s", argv[2]);
1147}
1148
1149/*
1150 * Get a directory listing
1151 * of remote files.
1152 */
1153void
1154ls(int argc, char **argv)
1155{
1156	char *cmd;
1157
1158	if (argc < 2)
1159		argc++, argv[1] = NULL;
1160	if (argc < 3)
1161		argc++, argv[2] = "-";
1162	if (argc > 3) {
1163		printf("usage: %s remote-directory local-file\n", argv[0]);
1164		code = -1;
1165		return;
1166	}
1167	cmd = argv[0][0] == 'n' ? "NLST" : "LIST";
1168	if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
1169		code = -1;
1170		return;
1171	}
1172	if (strcmp(argv[2], "-") && *argv[2] != '|')
1173	    if (!globulize(&argv[2]) || !confirm("output to local-file:",
1174						 argv[2])) {
1175		code = -1;
1176		return;
1177	    }
1178	recvrequest(cmd, argv[2], argv[1], "w", 0, 1);
1179}
1180
1181/*
1182 * Get a directory listing
1183 * of multiple remote files.
1184 */
1185void
1186mls(int argc, char **argv)
1187{
1188	sighand oldintr;
1189	int ointer, i;
1190	char *cmd, mode[1], *dest;
1191
1192	if (argc < 2 && !another(&argc, &argv, "remote-files"))
1193		goto usage;
1194	if (argc < 3 && !another(&argc, &argv, "local-file")) {
1195usage:
1196		printf("usage: %s remote-files local-file\n", argv[0]);
1197		code = -1;
1198		return;
1199	}
1200	dest = argv[argc - 1];
1201	argv[argc - 1] = NULL;
1202	if (strcmp(dest, "-") && *dest != '|')
1203		if (!globulize(&dest) ||
1204		    !confirm("output to local-file:", dest)) {
1205			code = -1;
1206			return;
1207	}
1208	cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
1209	mname = argv[0];
1210	mflag = 1;
1211	oldintr = signal(SIGINT, mabort);
1212	setjmp(jabort);
1213	for (i = 1; mflag && i < argc-1; ++i) {
1214		*mode = (i == 1) ? 'w' : 'a';
1215		recvrequest(cmd, dest, argv[i], mode, 0, 1);
1216		if (!mflag && fromatty) {
1217			ointer = interactive;
1218			interactive = 1;
1219			if (confirm("Continue with", argv[0])) {
1220				mflag ++;
1221			}
1222			interactive = ointer;
1223		}
1224	}
1225	signal(SIGINT, oldintr);
1226	mflag = 0;
1227}
1228
1229/*
1230 * Do a shell escape
1231 */
1232/*ARGSUSED*/
1233void
1234shell(int argc, char **argv)
1235{
1236	pid_t pid;
1237	RETSIGTYPE (*old1)(int), (*old2)(int);
1238	char shellnam[40], *shell, *namep;
1239	int status;
1240
1241	old1 = signal (SIGINT, SIG_IGN);
1242	old2 = signal (SIGQUIT, SIG_IGN);
1243	if ((pid = fork()) == 0) {
1244		for (pid = 3; pid < 20; pid++)
1245			close(pid);
1246		signal(SIGINT, SIG_DFL);
1247		signal(SIGQUIT, SIG_DFL);
1248		shell = getenv("SHELL");
1249		if (shell == NULL)
1250			shell = _PATH_BSHELL;
1251		namep = strrchr(shell,'/');
1252		if (namep == NULL)
1253			namep = shell;
1254		snprintf (shellnam, sizeof(shellnam),
1255			  "-%s", ++namep);
1256		if (strcmp(namep, "sh") != 0)
1257			shellnam[0] = '+';
1258		if (debug) {
1259			printf ("%s\n", shell);
1260			fflush (stdout);
1261		}
1262		if (argc > 1) {
1263			execl(shell,shellnam,"-c",altarg,(char *)0);
1264		}
1265		else {
1266			execl(shell,shellnam,(char *)0);
1267		}
1268		warn("%s", shell);
1269		code = -1;
1270		exit(1);
1271	}
1272	if (pid > 0)
1273		while (waitpid(-1, &status, 0) != pid)
1274			;
1275	signal(SIGINT, old1);
1276	signal(SIGQUIT, old2);
1277	if (pid == -1) {
1278		warn("%s", "Try again later");
1279		code = -1;
1280	}
1281	else {
1282		code = 0;
1283	}
1284}
1285
1286/*
1287 * Send new user information (re-login)
1288 */
1289void
1290user(int argc, char **argv)
1291{
1292	char acct[80];
1293	int n, aflag = 0;
1294	char tmp[256];
1295
1296	if (argc < 2)
1297		another(&argc, &argv, "username");
1298	if (argc < 2 || argc > 4) {
1299		printf("usage: %s username [password] [account]\n", argv[0]);
1300		code = -1;
1301		return;
1302	}
1303	n = command("USER %s", argv[1]);
1304	if (n == CONTINUE) {
1305	    if (argc < 3 ) {
1306		des_read_pw_string (tmp,
1307				    sizeof(tmp),
1308				    "Password: ", 0);
1309		argv[2] = tmp;
1310		argc++;
1311	    }
1312	    n = command("PASS %s", argv[2]);
1313	}
1314	if (n == CONTINUE) {
1315		if (argc < 4) {
1316			printf("Account: "); fflush(stdout);
1317			fgets(acct, sizeof(acct) - 1, stdin);
1318			acct[strlen(acct) - 1] = '\0';
1319			argv[3] = acct; argc++;
1320		}
1321		n = command("ACCT %s", argv[3]);
1322		aflag++;
1323	}
1324	if (n != COMPLETE) {
1325		fprintf(stdout, "Login failed.\n");
1326		return;
1327	}
1328	if (!aflag && argc == 4) {
1329		command("ACCT %s", argv[3]);
1330	}
1331}
1332
1333/*
1334 * Print working directory.
1335 */
1336/*VARARGS*/
1337void
1338pwd(int argc, char **argv)
1339{
1340	int oldverbose = verbose;
1341
1342	/*
1343	 * If we aren't verbose, this doesn't do anything!
1344	 */
1345	verbose = 1;
1346	if (command("PWD") == ERROR && code == 500) {
1347		printf("PWD command not recognized, trying XPWD\n");
1348		command("XPWD");
1349	}
1350	verbose = oldverbose;
1351}
1352
1353/*
1354 * Make a directory.
1355 */
1356void
1357makedir(int argc, char **argv)
1358{
1359
1360	if (argc < 2 && !another(&argc, &argv, "directory-name")) {
1361		printf("usage: %s directory-name\n", argv[0]);
1362		code = -1;
1363		return;
1364	}
1365	if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1366		if (verbose)
1367			printf("MKD command not recognized, trying XMKD\n");
1368		command("XMKD %s", argv[1]);
1369	}
1370}
1371
1372/*
1373 * Remove a directory.
1374 */
1375void
1376removedir(int argc, char **argv)
1377{
1378
1379	if (argc < 2 && !another(&argc, &argv, "directory-name")) {
1380		printf("usage: %s directory-name\n", argv[0]);
1381		code = -1;
1382		return;
1383	}
1384	if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1385		if (verbose)
1386			printf("RMD command not recognized, trying XRMD\n");
1387		command("XRMD %s", argv[1]);
1388	}
1389}
1390
1391/*
1392 * Send a line, verbatim, to the remote machine.
1393 */
1394void
1395quote(int argc, char **argv)
1396{
1397
1398	if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1399		printf("usage: %s line-to-send\n", argv[0]);
1400		code = -1;
1401		return;
1402	}
1403	quote1("", argc, argv);
1404}
1405
1406/*
1407 * Send a SITE command to the remote machine.  The line
1408 * is sent verbatim to the remote machine, except that the
1409 * word "SITE" is added at the front.
1410 */
1411void
1412site(int argc, char **argv)
1413{
1414
1415	if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1416		printf("usage: %s line-to-send\n", argv[0]);
1417		code = -1;
1418		return;
1419	}
1420	quote1("SITE ", argc, argv);
1421}
1422
1423/*
1424 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1425 * Send the result as a one-line command and get response.
1426 */
1427void
1428quote1(char *initial, int argc, char **argv)
1429{
1430    int i;
1431    char buf[BUFSIZ];		/* must be >= sizeof(line) */
1432
1433    strlcpy(buf, initial, sizeof(buf));
1434    for(i = 1; i < argc; i++) {
1435	if(i > 1)
1436	    strlcat(buf, " ", sizeof(buf));
1437	strlcat(buf, argv[i], sizeof(buf));
1438    }
1439    if (command("%s", buf) == PRELIM) {
1440	while (getreply(0) == PRELIM)
1441	    continue;
1442    }
1443}
1444
1445void
1446do_chmod(int argc, char **argv)
1447{
1448
1449	if (argc < 2 && !another(&argc, &argv, "mode"))
1450		goto usage;
1451	if (argc < 3 && !another(&argc, &argv, "file-name")) {
1452usage:
1453		printf("usage: %s mode file-name\n", argv[0]);
1454		code = -1;
1455		return;
1456	}
1457	command("SITE CHMOD %s %s", argv[1], argv[2]);
1458}
1459
1460void
1461do_umask(int argc, char **argv)
1462{
1463	int oldverbose = verbose;
1464
1465	verbose = 1;
1466	command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1467	verbose = oldverbose;
1468}
1469
1470void
1471ftp_idle(int argc, char **argv)
1472{
1473	int oldverbose = verbose;
1474
1475	verbose = 1;
1476	command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1477	verbose = oldverbose;
1478}
1479
1480/*
1481 * Ask the other side for help.
1482 */
1483void
1484rmthelp(int argc, char **argv)
1485{
1486	int oldverbose = verbose;
1487
1488	verbose = 1;
1489	command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1490	verbose = oldverbose;
1491}
1492
1493/*
1494 * Terminate session and exit.
1495 */
1496/*VARARGS*/
1497void
1498quit(int argc, char **argv)
1499{
1500
1501	if (connected)
1502		disconnect(0, 0);
1503	pswitch(1);
1504	if (connected) {
1505		disconnect(0, 0);
1506	}
1507	exit(0);
1508}
1509
1510/*
1511 * Terminate session, but don't exit.
1512 */
1513void
1514disconnect(int argc, char **argv)
1515{
1516
1517	if (!connected)
1518		return;
1519	command("QUIT");
1520	if (cout) {
1521		fclose(cout);
1522	}
1523	cout = NULL;
1524	connected = 0;
1525	sec_end();
1526	data = -1;
1527	if (!proxy) {
1528		macnum = 0;
1529	}
1530}
1531
1532int
1533confirm(char *cmd, char *file)
1534{
1535	char line[BUFSIZ];
1536
1537	if (!interactive)
1538		return (1);
1539	printf("%s %s? ", cmd, file);
1540	fflush(stdout);
1541	if (fgets(line, sizeof line, stdin) == NULL)
1542		return (0);
1543	return (*line == 'y' || *line == 'Y');
1544}
1545
1546void
1547fatal(char *msg)
1548{
1549
1550	errx(1, "%s", msg);
1551}
1552
1553/*
1554 * Glob a local file name specification with
1555 * the expectation of a single return value.
1556 * Can't control multiple values being expanded
1557 * from the expression, we return only the first.
1558 */
1559int
1560globulize(char **cpp)
1561{
1562	glob_t gl;
1563	int flags;
1564
1565	if (!doglob)
1566		return (1);
1567
1568	flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
1569	memset(&gl, 0, sizeof(gl));
1570	if (glob(*cpp, flags, NULL, &gl) ||
1571	    gl.gl_pathc == 0) {
1572		warnx("%s: not found", *cpp);
1573		globfree(&gl);
1574		return (0);
1575	}
1576	*cpp = strdup(gl.gl_pathv[0]);	/* XXX - wasted memory */
1577	globfree(&gl);
1578	return (1);
1579}
1580
1581void
1582account(int argc, char **argv)
1583{
1584	char acct[50];
1585
1586	if (argc > 1) {
1587		++argv;
1588		--argc;
1589		strlcpy (acct, *argv, sizeof(acct));
1590		while (argc > 1) {
1591			--argc;
1592			++argv;
1593			strlcat(acct, *argv, sizeof(acct));
1594		}
1595	}
1596	else {
1597	    des_read_pw_string(acct, sizeof(acct), "Account:", 0);
1598	}
1599	command("ACCT %s", acct);
1600}
1601
1602jmp_buf abortprox;
1603
1604static RETSIGTYPE
1605proxabort(int sig)
1606{
1607
1608	if (!proxy) {
1609		pswitch(1);
1610	}
1611	if (connected) {
1612		proxflag = 1;
1613	}
1614	else {
1615		proxflag = 0;
1616	}
1617	pswitch(0);
1618	longjmp(abortprox,1);
1619}
1620
1621void
1622doproxy(int argc, char **argv)
1623{
1624	struct cmd *c;
1625	RETSIGTYPE (*oldintr)(int);
1626
1627	if (argc < 2 && !another(&argc, &argv, "command")) {
1628		printf("usage: %s command\n", argv[0]);
1629		code = -1;
1630		return;
1631	}
1632	c = getcmd(argv[1]);
1633	if (c == (struct cmd *) -1) {
1634		printf("?Ambiguous command\n");
1635		fflush(stdout);
1636		code = -1;
1637		return;
1638	}
1639	if (c == 0) {
1640		printf("?Invalid command\n");
1641		fflush(stdout);
1642		code = -1;
1643		return;
1644	}
1645	if (!c->c_proxy) {
1646		printf("?Invalid proxy command\n");
1647		fflush(stdout);
1648		code = -1;
1649		return;
1650	}
1651	if (setjmp(abortprox)) {
1652		code = -1;
1653		return;
1654	}
1655	oldintr = signal(SIGINT, proxabort);
1656	pswitch(1);
1657	if (c->c_conn && !connected) {
1658		printf("Not connected\n");
1659		fflush(stdout);
1660		pswitch(0);
1661		signal(SIGINT, oldintr);
1662		code = -1;
1663		return;
1664	}
1665	(*c->c_handler)(argc-1, argv+1);
1666	if (connected) {
1667		proxflag = 1;
1668	}
1669	else {
1670		proxflag = 0;
1671	}
1672	pswitch(0);
1673	signal(SIGINT, oldintr);
1674}
1675
1676void
1677setcase(int argc, char **argv)
1678{
1679
1680	mcase = !mcase;
1681	printf("Case mapping %s.\n", onoff(mcase));
1682	code = mcase;
1683}
1684
1685void
1686setcr(int argc, char **argv)
1687{
1688
1689	crflag = !crflag;
1690	printf("Carriage Return stripping %s.\n", onoff(crflag));
1691	code = crflag;
1692}
1693
1694void
1695setntrans(int argc, char **argv)
1696{
1697	if (argc == 1) {
1698		ntflag = 0;
1699		printf("Ntrans off.\n");
1700		code = ntflag;
1701		return;
1702	}
1703	ntflag++;
1704	code = ntflag;
1705	strlcpy (ntin, argv[1], 17);
1706	if (argc == 2) {
1707		ntout[0] = '\0';
1708		return;
1709	}
1710	strlcpy (ntout, argv[2], 17);
1711}
1712
1713char *
1714dotrans(char *name)
1715{
1716	static char new[MaxPathLen];
1717	char *cp1, *cp2 = new;
1718	int i, ostop, found;
1719
1720	for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1721		continue;
1722	for (cp1 = name; *cp1; cp1++) {
1723		found = 0;
1724		for (i = 0; *(ntin + i) && i < 16; i++) {
1725			if (*cp1 == *(ntin + i)) {
1726				found++;
1727				if (i < ostop) {
1728					*cp2++ = *(ntout + i);
1729				}
1730				break;
1731			}
1732		}
1733		if (!found) {
1734			*cp2++ = *cp1;
1735		}
1736	}
1737	*cp2 = '\0';
1738	return (new);
1739}
1740
1741void
1742setnmap(int argc, char **argv)
1743{
1744	char *cp;
1745
1746	if (argc == 1) {
1747		mapflag = 0;
1748		printf("Nmap off.\n");
1749		code = mapflag;
1750		return;
1751	}
1752	if (argc < 3 && !another(&argc, &argv, "mapout")) {
1753		printf("Usage: %s [mapin mapout]\n",argv[0]);
1754		code = -1;
1755		return;
1756	}
1757	mapflag = 1;
1758	code = 1;
1759	cp = strchr(altarg, ' ');
1760	if (proxy) {
1761		while(*++cp == ' ')
1762			continue;
1763		altarg = cp;
1764		cp = strchr(altarg, ' ');
1765	}
1766	*cp = '\0';
1767	strlcpy(mapin, altarg, MaxPathLen);
1768	while (*++cp == ' ')
1769		continue;
1770	strlcpy(mapout, cp, MaxPathLen);
1771}
1772
1773char *
1774domap(char *name)
1775{
1776	static char new[MaxPathLen];
1777	char *cp1 = name, *cp2 = mapin;
1778	char *tp[9], *te[9];
1779	int i, toks[9], toknum = 0, match = 1;
1780
1781	for (i=0; i < 9; ++i) {
1782		toks[i] = 0;
1783	}
1784	while (match && *cp1 && *cp2) {
1785		switch (*cp2) {
1786			case '\\':
1787				if (*++cp2 != *cp1) {
1788					match = 0;
1789				}
1790				break;
1791			case '$':
1792				if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1793					if (*cp1 != *(++cp2+1)) {
1794						toks[toknum = *cp2 - '1']++;
1795						tp[toknum] = cp1;
1796						while (*++cp1 && *(cp2+1)
1797							!= *cp1);
1798						te[toknum] = cp1;
1799					}
1800					cp2++;
1801					break;
1802				}
1803				/* FALLTHROUGH */
1804			default:
1805				if (*cp2 != *cp1) {
1806					match = 0;
1807				}
1808				break;
1809		}
1810		if (match && *cp1) {
1811			cp1++;
1812		}
1813		if (match && *cp2) {
1814			cp2++;
1815		}
1816	}
1817	if (!match && *cp1) /* last token mismatch */
1818	{
1819		toks[toknum] = 0;
1820	}
1821	cp1 = new;
1822	*cp1 = '\0';
1823	cp2 = mapout;
1824	while (*cp2) {
1825		match = 0;
1826		switch (*cp2) {
1827			case '\\':
1828				if (*(cp2 + 1)) {
1829					*cp1++ = *++cp2;
1830				}
1831				break;
1832			case '[':
1833LOOP:
1834				if (*++cp2 == '$' && isdigit((unsigned char)*(cp2+1))) {
1835					if (*++cp2 == '0') {
1836						char *cp3 = name;
1837
1838						while (*cp3) {
1839							*cp1++ = *cp3++;
1840						}
1841						match = 1;
1842					}
1843					else if (toks[toknum = *cp2 - '1']) {
1844						char *cp3 = tp[toknum];
1845
1846						while (cp3 != te[toknum]) {
1847							*cp1++ = *cp3++;
1848						}
1849						match = 1;
1850					}
1851				}
1852				else {
1853					while (*cp2 && *cp2 != ',' &&
1854					    *cp2 != ']') {
1855						if (*cp2 == '\\') {
1856							cp2++;
1857						}
1858						else if (*cp2 == '$' &&
1859   						        isdigit((unsigned char)*(cp2+1))) {
1860							if (*++cp2 == '0') {
1861							   char *cp3 = name;
1862
1863							   while (*cp3) {
1864								*cp1++ = *cp3++;
1865							   }
1866							}
1867							else if (toks[toknum =
1868							    *cp2 - '1']) {
1869							   char *cp3=tp[toknum];
1870
1871							   while (cp3 !=
1872								  te[toknum]) {
1873								*cp1++ = *cp3++;
1874							   }
1875							}
1876						}
1877						else if (*cp2) {
1878							*cp1++ = *cp2++;
1879						}
1880					}
1881					if (!*cp2) {
1882						printf("nmap: unbalanced brackets\n");
1883						return (name);
1884					}
1885					match = 1;
1886					cp2--;
1887				}
1888				if (match) {
1889					while (*++cp2 && *cp2 != ']') {
1890					      if (*cp2 == '\\' && *(cp2 + 1)) {
1891							cp2++;
1892					      }
1893					}
1894					if (!*cp2) {
1895						printf("nmap: unbalanced brackets\n");
1896						return (name);
1897					}
1898					break;
1899				}
1900				switch (*++cp2) {
1901					case ',':
1902						goto LOOP;
1903					case ']':
1904						break;
1905					default:
1906						cp2--;
1907						goto LOOP;
1908				}
1909				break;
1910			case '$':
1911				if (isdigit((unsigned char)*(cp2 + 1))) {
1912					if (*++cp2 == '0') {
1913						char *cp3 = name;
1914
1915						while (*cp3) {
1916							*cp1++ = *cp3++;
1917						}
1918					}
1919					else if (toks[toknum = *cp2 - '1']) {
1920						char *cp3 = tp[toknum];
1921
1922						while (cp3 != te[toknum]) {
1923							*cp1++ = *cp3++;
1924						}
1925					}
1926					break;
1927				}
1928				/* intentional drop through */
1929			default:
1930				*cp1++ = *cp2;
1931				break;
1932		}
1933		cp2++;
1934	}
1935	*cp1 = '\0';
1936	if (!*new) {
1937		return (name);
1938	}
1939	return (new);
1940}
1941
1942void
1943setpassive(int argc, char **argv)
1944{
1945
1946	passivemode = !passivemode;
1947	printf("Passive mode %s.\n", onoff(passivemode));
1948	code = passivemode;
1949}
1950
1951void
1952setsunique(int argc, char **argv)
1953{
1954
1955	sunique = !sunique;
1956	printf("Store unique %s.\n", onoff(sunique));
1957	code = sunique;
1958}
1959
1960void
1961setrunique(int argc, char **argv)
1962{
1963
1964	runique = !runique;
1965	printf("Receive unique %s.\n", onoff(runique));
1966	code = runique;
1967}
1968
1969/* change directory to perent directory */
1970void
1971cdup(int argc, char **argv)
1972{
1973
1974	if (command("CDUP") == ERROR && code == 500) {
1975		if (verbose)
1976			printf("CDUP command not recognized, trying XCUP\n");
1977		command("XCUP");
1978	}
1979}
1980
1981/* restart transfer at specific point */
1982void
1983restart(int argc, char **argv)
1984{
1985
1986    if (argc != 2)
1987	printf("restart: offset not specified\n");
1988    else {
1989	restart_point = atol(argv[1]);
1990	printf("restarting at %ld. %s\n", (long)restart_point,
1991	       "execute get, put or append to initiate transfer");
1992    }
1993}
1994
1995/* show remote system type */
1996void
1997syst(int argc, char **argv)
1998{
1999
2000	command("SYST");
2001}
2002
2003void
2004macdef(int argc, char **argv)
2005{
2006	char *tmp;
2007	int c;
2008
2009	if (macnum == 16) {
2010		printf("Limit of 16 macros have already been defined\n");
2011		code = -1;
2012		return;
2013	}
2014	if (argc < 2 && !another(&argc, &argv, "macro name")) {
2015		printf("Usage: %s macro_name\n",argv[0]);
2016		code = -1;
2017		return;
2018	}
2019	if (interactive) {
2020		printf("Enter macro line by line, terminating it with a null line\n");
2021	}
2022	strlcpy(macros[macnum].mac_name,
2023			argv[1],
2024			sizeof(macros[macnum].mac_name));
2025	if (macnum == 0) {
2026		macros[macnum].mac_start = macbuf;
2027	}
2028	else {
2029		macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2030	}
2031	tmp = macros[macnum].mac_start;
2032	while (tmp != macbuf+4096) {
2033		if ((c = getchar()) == EOF) {
2034			printf("macdef:end of file encountered\n");
2035			code = -1;
2036			return;
2037		}
2038		if ((*tmp = c) == '\n') {
2039			if (tmp == macros[macnum].mac_start) {
2040				macros[macnum++].mac_end = tmp;
2041				code = 0;
2042				return;
2043			}
2044			if (*(tmp-1) == '\0') {
2045				macros[macnum++].mac_end = tmp - 1;
2046				code = 0;
2047				return;
2048			}
2049			*tmp = '\0';
2050		}
2051		tmp++;
2052	}
2053	while (1) {
2054		while ((c = getchar()) != '\n' && c != EOF)
2055			/* LOOP */;
2056		if (c == EOF || getchar() == '\n') {
2057			printf("Macro not defined - 4k buffer exceeded\n");
2058			code = -1;
2059			return;
2060		}
2061	}
2062}
2063
2064/*
2065 * get size of file on remote machine
2066 */
2067void
2068sizecmd(int argc, char **argv)
2069{
2070
2071	if (argc < 2 && !another(&argc, &argv, "filename")) {
2072		printf("usage: %s filename\n", argv[0]);
2073		code = -1;
2074		return;
2075	}
2076	command("SIZE %s", argv[1]);
2077}
2078
2079/*
2080 * get last modification time of file on remote machine
2081 */
2082void
2083modtime(int argc, char **argv)
2084{
2085	int overbose;
2086
2087	if (argc < 2 && !another(&argc, &argv, "filename")) {
2088		printf("usage: %s filename\n", argv[0]);
2089		code = -1;
2090		return;
2091	}
2092	overbose = verbose;
2093	if (debug == 0)
2094		verbose = -1;
2095	if (command("MDTM %s", argv[1]) == COMPLETE) {
2096		int yy, mo, day, hour, min, sec;
2097		sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
2098			&day, &hour, &min, &sec);
2099		/* might want to print this in local time */
2100		printf("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1],
2101			mo, day, yy, hour, min, sec);
2102	} else
2103		printf("%s\n", reply_string);
2104	verbose = overbose;
2105}
2106
2107/*
2108 * show status on reomte machine
2109 */
2110void
2111rmtstatus(int argc, char **argv)
2112{
2113
2114	command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
2115}
2116
2117/*
2118 * get file if modtime is more recent than current file
2119 */
2120void
2121newer(int argc, char **argv)
2122{
2123
2124	if (getit(argc, argv, -1, curtype == TYPE_I ? "wb" : "w"))
2125		printf("Local file \"%s\" is newer than remote file \"%s\"\n",
2126			argv[2], argv[1]);
2127}
2128