xref: /csrg-svn/usr.bin/ftp/cmds.c (revision 32344)
1 /*
2  * Copyright (c) 1985 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)cmds.c	5.6 (Berkeley) 10/06/87";
9 #endif not lint
10 
11 /*
12  * FTP User Program -- Command Routines.
13  */
14 #include "ftp_var.h"
15 #include <sys/socket.h>
16 
17 #include <arpa/ftp.h>
18 
19 #include <signal.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include <netdb.h>
23 #include <ctype.h>
24 #include <sys/wait.h>
25 
26 
27 extern	char *globerr;
28 extern	char **glob();
29 extern	char *home;
30 extern	short gflag;
31 extern	char *remglob();
32 extern	char *getenv();
33 extern	char *index();
34 extern	char *rindex();
35 char *mname;
36 jmp_buf jabort;
37 char *dotrans(), *domap();
38 
39 /*
40  * Connect to peer server and
41  * auto-login, if possible.
42  */
43 setpeer(argc, argv)
44 	int argc;
45 	char *argv[];
46 {
47 	char *host, *hookup();
48 	int port;
49 
50 	if (connected) {
51 		printf("Already connected to %s, use close first.\n",
52 			hostname);
53 		code = -1;
54 		return;
55 	}
56 	if (argc < 2) {
57 		(void) strcat(line, " ");
58 		printf("(to) ");
59 		(void) gets(&line[strlen(line)]);
60 		makeargv();
61 		argc = margc;
62 		argv = margv;
63 	}
64 	if (argc > 3) {
65 		printf("usage: %s host-name [port]\n", argv[0]);
66 		code = -1;
67 		return;
68 	}
69 	port = sp->s_port;
70 	if (argc > 2) {
71 		port = atoi(argv[2]);
72 		if (port <= 0) {
73 			printf("%s: bad port number-- %s\n", argv[1], argv[2]);
74 			printf ("usage: %s host-name [port]\n", argv[0]);
75 			code = -1;
76 			return;
77 		}
78 		port = htons(port);
79 	}
80 	host = hookup(argv[1], port);
81 	if (host) {
82 		connected = 1;
83 		if (autologin)
84 			(void) login(argv[1]);
85 	}
86 }
87 
88 struct	types {
89 	char	*t_name;
90 	char	*t_mode;
91 	int	t_type;
92 	char	*t_arg;
93 } types[] = {
94 	{ "ascii",	"A",	TYPE_A,	0 },
95 	{ "binary",	"I",	TYPE_I,	0 },
96 	{ "image",	"I",	TYPE_I,	0 },
97 	{ "ebcdic",	"E",	TYPE_E,	0 },
98 	{ "tenex",	"L",	TYPE_L,	bytename },
99 	0
100 };
101 
102 /*
103  * Set transfer type.
104  */
105 settype(argc, argv)
106 	char *argv[];
107 {
108 	register struct types *p;
109 	int comret;
110 
111 	if (argc > 2) {
112 		char *sep;
113 
114 		printf("usage: %s [", argv[0]);
115 		sep = " ";
116 		for (p = types; p->t_name; p++) {
117 			printf("%s%s", sep, p->t_name);
118 			if (*sep == ' ')
119 				sep = " | ";
120 		}
121 		printf(" ]\n");
122 		code = -1;
123 		return;
124 	}
125 	if (argc < 2) {
126 		printf("Using %s mode to transfer files.\n", typename);
127 		code = 0;
128 		return;
129 	}
130 	for (p = types; p->t_name; p++)
131 		if (strcmp(argv[1], p->t_name) == 0)
132 			break;
133 	if (p->t_name == 0) {
134 		printf("%s: unknown mode\n", argv[1]);
135 		code = -1;
136 		return;
137 	}
138 	if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
139 		comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
140 	else
141 		comret = command("TYPE %s", p->t_mode);
142 	if (comret == COMPLETE) {
143 		(void) strcpy(typename, p->t_name);
144 		type = p->t_type;
145 	}
146 }
147 
148 /*
149  * Set binary transfer type.
150  */
151 /*VARARGS*/
152 setbinary()
153 {
154 
155 	call(settype, "type", "binary", 0);
156 }
157 
158 /*
159  * Set ascii transfer type.
160  */
161 /*VARARGS*/
162 setascii()
163 {
164 
165 	call(settype, "type", "ascii", 0);
166 }
167 
168 /*
169  * Set tenex transfer type.
170  */
171 /*VARARGS*/
172 settenex()
173 {
174 
175 	call(settype, "type", "tenex", 0);
176 }
177 
178 /*
179  * Set ebcdic transfer type.
180  */
181 /*VARARGS*/
182 setebcdic()
183 {
184 
185 	call(settype, "type", "ebcdic", 0);
186 }
187 
188 /*
189  * Set file transfer mode.
190  */
191 /*ARGSUSED*/
192 setmode(argc, argv)
193 	char *argv[];
194 {
195 
196 	printf("We only support %s mode, sorry.\n", modename);
197 	code = -1;
198 }
199 
200 /*
201  * Set file transfer format.
202  */
203 /*ARGSUSED*/
204 setform(argc, argv)
205 	char *argv[];
206 {
207 
208 	printf("We only support %s format, sorry.\n", formname);
209 	code = -1;
210 }
211 
212 /*
213  * Set file transfer structure.
214  */
215 /*ARGSUSED*/
216 setstruct(argc, argv)
217 	char *argv[];
218 {
219 
220 	printf("We only support %s structure, sorry.\n", structname);
221 	code = -1;
222 }
223 
224 /*
225  * Send a single file.
226  */
227 put(argc, argv)
228 	int argc;
229 	char *argv[];
230 {
231 	char *cmd;
232 	int loc = 0;
233 	char *oldargv1;
234 
235 	if (argc == 2) {
236 		argc++;
237 		argv[2] = argv[1];
238 		loc++;
239 	}
240 	if (argc < 2) {
241 		(void) strcat(line, " ");
242 		printf("(local-file) ");
243 		(void) gets(&line[strlen(line)]);
244 		makeargv();
245 		argc = margc;
246 		argv = margv;
247 	}
248 	if (argc < 2) {
249 usage:
250 		printf("usage:%s local-file remote-file\n", argv[0]);
251 		code = -1;
252 		return;
253 	}
254 	if (argc < 3) {
255 		(void) strcat(line, " ");
256 		printf("(remote-file) ");
257 		(void) gets(&line[strlen(line)]);
258 		makeargv();
259 		argc = margc;
260 		argv = margv;
261 	}
262 	if (argc < 3)
263 		goto usage;
264 	oldargv1 = argv[1];
265 	if (!globulize(&argv[1])) {
266 		code = -1;
267 		return;
268 	}
269 	/*
270 	 * If "globulize" modifies argv[1], and argv[2] is a copy of
271 	 * the old argv[1], make it a copy of the new argv[1].
272 	 */
273 	if (argv[1] != oldargv1 && argv[2] == oldargv1) {
274 		argv[2] = argv[1];
275 	}
276 	cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
277 	if (loc && ntflag) {
278 		argv[2] = dotrans(argv[2]);
279 	}
280 	if (loc && mapflag) {
281 		argv[2] = domap(argv[2]);
282 	}
283 	sendrequest(cmd, argv[1], argv[2]);
284 }
285 
286 /*
287  * Send multiple files.
288  */
289 mput(argc, argv)
290 	char *argv[];
291 {
292 	register int i;
293 	int ointer, (*oldintr)(), mabort();
294 	extern jmp_buf jabort;
295 	char *tp;
296 
297 	if (argc < 2) {
298 		(void) strcat(line, " ");
299 		printf("(local-files) ");
300 		(void) gets(&line[strlen(line)]);
301 		makeargv();
302 		argc = margc;
303 		argv = margv;
304 	}
305 	if (argc < 2) {
306 		printf("usage:%s local-files\n", argv[0]);
307 		code = -1;
308 		return;
309 	}
310 	mname = argv[0];
311 	mflag = 1;
312 	oldintr = signal(SIGINT, mabort);
313 	(void) setjmp(jabort);
314 	if (proxy) {
315 		char *cp, *tp2, tmpbuf[MAXPATHLEN];
316 
317 		while ((cp = remglob(argv,0)) != NULL) {
318 			if (*cp == 0) {
319 				mflag = 0;
320 				continue;
321 			}
322 			if (mflag && confirm(argv[0], cp)) {
323 				tp = cp;
324 				if (mcase) {
325 					while (*tp && !islower(*tp)) {
326 						tp++;
327 					}
328 					if (!*tp) {
329 						tp = cp;
330 						tp2 = tmpbuf;
331 						while ((*tp2 = *tp) != NULL) {
332 						     if (isupper(*tp2)) {
333 						        *tp2 = 'a' + *tp2 - 'A';
334 						     }
335 						     tp++;
336 						     tp2++;
337 						}
338 					}
339 					tp = tmpbuf;
340 				}
341 				if (ntflag) {
342 					tp = dotrans(tp);
343 				}
344 				if (mapflag) {
345 					tp = domap(tp);
346 				}
347 				sendrequest((sunique) ? "STOU" : "STOR", cp,tp);
348 				if (!mflag && fromatty) {
349 					ointer = interactive;
350 					interactive = 1;
351 					if (confirm("Continue with","mput")) {
352 						mflag++;
353 					}
354 					interactive = ointer;
355 				}
356 			}
357 		}
358 		(void) signal(SIGINT, oldintr);
359 		mflag = 0;
360 		return;
361 	}
362 	for (i = 1; i < argc; i++) {
363 		register char **cpp, **gargs;
364 
365 		if (!doglob) {
366 			if (mflag && confirm(argv[0], argv[i])) {
367 				tp = (ntflag) ? dotrans(argv[i]) : argv[i];
368 				tp = (mapflag) ? domap(tp) : tp;
369 				sendrequest((sunique) ? "STOU" : "STOR",
370 				            argv[i], tp);
371 				if (!mflag && fromatty) {
372 					ointer = interactive;
373 					interactive = 1;
374 					if (confirm("Continue with","mput")) {
375 						mflag++;
376 					}
377 					interactive = ointer;
378 				}
379 			}
380 			continue;
381 		}
382 		gargs = glob(argv[i]);
383 		if (globerr != NULL) {
384 			printf("%s\n", globerr);
385 			if (gargs)
386 				blkfree(gargs);
387 			continue;
388 		}
389 		for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
390 			if (mflag && confirm(argv[0], *cpp)) {
391 				tp = (ntflag) ? dotrans(*cpp) : *cpp;
392 				tp = (mapflag) ? domap(tp) : tp;
393 				sendrequest((sunique) ? "STOU" : "STOR",
394 					   *cpp, tp);
395 				if (!mflag && fromatty) {
396 					ointer = interactive;
397 					interactive = 1;
398 					if (confirm("Continue with","mput")) {
399 						mflag++;
400 					}
401 					interactive = ointer;
402 				}
403 			}
404 		}
405 		if (gargs != NULL)
406 			blkfree(gargs);
407 	}
408 	(void) signal(SIGINT, oldintr);
409 	mflag = 0;
410 }
411 
412 /*
413  * Receive one file.
414  */
415 get(argc, argv)
416 	char *argv[];
417 {
418 	int loc = 0;
419 
420 	if (argc == 2) {
421 		argc++;
422 		argv[2] = argv[1];
423 		loc++;
424 	}
425 	if (argc < 2) {
426 		(void) strcat(line, " ");
427 		printf("(remote-file) ");
428 		(void) gets(&line[strlen(line)]);
429 		makeargv();
430 		argc = margc;
431 		argv = margv;
432 	}
433 	if (argc < 2) {
434 usage:
435 		printf("usage: %s remote-file [ local-file ]\n", argv[0]);
436 		code = -1;
437 		return;
438 	}
439 	if (argc < 3) {
440 		(void) strcat(line, " ");
441 		printf("(local-file) ");
442 		(void) gets(&line[strlen(line)]);
443 		makeargv();
444 		argc = margc;
445 		argv = margv;
446 	}
447 	if (argc < 3)
448 		goto usage;
449 	if (!globulize(&argv[2])) {
450 		code = -1;
451 		return;
452 	}
453 	if (loc && mcase) {
454 		char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
455 
456 		while (*tp && !islower(*tp)) {
457 			tp++;
458 		}
459 		if (!*tp) {
460 			tp = argv[2];
461 			tp2 = tmpbuf;
462 			while ((*tp2 = *tp) != NULL) {
463 				if (isupper(*tp2)) {
464 					*tp2 = 'a' + *tp2 - 'A';
465 				}
466 				tp++;
467 				tp2++;
468 			}
469 			argv[2] = tmpbuf;
470 		}
471 	}
472 	if (loc && ntflag) {
473 		argv[2] = dotrans(argv[2]);
474 	}
475 	if (loc && mapflag) {
476 		argv[2] = domap(argv[2]);
477 	}
478 	recvrequest("RETR", argv[2], argv[1], "w");
479 }
480 
481 mabort()
482 {
483 	int ointer;
484 	extern jmp_buf jabort;
485 
486 	printf("\n");
487 	(void) fflush(stdout);
488 	if (mflag && fromatty) {
489 		ointer = interactive;
490 		interactive = 1;
491 		if (confirm("Continue with", mname)) {
492 			interactive = ointer;
493 			longjmp(jabort,0);
494 		}
495 		interactive = ointer;
496 	}
497 	mflag = 0;
498 	longjmp(jabort,0);
499 }
500 
501 /*
502  * Get multiple files.
503  */
504 mget(argc, argv)
505 	char *argv[];
506 {
507 	char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
508 	int ointer, (*oldintr)(), mabort();
509 	extern jmp_buf jabort;
510 
511 	if (argc < 2) {
512 		(void) strcat(line, " ");
513 		printf("(remote-files) ");
514 		(void) gets(&line[strlen(line)]);
515 		makeargv();
516 		argc = margc;
517 		argv = margv;
518 	}
519 	if (argc < 2) {
520 		printf("usage:%s remote-files\n", argv[0]);
521 		code = -1;
522 		return;
523 	}
524 	mname = argv[0];
525 	mflag = 1;
526 	oldintr = signal(SIGINT,mabort);
527 	(void) setjmp(jabort);
528 	while ((cp = remglob(argv,proxy)) != NULL) {
529 		if (*cp == '\0') {
530 			mflag = 0;
531 			continue;
532 		}
533 		if (mflag && confirm(argv[0], cp)) {
534 			tp = cp;
535 			if (mcase) {
536 				while (*tp && !islower(*tp)) {
537 					tp++;
538 				}
539 				if (!*tp) {
540 					tp = cp;
541 					tp2 = tmpbuf;
542 					while ((*tp2 = *tp) != NULL) {
543 						if (isupper(*tp2)) {
544 							*tp2 = 'a' + *tp2 - 'A';
545 						}
546 						tp++;
547 						tp2++;
548 					}
549 				}
550 				tp = tmpbuf;
551 			}
552 			if (ntflag) {
553 				tp = dotrans(tp);
554 			}
555 			if (mapflag) {
556 				tp = domap(tp);
557 			}
558 			recvrequest("RETR", tp, cp, "w");
559 			if (!mflag && fromatty) {
560 				ointer = interactive;
561 				interactive = 1;
562 				if (confirm("Continue with","mget")) {
563 					mflag++;
564 				}
565 				interactive = ointer;
566 			}
567 		}
568 	}
569 	(void) signal(SIGINT,oldintr);
570 	mflag = 0;
571 }
572 
573 char *
574 remglob(argv,doswitch)
575 	char *argv[];
576 	int doswitch;
577 {
578 	char temp[16];
579 	static char buf[MAXPATHLEN];
580 	static FILE *ftemp = NULL;
581 	static char **args;
582 	int oldverbose, oldhash;
583 	char *cp, *mode;
584 
585 	if (!mflag) {
586 		if (!doglob) {
587 			args = NULL;
588 		}
589 		else {
590 			if (ftemp) {
591 				(void) fclose(ftemp);
592 				ftemp = NULL;
593 			}
594 		}
595 		return(NULL);
596 	}
597 	if (!doglob) {
598 		if (args == NULL)
599 			args = argv;
600 		if ((cp = *++args) == NULL)
601 			args = NULL;
602 		return (cp);
603 	}
604 	if (ftemp == NULL) {
605 		(void) strcpy(temp, "/tmp/ftpXXXXXX");
606 		(void) mktemp(temp);
607 		oldverbose = verbose, verbose = 0;
608 		oldhash = hash, hash = 0;
609 		if (doswitch) {
610 			pswitch(!proxy);
611 		}
612 		for (mode = "w"; *++argv != NULL; mode = "a")
613 			recvrequest ("NLST", temp, *argv, mode);
614 		if (doswitch) {
615 			pswitch(!proxy);
616 		}
617 		verbose = oldverbose; hash = oldhash;
618 		ftemp = fopen(temp, "r");
619 		(void) unlink(temp);
620 		if (ftemp == NULL) {
621 			printf("can't find list of remote files, oops\n");
622 			return (NULL);
623 		}
624 	}
625 	if (fgets(buf, sizeof (buf), ftemp) == NULL) {
626 		(void) fclose(ftemp), ftemp = NULL;
627 		return (NULL);
628 	}
629 	if ((cp = index(buf, '\n')) != NULL)
630 		*cp = '\0';
631 	return (buf);
632 }
633 
634 char *
635 onoff(bool)
636 	int bool;
637 {
638 
639 	return (bool ? "on" : "off");
640 }
641 
642 /*
643  * Show status.
644  */
645 /*ARGSUSED*/
646 status(argc, argv)
647 	char *argv[];
648 {
649 	int i;
650 
651 	if (connected)
652 		printf("Connected to %s.\n", hostname);
653 	else
654 		printf("Not connected.\n");
655 	if (!proxy) {
656 		pswitch(1);
657 		if (connected) {
658 			printf("Connected for proxy commands to %s.\n", hostname);
659 		}
660 		else {
661 			printf("No proxy connection.\n");
662 		}
663 		pswitch(0);
664 	}
665 	printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
666 		modename, typename, formname, structname);
667 	printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
668 		onoff(verbose), onoff(bell), onoff(interactive),
669 		onoff(doglob));
670 	printf("Store unique: %s; Receive unique: %s\n", onoff(sunique),
671 		onoff(runique));
672 	printf("Case: %s; CR stripping: %s\n",onoff(mcase),onoff(crflag));
673 	if (ntflag) {
674 		printf("Ntrans: (in) %s (out) %s\n", ntin,ntout);
675 	}
676 	else {
677 		printf("Ntrans: off\n");
678 	}
679 	if (mapflag) {
680 		printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
681 	}
682 	else {
683 		printf("Nmap: off\n");
684 	}
685 	printf("Hash mark printing: %s; Use of PORT cmds: %s\n",
686 		onoff(hash), onoff(sendport));
687 	if (macnum > 0) {
688 		printf("Macros:\n");
689 		for (i=0; i<macnum; i++) {
690 			printf("\t%s\n",macros[i].mac_name);
691 		}
692 	}
693 	code = 0;
694 }
695 
696 /*
697  * Set beep on cmd completed mode.
698  */
699 /*VARARGS*/
700 setbell()
701 {
702 
703 	bell = !bell;
704 	printf("Bell mode %s.\n", onoff(bell));
705 	code = bell;
706 }
707 
708 /*
709  * Turn on packet tracing.
710  */
711 /*VARARGS*/
712 settrace()
713 {
714 
715 	trace = !trace;
716 	printf("Packet tracing %s.\n", onoff(trace));
717 	code = trace;
718 }
719 
720 /*
721  * Toggle hash mark printing during transfers.
722  */
723 /*VARARGS*/
724 sethash()
725 {
726 
727 	hash = !hash;
728 	printf("Hash mark printing %s", onoff(hash));
729 	code = hash;
730 	if (hash)
731 		printf(" (%d bytes/hash mark)", BUFSIZ);
732 	printf(".\n");
733 }
734 
735 /*
736  * Turn on printing of server echo's.
737  */
738 /*VARARGS*/
739 setverbose()
740 {
741 
742 	verbose = !verbose;
743 	printf("Verbose mode %s.\n", onoff(verbose));
744 	code = verbose;
745 }
746 
747 /*
748  * Toggle PORT cmd use before each data connection.
749  */
750 /*VARARGS*/
751 setport()
752 {
753 
754 	sendport = !sendport;
755 	printf("Use of PORT cmds %s.\n", onoff(sendport));
756 	code = sendport;
757 }
758 
759 /*
760  * Turn on interactive prompting
761  * during mget, mput, and mdelete.
762  */
763 /*VARARGS*/
764 setprompt()
765 {
766 
767 	interactive = !interactive;
768 	printf("Interactive mode %s.\n", onoff(interactive));
769 	code = interactive;
770 }
771 
772 /*
773  * Toggle metacharacter interpretation
774  * on local file names.
775  */
776 /*VARARGS*/
777 setglob()
778 {
779 
780 	doglob = !doglob;
781 	printf("Globbing %s.\n", onoff(doglob));
782 	code = doglob;
783 }
784 
785 /*
786  * Set debugging mode on/off and/or
787  * set level of debugging.
788  */
789 /*VARARGS*/
790 setdebug(argc, argv)
791 	char *argv[];
792 {
793 	int val;
794 
795 	if (argc > 1) {
796 		val = atoi(argv[1]);
797 		if (val < 0) {
798 			printf("%s: bad debugging value.\n", argv[1]);
799 			code = -1;
800 			return;
801 		}
802 	} else
803 		val = !debug;
804 	debug = val;
805 	if (debug)
806 		options |= SO_DEBUG;
807 	else
808 		options &= ~SO_DEBUG;
809 	printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
810 	code = debug > 0;
811 }
812 
813 /*
814  * Set current working directory
815  * on remote machine.
816  */
817 cd(argc, argv)
818 	char *argv[];
819 {
820 
821 	if (argc < 2) {
822 		(void) strcat(line, " ");
823 		printf("(remote-directory) ");
824 		(void) gets(&line[strlen(line)]);
825 		makeargv();
826 		argc = margc;
827 		argv = margv;
828 	}
829 	if (argc < 2) {
830 		printf("usage:%s remote-directory\n", argv[0]);
831 		code = -1;
832 		return;
833 	}
834 	(void) command("CWD %s", argv[1]);
835 }
836 
837 /*
838  * Set current working directory
839  * on local machine.
840  */
841 lcd(argc, argv)
842 	char *argv[];
843 {
844 	char buf[MAXPATHLEN];
845 
846 	if (argc < 2)
847 		argc++, argv[1] = home;
848 	if (argc != 2) {
849 		printf("usage:%s local-directory\n", argv[0]);
850 		code = -1;
851 		return;
852 	}
853 	if (!globulize(&argv[1])) {
854 		code = -1;
855 		return;
856 	}
857 	if (chdir(argv[1]) < 0) {
858 		perror(argv[1]);
859 		code = -1;
860 		return;
861 	}
862 	printf("Local directory now %s\n", getwd(buf));
863 	code = 0;
864 }
865 
866 /*
867  * Delete a single file.
868  */
869 delete(argc, argv)
870 	char *argv[];
871 {
872 
873 	if (argc < 2) {
874 		(void) strcat(line, " ");
875 		printf("(remote-file) ");
876 		(void) gets(&line[strlen(line)]);
877 		makeargv();
878 		argc = margc;
879 		argv = margv;
880 	}
881 	if (argc < 2) {
882 		printf("usage:%s remote-file\n", argv[0]);
883 		code = -1;
884 		return;
885 	}
886 	(void) command("DELE %s", argv[1]);
887 }
888 
889 /*
890  * Delete multiple files.
891  */
892 mdelete(argc, argv)
893 	char *argv[];
894 {
895 	char *cp;
896 	int ointer, (*oldintr)(), mabort();
897 	extern jmp_buf jabort;
898 
899 	if (argc < 2) {
900 		(void) strcat(line, " ");
901 		printf("(remote-files) ");
902 		(void) gets(&line[strlen(line)]);
903 		makeargv();
904 		argc = margc;
905 		argv = margv;
906 	}
907 	if (argc < 2) {
908 		printf("usage:%s remote-files\n", argv[0]);
909 		code = -1;
910 		return;
911 	}
912 	mname = argv[0];
913 	mflag = 1;
914 	oldintr = signal(SIGINT, mabort);
915 	(void) setjmp(jabort);
916 	while ((cp = remglob(argv,0)) != NULL) {
917 		if (*cp == '\0') {
918 			mflag = 0;
919 			continue;
920 		}
921 		if (mflag && confirm(argv[0], cp)) {
922 			(void) command("DELE %s", cp);
923 			if (!mflag && fromatty) {
924 				ointer = interactive;
925 				interactive = 1;
926 				if (confirm("Continue with", "mdelete")) {
927 					mflag++;
928 				}
929 				interactive = ointer;
930 			}
931 		}
932 	}
933 	(void) signal(SIGINT, oldintr);
934 	mflag = 0;
935 }
936 
937 /*
938  * Rename a remote file.
939  */
940 renamefile(argc, argv)
941 	char *argv[];
942 {
943 
944 	if (argc < 2) {
945 		(void) strcat(line, " ");
946 		printf("(from-name) ");
947 		(void) gets(&line[strlen(line)]);
948 		makeargv();
949 		argc = margc;
950 		argv = margv;
951 	}
952 	if (argc < 2) {
953 usage:
954 		printf("%s from-name to-name\n", argv[0]);
955 		code = -1;
956 		return;
957 	}
958 	if (argc < 3) {
959 		(void) strcat(line, " ");
960 		printf("(to-name) ");
961 		(void) gets(&line[strlen(line)]);
962 		makeargv();
963 		argc = margc;
964 		argv = margv;
965 	}
966 	if (argc < 3)
967 		goto usage;
968 	if (command("RNFR %s", argv[1]) == CONTINUE)
969 		(void) command("RNTO %s", argv[2]);
970 }
971 
972 /*
973  * Get a directory listing
974  * of remote files.
975  */
976 ls(argc, argv)
977 	char *argv[];
978 {
979 	char *cmd;
980 
981 	if (argc < 2)
982 		argc++, argv[1] = NULL;
983 	if (argc < 3)
984 		argc++, argv[2] = "-";
985 	if (argc > 3) {
986 		printf("usage: %s remote-directory local-file\n", argv[0]);
987 		code = -1;
988 		return;
989 	}
990 	cmd = argv[0][0] == 'l' ? "NLST" : "LIST";
991 	if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
992 		code = -1;
993 		return;
994 	}
995 	if (strcmp(argv[2], "-") && *argv[2] != '|')
996 		if (!globulize(&argv[2]) || !confirm("output to local-file:", argv[2])) {
997 			code = -1;
998 			return;
999 	}
1000 	recvrequest(cmd, argv[2], argv[1], "w");
1001 }
1002 
1003 /*
1004  * Get a directory listing
1005  * of multiple remote files.
1006  */
1007 mls(argc, argv)
1008 	char *argv[];
1009 {
1010 	char *cmd, mode[1], *dest;
1011 	int ointer, i, (*oldintr)(), mabort();
1012 	extern jmp_buf jabort;
1013 
1014 	if (argc < 2) {
1015 		(void) strcat(line, " ");
1016 		printf("(remote-files) ");
1017 		(void) gets(&line[strlen(line)]);
1018 		makeargv();
1019 		argc = margc;
1020 		argv = margv;
1021 	}
1022 	if (argc < 3) {
1023 		(void) strcat(line, " ");
1024 		printf("(local-file) ");
1025 		(void) gets(&line[strlen(line)]);
1026 		makeargv();
1027 		argc = margc;
1028 		argv = margv;
1029 	}
1030 	if (argc < 3) {
1031 		printf("usage:%s remote-files local-file\n", argv[0]);
1032 		code = -1;
1033 		return;
1034 	}
1035 	dest = argv[argc - 1];
1036 	argv[argc - 1] = NULL;
1037 	if (strcmp(dest, "-") && *dest != '|')
1038 		if (!globulize(&dest) || !confirm("output to local-file:", dest)) {
1039 			code = -1;
1040 			return;
1041 	}
1042 	cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
1043 	mname = argv[0];
1044 	mflag = 1;
1045 	oldintr = signal(SIGINT, mabort);
1046 	(void) setjmp(jabort);
1047 	for (i = 1; mflag && i < argc-1; ++i) {
1048 		*mode = (i == 1) ? 'w' : 'a';
1049 		recvrequest(cmd, dest, argv[i], mode);
1050 		if (!mflag && fromatty) {
1051 			ointer = interactive;
1052 			interactive = 1;
1053 			if (confirm("Continue with", argv[0])) {
1054 				mflag ++;
1055 			}
1056 			interactive = ointer;
1057 		}
1058 	}
1059 	(void) signal(SIGINT, oldintr);
1060 	mflag = 0;
1061 }
1062 
1063 /*
1064  * Do a shell escape
1065  */
1066 /*ARGSUSED*/
1067 shell(argc, argv)
1068 	char *argv[];
1069 {
1070 	int pid, (*old1)(), (*old2)();
1071 	char shellnam[40], *shell, *namep;
1072 	union wait status;
1073 
1074 	old1 = signal (SIGINT, SIG_IGN);
1075 	old2 = signal (SIGQUIT, SIG_IGN);
1076 	if ((pid = fork()) == 0) {
1077 		for (pid = 3; pid < 20; pid++)
1078 			(void) close(pid);
1079 		(void) signal(SIGINT, SIG_DFL);
1080 		(void) signal(SIGQUIT, SIG_DFL);
1081 		shell = getenv("SHELL");
1082 		if (shell == NULL)
1083 			shell = "/bin/sh";
1084 		namep = rindex(shell,'/');
1085 		if (namep == NULL)
1086 			namep = shell;
1087 		(void) strcpy(shellnam,"-");
1088 		(void) strcat(shellnam, ++namep);
1089 		if (strcmp(namep, "sh") != 0)
1090 			shellnam[0] = '+';
1091 		if (debug) {
1092 			printf ("%s\n", shell);
1093 			(void) fflush (stdout);
1094 		}
1095 		if (argc > 1) {
1096 			execl(shell,shellnam,"-c",altarg,(char *)0);
1097 		}
1098 		else {
1099 			execl(shell,shellnam,(char *)0);
1100 		}
1101 		perror(shell);
1102 		code = -1;
1103 		exit(1);
1104 		}
1105 	if (pid > 0)
1106 		while (wait(&status) != pid)
1107 			;
1108 	(void) signal(SIGINT, old1);
1109 	(void) signal(SIGQUIT, old2);
1110 	if (pid == -1) {
1111 		perror("Try again later");
1112 		code = -1;
1113 	}
1114 	else {
1115 		code = 0;
1116 	}
1117 	return (0);
1118 }
1119 
1120 /*
1121  * Send new user information (re-login)
1122  */
1123 user(argc, argv)
1124 	int argc;
1125 	char **argv;
1126 {
1127 	char acct[80], *mygetpass();
1128 	int n, aflag = 0;
1129 
1130 	if (argc < 2) {
1131 		(void) strcat(line, " ");
1132 		printf("(username) ");
1133 		(void) gets(&line[strlen(line)]);
1134 		makeargv();
1135 		argc = margc;
1136 		argv = margv;
1137 	}
1138 	if (argc > 4) {
1139 		printf("usage: %s username [password] [account]\n", argv[0]);
1140 		code = -1;
1141 		return (0);
1142 	}
1143 	n = command("USER %s", argv[1]);
1144 	if (n == CONTINUE) {
1145 		if (argc < 3 )
1146 			argv[2] = mygetpass("Password: "), argc++;
1147 		n = command("PASS %s", argv[2]);
1148 	}
1149 	if (n == CONTINUE) {
1150 		if (argc < 4) {
1151 			printf("Account: "); (void) fflush(stdout);
1152 			(void) fgets(acct, sizeof(acct) - 1, stdin);
1153 			acct[strlen(acct) - 1] = '\0';
1154 			argv[3] = acct; argc++;
1155 		}
1156 		n = command("ACCT %s", argv[3]);
1157 		aflag++;
1158 	}
1159 	if (n != COMPLETE) {
1160 		fprintf(stdout, "Login failed.\n");
1161 		return (0);
1162 	}
1163 	if (!aflag && argc == 4) {
1164 		(void) command("ACCT %s", argv[3]);
1165 	}
1166 	return (1);
1167 }
1168 
1169 /*
1170  * Print working directory.
1171  */
1172 /*VARARGS*/
1173 pwd()
1174 {
1175 
1176 	(void) command("PWD");
1177 }
1178 
1179 /*
1180  * Make a directory.
1181  */
1182 makedir(argc, argv)
1183 	char *argv[];
1184 {
1185 
1186 	if (argc < 2) {
1187 		(void) strcat(line, " ");
1188 		printf("(directory-name) ");
1189 		(void) gets(&line[strlen(line)]);
1190 		makeargv();
1191 		argc = margc;
1192 		argv = margv;
1193 	}
1194 	if (argc < 2) {
1195 		printf("usage: %s directory-name\n", argv[0]);
1196 		code = -1;
1197 		return;
1198 	}
1199 	(void) command("MKD %s", argv[1]);
1200 }
1201 
1202 /*
1203  * Remove a directory.
1204  */
1205 removedir(argc, argv)
1206 	char *argv[];
1207 {
1208 
1209 	if (argc < 2) {
1210 		(void) strcat(line, " ");
1211 		printf("(directory-name) ");
1212 		(void) gets(&line[strlen(line)]);
1213 		makeargv();
1214 		argc = margc;
1215 		argv = margv;
1216 	}
1217 	if (argc < 2) {
1218 		printf("usage: %s directory-name\n", argv[0]);
1219 		code = -1;
1220 		return;
1221 	}
1222 	(void) command("RMD %s", argv[1]);
1223 }
1224 
1225 /*
1226  * Send a line, verbatim, to the remote machine.
1227  */
1228 quote(argc, argv)
1229 	char *argv[];
1230 {
1231 	int i;
1232 	char buf[BUFSIZ];
1233 
1234 	if (argc < 2) {
1235 		(void) strcat(line, " ");
1236 		printf("(command line to send) ");
1237 		(void) gets(&line[strlen(line)]);
1238 		makeargv();
1239 		argc = margc;
1240 		argv = margv;
1241 	}
1242 	if (argc < 2) {
1243 		printf("usage: %s line-to-send\n", argv[0]);
1244 		code = -1;
1245 		return;
1246 	}
1247 	(void) strcpy(buf, argv[1]);
1248 	for (i = 2; i < argc; i++) {
1249 		(void) strcat(buf, " ");
1250 		(void) strcat(buf, argv[i]);
1251 	}
1252 	if (command(buf) == PRELIM) {
1253 		while (getreply(0) == PRELIM);
1254 	}
1255 }
1256 
1257 /*
1258  * Ask the other side for help.
1259  */
1260 rmthelp(argc, argv)
1261 	char *argv[];
1262 {
1263 	int oldverbose = verbose;
1264 
1265 	verbose = 1;
1266 	(void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1267 	verbose = oldverbose;
1268 }
1269 
1270 /*
1271  * Terminate session and exit.
1272  */
1273 /*VARARGS*/
1274 quit()
1275 {
1276 
1277 	if (connected)
1278 		disconnect();
1279 	pswitch(1);
1280 	if (connected) {
1281 		disconnect();
1282 	}
1283 	exit(0);
1284 }
1285 
1286 /*
1287  * Terminate session, but don't exit.
1288  */
1289 disconnect()
1290 {
1291 	extern FILE *cout;
1292 	extern int data;
1293 
1294 	if (!connected)
1295 		return;
1296 	(void) command("QUIT");
1297 	if (cout) {
1298 		(void) fclose(cout);
1299 	}
1300 	cout = NULL;
1301 	connected = 0;
1302 	data = -1;
1303 	if (!proxy) {
1304 		macnum = 0;
1305 	}
1306 }
1307 
1308 confirm(cmd, file)
1309 	char *cmd, *file;
1310 {
1311 	char line[BUFSIZ];
1312 
1313 	if (!interactive)
1314 		return (1);
1315 	printf("%s %s? ", cmd, file);
1316 	(void) fflush(stdout);
1317 	(void) gets(line);
1318 	return (*line != 'n' && *line != 'N');
1319 }
1320 
1321 fatal(msg)
1322 	char *msg;
1323 {
1324 
1325 	fprintf(stderr, "ftp: %s\n", msg);
1326 	exit(1);
1327 }
1328 
1329 /*
1330  * Glob a local file name specification with
1331  * the expectation of a single return value.
1332  * Can't control multiple values being expanded
1333  * from the expression, we return only the first.
1334  */
1335 globulize(cpp)
1336 	char **cpp;
1337 {
1338 	char **globbed;
1339 
1340 	if (!doglob)
1341 		return (1);
1342 	globbed = glob(*cpp);
1343 	if (globerr != NULL) {
1344 		printf("%s: %s\n", *cpp, globerr);
1345 		if (globbed)
1346 			blkfree(globbed);
1347 		return (0);
1348 	}
1349 	if (globbed) {
1350 		*cpp = *globbed++;
1351 		/* don't waste too much memory */
1352 		if (*globbed)
1353 			blkfree(globbed);
1354 	}
1355 	return (1);
1356 }
1357 
1358 account(argc,argv)
1359 
1360 	int argc;
1361 	char **argv;
1362 {
1363 	char acct[50], *mygetpass(), *ap;
1364 
1365 	if (argc > 1) {
1366 		++argv;
1367 		--argc;
1368 		(void) strncpy(acct,*argv,49);
1369 		acct[50] = '\0';
1370 		while (argc > 1) {
1371 			--argc;
1372 			++argv;
1373 			(void) strncat(acct,*argv, 49-strlen(acct));
1374 		}
1375 		ap = acct;
1376 	}
1377 	else {
1378 		ap = mygetpass("Account:");
1379 	}
1380 	(void) command("ACCT %s", ap);
1381 }
1382 
1383 jmp_buf abortprox;
1384 
1385 proxabort()
1386 {
1387 	extern int proxy;
1388 
1389 	if (!proxy) {
1390 		pswitch(1);
1391 	}
1392 	if (connected) {
1393 		proxflag = 1;
1394 	}
1395 	else {
1396 		proxflag = 0;
1397 	}
1398 	pswitch(0);
1399 	longjmp(abortprox,1);
1400 }
1401 
1402 doproxy(argc,argv)
1403 	int argc;
1404 	char *argv[];
1405 {
1406 	int (*oldintr)(), proxabort();
1407 	register struct cmd *c;
1408 	struct cmd *getcmd();
1409 	extern struct cmd cmdtab[];
1410 	extern jmp_buf abortprox;
1411 
1412 	if (argc < 2) {
1413 		(void) strcat(line, " ");
1414 		printf("(command) ");
1415 		(void) gets(&line[strlen(line)]);
1416 		makeargv();
1417 		argc = margc;
1418 		argv = margv;
1419 	}
1420 	if (argc < 2) {
1421 		printf("usage:%s command\n", argv[0]);
1422 		code = -1;
1423 		return;
1424 	}
1425 	c = getcmd(argv[1]);
1426 	if (c == (struct cmd *) -1) {
1427 		printf("?Ambiguous command\n");
1428 		(void) fflush(stdout);
1429 		code = -1;
1430 		return;
1431 	}
1432 	if (c == 0) {
1433 		printf("?Invalid command\n");
1434 		(void) fflush(stdout);
1435 		code = -1;
1436 		return;
1437 	}
1438 	if (!c->c_proxy) {
1439 		printf("?Invalid proxy command\n");
1440 		(void) fflush(stdout);
1441 		code = -1;
1442 		return;
1443 	}
1444 	if (setjmp(abortprox)) {
1445 		code = -1;
1446 		return;
1447 	}
1448 	oldintr = signal(SIGINT, proxabort);
1449 	pswitch(1);
1450 	if (c->c_conn && !connected) {
1451 		printf("Not connected\n");
1452 		(void) fflush(stdout);
1453 		pswitch(0);
1454 		(void) signal(SIGINT, oldintr);
1455 		code = -1;
1456 		return;
1457 	}
1458 	(*c->c_handler)(argc-1, argv+1);
1459 	if (connected) {
1460 		proxflag = 1;
1461 	}
1462 	else {
1463 		proxflag = 0;
1464 	}
1465 	pswitch(0);
1466 	(void) signal(SIGINT, oldintr);
1467 }
1468 
1469 setcase()
1470 {
1471 	mcase = !mcase;
1472 	printf("Case mapping %s.\n", onoff(mcase));
1473 	code = mcase;
1474 }
1475 
1476 setcr()
1477 {
1478 	crflag = !crflag;
1479 	printf("Carriage Return stripping %s.\n", onoff(crflag));
1480 	code = crflag;
1481 }
1482 
1483 setntrans(argc,argv)
1484 	int argc;
1485 	char *argv[];
1486 {
1487 	if (argc == 1) {
1488 		ntflag = 0;
1489 		printf("Ntrans off.\n");
1490 		code = ntflag;
1491 		return;
1492 	}
1493 	ntflag++;
1494 	code = ntflag;
1495 	(void) strncpy(ntin, argv[1], 16);
1496 	ntin[16] = '\0';
1497 	if (argc == 2) {
1498 		ntout[0] = '\0';
1499 		return;
1500 	}
1501 	(void) strncpy(ntout, argv[2], 16);
1502 	ntout[16] = '\0';
1503 }
1504 
1505 char *
1506 dotrans(name)
1507 	char *name;
1508 {
1509 	static char new[MAXPATHLEN];
1510 	char *cp1, *cp2 = new;
1511 	register int i, ostop, found;
1512 
1513 	for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++);
1514 	for (cp1 = name; *cp1; cp1++) {
1515 		found = 0;
1516 		for (i = 0; *(ntin + i) && i < 16; i++) {
1517 			if (*cp1 == *(ntin + i)) {
1518 				found++;
1519 				if (i < ostop) {
1520 					*cp2++ = *(ntout + i);
1521 				}
1522 				break;
1523 			}
1524 		}
1525 		if (!found) {
1526 			*cp2++ = *cp1;
1527 		}
1528 	}
1529 	*cp2 = '\0';
1530 	return(new);
1531 }
1532 
1533 setnmap(argc, argv)
1534 	int argc;
1535 	char *argv[];
1536 {
1537 	char *cp;
1538 
1539 	if (argc == 1) {
1540 		mapflag = 0;
1541 		printf("Nmap off.\n");
1542 		code = mapflag;
1543 		return;
1544 	}
1545 	if (argc < 3) {
1546 		(void) strcat(line, " ");
1547 		printf("(mapout) ");
1548 		(void) gets(&line[strlen(line)]);
1549 		makeargv();
1550 		argc = margc;
1551 		argv = margv;
1552 	}
1553 	if (argc < 3) {
1554 		printf("Usage: %s [mapin mapout]\n",argv[0]);
1555 		code = -1;
1556 		return;
1557 	}
1558 	mapflag = 1;
1559 	code = 1;
1560 	cp = index(altarg, ' ');
1561 	if (proxy) {
1562 		while(*++cp == ' ');
1563 		altarg = cp;
1564 		cp = index(altarg, ' ');
1565 	}
1566 	*cp = '\0';
1567 	(void) strncpy(mapin, altarg, MAXPATHLEN - 1);
1568 	while (*++cp == ' ');
1569 	(void) strncpy(mapout, cp, MAXPATHLEN - 1);
1570 }
1571 
1572 char *
1573 domap(name)
1574 	char *name;
1575 {
1576 	static char new[MAXPATHLEN];
1577 	register char *cp1 = name, *cp2 = mapin;
1578 	char *tp[9], *te[9];
1579 	int i, toks[9], toknum, match = 1;
1580 
1581 	for (i=0; i < 9; ++i) {
1582 		toks[i] = 0;
1583 	}
1584 	while (match && *cp1 && *cp2) {
1585 		switch (*cp2) {
1586 			case '\\':
1587 				if (*++cp2 != *cp1) {
1588 					match = 0;
1589 				}
1590 				break;
1591 			case '$':
1592 				if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1593 					if (*cp1 != *(++cp2+1)) {
1594 						toks[toknum = *cp2 - '1']++;
1595 						tp[toknum] = cp1;
1596 						while (*++cp1 && *(cp2+1)
1597 							!= *cp1);
1598 						te[toknum] = cp1;
1599 					}
1600 					cp2++;
1601 					break;
1602 				}
1603 				/* intentional drop through */
1604 			default:
1605 				if (*cp2 != *cp1) {
1606 					match = 0;
1607 				}
1608 				break;
1609 		}
1610 		if (*cp1) {
1611 			cp1++;
1612 		}
1613 		if (*cp2) {
1614 			cp2++;
1615 		}
1616 	}
1617 	cp1 = new;
1618 	*cp1 = '\0';
1619 	cp2 = mapout;
1620 	while (*cp2) {
1621 		match = 0;
1622 		switch (*cp2) {
1623 			case '\\':
1624 				if (*(cp2 + 1)) {
1625 					*cp1++ = *++cp2;
1626 				}
1627 				break;
1628 			case '[':
1629 LOOP:
1630 				if (*++cp2 == '$' && isdigit(*(cp2+1))) {
1631 					if (*++cp2 == '0') {
1632 						char *cp3 = name;
1633 
1634 						while (*cp3) {
1635 							*cp1++ = *cp3++;
1636 						}
1637 						match = 1;
1638 					}
1639 					else if (toks[toknum = *cp2 - '1']) {
1640 						char *cp3 = tp[toknum];
1641 
1642 						while (cp3 != te[toknum]) {
1643 							*cp1++ = *cp3++;
1644 						}
1645 						match = 1;
1646 					}
1647 				}
1648 				else {
1649 					while (*cp2 && *cp2 != ',' &&
1650 					    *cp2 != ']') {
1651 						if (*cp2 == '\\') {
1652 							cp2++;
1653 						}
1654 						else if (*cp2 == '$' &&
1655    						        isdigit(*(cp2+1))) {
1656 							if (*++cp2 == '0') {
1657 							   char *cp3 = name;
1658 
1659 							   while (*cp3) {
1660 								*cp1++ = *cp3++;
1661 							   }
1662 							}
1663 							else if (toks[toknum =
1664 							    *cp2 - '1']) {
1665 							   char *cp3=tp[toknum];
1666 
1667 							   while (cp3 !=
1668 								  te[toknum]) {
1669 								*cp1++ = *cp3++;
1670 							   }
1671 							}
1672 						}
1673 						else if (*cp2) {
1674 							*cp1++ = *cp2++;
1675 						}
1676 					}
1677 					if (!*cp2) {
1678 						printf("nmap: unbalanced brackets\n");
1679 						return(name);
1680 					}
1681 					match = 1;
1682 					cp2--;
1683 				}
1684 				if (match) {
1685 					while (*++cp2 && *cp2 != ']') {
1686 					      if (*cp2 == '\\' && *(cp2 + 1)) {
1687 							cp2++;
1688 					      }
1689 					}
1690 					if (!*cp2) {
1691 						printf("nmap: unbalanced brackets\n");
1692 						return(name);
1693 					}
1694 					break;
1695 				}
1696 				switch (*++cp2) {
1697 					case ',':
1698 						goto LOOP;
1699 					case ']':
1700 						break;
1701 					default:
1702 						cp2--;
1703 						goto LOOP;
1704 				}
1705 				break;
1706 			case '$':
1707 				if (isdigit(*(cp2 + 1))) {
1708 					if (*++cp2 == '0') {
1709 						char *cp3 = name;
1710 
1711 						while (*cp3) {
1712 							*cp1++ = *cp3++;
1713 						}
1714 					}
1715 					else if (toks[toknum = *cp2 - '1']) {
1716 						char *cp3 = tp[toknum];
1717 
1718 						while (cp3 != te[toknum]) {
1719 							*cp1++ = *cp3++;
1720 						}
1721 					}
1722 					break;
1723 				}
1724 				/* intentional drop through */
1725 			default:
1726 				*cp1++ = *cp2;
1727 				break;
1728 		}
1729 		cp2++;
1730 	}
1731 	*cp1 = '\0';
1732 	if (!*new) {
1733 		return(name);
1734 	}
1735 	return(new);
1736 }
1737 
1738 setsunique()
1739 {
1740 	sunique = !sunique;
1741 	printf("Store unique %s.\n", onoff(sunique));
1742 	code = sunique;
1743 }
1744 
1745 setrunique()
1746 {
1747 	runique = !runique;
1748 	printf("Receive unique %s.\n", onoff(runique));
1749 	code = runique;
1750 }
1751 
1752 /* change directory to perent directory */
1753 cdup()
1754 {
1755 	(void) command("CDUP");
1756 }
1757 
1758 macdef(argc, argv)
1759 	int argc;
1760 	char *argv[];
1761 {
1762 	char *tmp;
1763 	int c;
1764 
1765 	if (macnum == 16) {
1766 		printf("Limit of 16 macros have already been defined\n");
1767 		code = -1;
1768 		return;
1769 	}
1770 	if (argc < 2) {
1771 		(void) strcat(line, " ");
1772 		printf("(macro name) ");
1773 		(void) gets(&line[strlen(line)]);
1774 		makeargv();
1775 		argc = margc;
1776 		argv = margv;
1777 	}
1778 	if (argc != 2) {
1779 		printf("Usage: %s macro_name\n",argv[0]);
1780 		code = -1;
1781 		return;
1782 	}
1783 	if (interactive) {
1784 		printf("Enter macro line by line, terminating it with a null line\n");
1785 	}
1786 	(void) strncpy(macros[macnum].mac_name, argv[1], 8);
1787 	if (macnum == 0) {
1788 		macros[macnum].mac_start = macbuf;
1789 	}
1790 	else {
1791 		macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
1792 	}
1793 	tmp = macros[macnum].mac_start;
1794 	while (tmp != macbuf+4096) {
1795 		if ((c = getchar()) == EOF) {
1796 			printf("macdef:end of file encountered\n");
1797 			code = -1;
1798 			return;
1799 		}
1800 		if ((*tmp = c) == '\n') {
1801 			if (tmp == macros[macnum].mac_start) {
1802 				macros[macnum++].mac_end = tmp;
1803 				code = 0;
1804 				return;
1805 			}
1806 			if (*(tmp-1) == '\0') {
1807 				macros[macnum++].mac_end = tmp - 1;
1808 				code = 0;
1809 				return;
1810 			}
1811 			*tmp = '\0';
1812 		}
1813 		tmp++;
1814 	}
1815 	while (1) {
1816 		while ((c = getchar()) != '\n' && c != EOF);
1817 		if (c == EOF || getchar() == '\n') {
1818 			printf("Macro not defined - 4k buffer exceeded\n");
1819 			code = -1;
1820 			return;
1821 		}
1822 	}
1823 }
1824