xref: /csrg-svn/usr.bin/ftp/cmds.c (revision 26497)
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.5 (Berkeley) 03/07/86";
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 	recvrequest(cmd, argv[2], argv[1], "w");
996 }
997 
998 /*
999  * Get a directory listing
1000  * of multiple remote files.
1001  */
1002 mls(argc, argv)
1003 	char *argv[];
1004 {
1005 	char *cmd, mode[1], *dest;
1006 	int ointer, i, (*oldintr)(), mabort();
1007 	extern jmp_buf jabort;
1008 
1009 	if (argc < 2) {
1010 		(void) strcat(line, " ");
1011 		printf("(remote-files) ");
1012 		(void) gets(&line[strlen(line)]);
1013 		makeargv();
1014 		argc = margc;
1015 		argv = margv;
1016 	}
1017 	if (argc < 3) {
1018 		(void) strcat(line, " ");
1019 		printf("(local-file) ");
1020 		(void) gets(&line[strlen(line)]);
1021 		makeargv();
1022 		argc = margc;
1023 		argv = margv;
1024 	}
1025 	if (argc < 3) {
1026 		printf("usage:%s remote-files local-file\n", argv[0]);
1027 		code = -1;
1028 		return;
1029 	}
1030 	dest = argv[argc - 1];
1031 	argv[argc - 1] = NULL;
1032 	if (strcmp(dest, "-") && *dest != '|')
1033 		if (!globulize(&dest) || !confirm("output to local-file:", dest)) {
1034 			code = -1;
1035 			return;
1036 	}
1037 	cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
1038 	mname = argv[0];
1039 	mflag = 1;
1040 	oldintr = signal(SIGINT, mabort);
1041 	(void) setjmp(jabort);
1042 	for (i = 1; mflag && i < argc-1; ++i) {
1043 		*mode = (i == 1) ? 'w' : 'a';
1044 		recvrequest(cmd, dest, argv[i], mode);
1045 		if (!mflag && fromatty) {
1046 			ointer = interactive;
1047 			interactive = 1;
1048 			if (confirm("Continue with", argv[0])) {
1049 				mflag ++;
1050 			}
1051 			interactive = ointer;
1052 		}
1053 	}
1054 	(void) signal(SIGINT, oldintr);
1055 	mflag = 0;
1056 }
1057 
1058 /*
1059  * Do a shell escape
1060  */
1061 /*ARGSUSED*/
1062 shell(argc, argv)
1063 	char *argv[];
1064 {
1065 	int pid, (*old1)(), (*old2)();
1066 	char shellnam[40], *shell, *namep;
1067 	union wait status;
1068 
1069 	old1 = signal (SIGINT, SIG_IGN);
1070 	old2 = signal (SIGQUIT, SIG_IGN);
1071 	if ((pid = fork()) == 0) {
1072 		for (pid = 3; pid < 20; pid++)
1073 			(void) close(pid);
1074 		(void) signal(SIGINT, SIG_DFL);
1075 		(void) signal(SIGQUIT, SIG_DFL);
1076 		shell = getenv("SHELL");
1077 		if (shell == NULL)
1078 			shell = "/bin/sh";
1079 		namep = rindex(shell,'/');
1080 		if (namep == NULL)
1081 			namep = shell;
1082 		(void) strcpy(shellnam,"-");
1083 		(void) strcat(shellnam, ++namep);
1084 		if (strcmp(namep, "sh") != 0)
1085 			shellnam[0] = '+';
1086 		if (debug) {
1087 			printf ("%s\n", shell);
1088 			(void) fflush (stdout);
1089 		}
1090 		if (argc > 1) {
1091 			execl(shell,shellnam,"-c",altarg,(char *)0);
1092 		}
1093 		else {
1094 			execl(shell,shellnam,(char *)0);
1095 		}
1096 		perror(shell);
1097 		code = -1;
1098 		exit(1);
1099 		}
1100 	if (pid > 0)
1101 		while (wait(&status) != pid)
1102 			;
1103 	(void) signal(SIGINT, old1);
1104 	(void) signal(SIGQUIT, old2);
1105 	if (pid == -1) {
1106 		perror("Try again later");
1107 		code = -1;
1108 	}
1109 	else {
1110 		code = 0;
1111 	}
1112 	return (0);
1113 }
1114 
1115 /*
1116  * Send new user information (re-login)
1117  */
1118 user(argc, argv)
1119 	int argc;
1120 	char **argv;
1121 {
1122 	char acct[80], *mygetpass();
1123 	int n, aflag = 0;
1124 
1125 	if (argc < 2) {
1126 		(void) strcat(line, " ");
1127 		printf("(username) ");
1128 		(void) gets(&line[strlen(line)]);
1129 		makeargv();
1130 		argc = margc;
1131 		argv = margv;
1132 	}
1133 	if (argc > 4) {
1134 		printf("usage: %s username [password] [account]\n", argv[0]);
1135 		code = -1;
1136 		return (0);
1137 	}
1138 	n = command("USER %s", argv[1]);
1139 	if (n == CONTINUE) {
1140 		if (argc < 3 )
1141 			argv[2] = mygetpass("Password: "), argc++;
1142 		n = command("PASS %s", argv[2]);
1143 	}
1144 	if (n == CONTINUE) {
1145 		if (argc < 4) {
1146 			printf("Account: "); (void) fflush(stdout);
1147 			(void) fgets(acct, sizeof(acct) - 1, stdin);
1148 			acct[strlen(acct) - 1] = '\0';
1149 			argv[3] = acct; argc++;
1150 		}
1151 		n = command("ACCT %s", argv[3]);
1152 		aflag++;
1153 	}
1154 	if (n != COMPLETE) {
1155 		fprintf(stdout, "Login failed.\n");
1156 		return (0);
1157 	}
1158 	if (!aflag && argc == 4) {
1159 		(void) command("ACCT %s", argv[3]);
1160 	}
1161 	return (1);
1162 }
1163 
1164 /*
1165  * Print working directory.
1166  */
1167 /*VARARGS*/
1168 pwd()
1169 {
1170 
1171 	(void) command("PWD");
1172 }
1173 
1174 /*
1175  * Make a directory.
1176  */
1177 makedir(argc, argv)
1178 	char *argv[];
1179 {
1180 
1181 	if (argc < 2) {
1182 		(void) strcat(line, " ");
1183 		printf("(directory-name) ");
1184 		(void) gets(&line[strlen(line)]);
1185 		makeargv();
1186 		argc = margc;
1187 		argv = margv;
1188 	}
1189 	if (argc < 2) {
1190 		printf("usage: %s directory-name\n", argv[0]);
1191 		code = -1;
1192 		return;
1193 	}
1194 	(void) command("MKD %s", argv[1]);
1195 }
1196 
1197 /*
1198  * Remove a directory.
1199  */
1200 removedir(argc, argv)
1201 	char *argv[];
1202 {
1203 
1204 	if (argc < 2) {
1205 		(void) strcat(line, " ");
1206 		printf("(directory-name) ");
1207 		(void) gets(&line[strlen(line)]);
1208 		makeargv();
1209 		argc = margc;
1210 		argv = margv;
1211 	}
1212 	if (argc < 2) {
1213 		printf("usage: %s directory-name\n", argv[0]);
1214 		code = -1;
1215 		return;
1216 	}
1217 	(void) command("RMD %s", argv[1]);
1218 }
1219 
1220 /*
1221  * Send a line, verbatim, to the remote machine.
1222  */
1223 quote(argc, argv)
1224 	char *argv[];
1225 {
1226 	int i;
1227 	char buf[BUFSIZ];
1228 
1229 	if (argc < 2) {
1230 		(void) strcat(line, " ");
1231 		printf("(command line to send) ");
1232 		(void) gets(&line[strlen(line)]);
1233 		makeargv();
1234 		argc = margc;
1235 		argv = margv;
1236 	}
1237 	if (argc < 2) {
1238 		printf("usage: %s line-to-send\n", argv[0]);
1239 		code = -1;
1240 		return;
1241 	}
1242 	(void) strcpy(buf, argv[1]);
1243 	for (i = 2; i < argc; i++) {
1244 		(void) strcat(buf, " ");
1245 		(void) strcat(buf, argv[i]);
1246 	}
1247 	if (command(buf) == PRELIM) {
1248 		while (getreply(0) == PRELIM);
1249 	}
1250 }
1251 
1252 /*
1253  * Ask the other side for help.
1254  */
1255 rmthelp(argc, argv)
1256 	char *argv[];
1257 {
1258 	int oldverbose = verbose;
1259 
1260 	verbose = 1;
1261 	(void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1262 	verbose = oldverbose;
1263 }
1264 
1265 /*
1266  * Terminate session and exit.
1267  */
1268 /*VARARGS*/
1269 quit()
1270 {
1271 
1272 	if (connected)
1273 		disconnect();
1274 	pswitch(1);
1275 	if (connected) {
1276 		disconnect();
1277 	}
1278 	exit(0);
1279 }
1280 
1281 /*
1282  * Terminate session, but don't exit.
1283  */
1284 disconnect()
1285 {
1286 	extern FILE *cout;
1287 	extern int data;
1288 
1289 	if (!connected)
1290 		return;
1291 	(void) command("QUIT");
1292 	if (cout) {
1293 		(void) fclose(cout);
1294 	}
1295 	cout = NULL;
1296 	connected = 0;
1297 	data = -1;
1298 	if (!proxy) {
1299 		macnum = 0;
1300 	}
1301 }
1302 
1303 confirm(cmd, file)
1304 	char *cmd, *file;
1305 {
1306 	char line[BUFSIZ];
1307 
1308 	if (!interactive)
1309 		return (1);
1310 	printf("%s %s? ", cmd, file);
1311 	(void) fflush(stdout);
1312 	(void) gets(line);
1313 	return (*line != 'n' && *line != 'N');
1314 }
1315 
1316 fatal(msg)
1317 	char *msg;
1318 {
1319 
1320 	fprintf(stderr, "ftp: %s\n", msg);
1321 	exit(1);
1322 }
1323 
1324 /*
1325  * Glob a local file name specification with
1326  * the expectation of a single return value.
1327  * Can't control multiple values being expanded
1328  * from the expression, we return only the first.
1329  */
1330 globulize(cpp)
1331 	char **cpp;
1332 {
1333 	char **globbed;
1334 
1335 	if (!doglob)
1336 		return (1);
1337 	globbed = glob(*cpp);
1338 	if (globerr != NULL) {
1339 		printf("%s: %s\n", *cpp, globerr);
1340 		if (globbed)
1341 			blkfree(globbed);
1342 		return (0);
1343 	}
1344 	if (globbed) {
1345 		*cpp = *globbed++;
1346 		/* don't waste too much memory */
1347 		if (*globbed)
1348 			blkfree(globbed);
1349 	}
1350 	return (1);
1351 }
1352 
1353 account(argc,argv)
1354 
1355 	int argc;
1356 	char **argv;
1357 {
1358 	char acct[50], *mygetpass(), *ap;
1359 
1360 	if (argc > 1) {
1361 		++argv;
1362 		--argc;
1363 		(void) strncpy(acct,*argv,49);
1364 		acct[50] = '\0';
1365 		while (argc > 1) {
1366 			--argc;
1367 			++argv;
1368 			(void) strncat(acct,*argv, 49-strlen(acct));
1369 		}
1370 		ap = acct;
1371 	}
1372 	else {
1373 		ap = mygetpass("Account:");
1374 	}
1375 	(void) command("ACCT %s", ap);
1376 }
1377 
1378 jmp_buf abortprox;
1379 
1380 proxabort()
1381 {
1382 	extern int proxy;
1383 
1384 	if (!proxy) {
1385 		pswitch(1);
1386 	}
1387 	if (connected) {
1388 		proxflag = 1;
1389 	}
1390 	else {
1391 		proxflag = 0;
1392 	}
1393 	pswitch(0);
1394 	longjmp(abortprox,1);
1395 }
1396 
1397 doproxy(argc,argv)
1398 	int argc;
1399 	char *argv[];
1400 {
1401 	int (*oldintr)(), proxabort();
1402 	register struct cmd *c;
1403 	struct cmd *getcmd();
1404 	extern struct cmd cmdtab[];
1405 	extern jmp_buf abortprox;
1406 
1407 	if (argc < 2) {
1408 		(void) strcat(line, " ");
1409 		printf("(command) ");
1410 		(void) gets(&line[strlen(line)]);
1411 		makeargv();
1412 		argc = margc;
1413 		argv = margv;
1414 	}
1415 	if (argc < 2) {
1416 		printf("usage:%s command\n", argv[0]);
1417 		code = -1;
1418 		return;
1419 	}
1420 	c = getcmd(argv[1]);
1421 	if (c == (struct cmd *) -1) {
1422 		printf("?Ambiguous command\n");
1423 		(void) fflush(stdout);
1424 		code = -1;
1425 		return;
1426 	}
1427 	if (c == 0) {
1428 		printf("?Invalid command\n");
1429 		(void) fflush(stdout);
1430 		code = -1;
1431 		return;
1432 	}
1433 	if (!c->c_proxy) {
1434 		printf("?Invalid proxy command\n");
1435 		(void) fflush(stdout);
1436 		code = -1;
1437 		return;
1438 	}
1439 	if (setjmp(abortprox)) {
1440 		code = -1;
1441 		return;
1442 	}
1443 	oldintr = signal(SIGINT, proxabort);
1444 	pswitch(1);
1445 	if (c->c_conn && !connected) {
1446 		printf("Not connected\n");
1447 		(void) fflush(stdout);
1448 		pswitch(0);
1449 		(void) signal(SIGINT, oldintr);
1450 		code = -1;
1451 		return;
1452 	}
1453 	(*c->c_handler)(argc-1, argv+1);
1454 	if (connected) {
1455 		proxflag = 1;
1456 	}
1457 	else {
1458 		proxflag = 0;
1459 	}
1460 	pswitch(0);
1461 	(void) signal(SIGINT, oldintr);
1462 }
1463 
1464 setcase()
1465 {
1466 	mcase = !mcase;
1467 	printf("Case mapping %s.\n", onoff(mcase));
1468 	code = mcase;
1469 }
1470 
1471 setcr()
1472 {
1473 	crflag = !crflag;
1474 	printf("Carriage Return stripping %s.\n", onoff(crflag));
1475 	code = crflag;
1476 }
1477 
1478 setntrans(argc,argv)
1479 	int argc;
1480 	char *argv[];
1481 {
1482 	if (argc == 1) {
1483 		ntflag = 0;
1484 		printf("Ntrans off.\n");
1485 		code = ntflag;
1486 		return;
1487 	}
1488 	ntflag++;
1489 	code = ntflag;
1490 	(void) strncpy(ntin, argv[1], 16);
1491 	ntin[16] = '\0';
1492 	if (argc == 2) {
1493 		ntout[0] = '\0';
1494 		return;
1495 	}
1496 	(void) strncpy(ntout, argv[2], 16);
1497 	ntout[16] = '\0';
1498 }
1499 
1500 char *
1501 dotrans(name)
1502 	char *name;
1503 {
1504 	static char new[MAXPATHLEN];
1505 	char *cp1, *cp2 = new;
1506 	register int i, ostop, found;
1507 
1508 	for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++);
1509 	for (cp1 = name; *cp1; cp1++) {
1510 		found = 0;
1511 		for (i = 0; *(ntin + i) && i < 16; i++) {
1512 			if (*cp1 == *(ntin + i)) {
1513 				found++;
1514 				if (i < ostop) {
1515 					*cp2++ = *(ntout + i);
1516 				}
1517 				break;
1518 			}
1519 		}
1520 		if (!found) {
1521 			*cp2++ = *cp1;
1522 		}
1523 	}
1524 	*cp2 = '\0';
1525 	return(new);
1526 }
1527 
1528 setnmap(argc, argv)
1529 	int argc;
1530 	char *argv[];
1531 {
1532 	char *cp;
1533 
1534 	if (argc == 1) {
1535 		mapflag = 0;
1536 		printf("Nmap off.\n");
1537 		code = mapflag;
1538 		return;
1539 	}
1540 	if (argc < 3) {
1541 		(void) strcat(line, " ");
1542 		printf("(mapout) ");
1543 		(void) gets(&line[strlen(line)]);
1544 		makeargv();
1545 		argc = margc;
1546 		argv = margv;
1547 	}
1548 	if (argc < 3) {
1549 		printf("Usage: %s [mapin mapout]\n",argv[0]);
1550 		code = -1;
1551 		return;
1552 	}
1553 	mapflag = 1;
1554 	code = 1;
1555 	cp = index(altarg, ' ');
1556 	if (proxy) {
1557 		while(*++cp == ' ');
1558 		altarg = cp;
1559 		cp = index(altarg, ' ');
1560 	}
1561 	*cp = '\0';
1562 	(void) strncpy(mapin, altarg, MAXPATHLEN - 1);
1563 	while (*++cp == ' ');
1564 	(void) strncpy(mapout, cp, MAXPATHLEN - 1);
1565 }
1566 
1567 char *
1568 domap(name)
1569 	char *name;
1570 {
1571 	static char new[MAXPATHLEN];
1572 	register char *cp1 = name, *cp2 = mapin;
1573 	char *tp[9], *te[9];
1574 	int i, toks[9], toknum, match = 1;
1575 
1576 	for (i=0; i < 9; ++i) {
1577 		toks[i] = 0;
1578 	}
1579 	while (match && *cp1 && *cp2) {
1580 		switch (*cp2) {
1581 			case '\\':
1582 				if (*++cp2 != *cp1) {
1583 					match = 0;
1584 				}
1585 				break;
1586 			case '$':
1587 				if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1588 					if (*cp1 != *(++cp2+1)) {
1589 						toks[toknum = *cp2 - '1']++;
1590 						tp[toknum] = cp1;
1591 						while (*++cp1 && *(cp2+1)
1592 							!= *cp1);
1593 						te[toknum] = cp1;
1594 					}
1595 					cp2++;
1596 					break;
1597 				}
1598 				/* intentional drop through */
1599 			default:
1600 				if (*cp2 != *cp1) {
1601 					match = 0;
1602 				}
1603 				break;
1604 		}
1605 		if (*cp1) {
1606 			cp1++;
1607 		}
1608 		if (*cp2) {
1609 			cp2++;
1610 		}
1611 	}
1612 	cp1 = new;
1613 	*cp1 = '\0';
1614 	cp2 = mapout;
1615 	while (*cp2) {
1616 		match = 0;
1617 		switch (*cp2) {
1618 			case '\\':
1619 				if (*(cp2 + 1)) {
1620 					*cp1++ = *++cp2;
1621 				}
1622 				break;
1623 			case '[':
1624 LOOP:
1625 				if (*++cp2 == '$' && isdigit(*(cp2+1))) {
1626 					if (*++cp2 == '0') {
1627 						char *cp3 = name;
1628 
1629 						while (*cp3) {
1630 							*cp1++ = *cp3++;
1631 						}
1632 						match = 1;
1633 					}
1634 					else if (toks[toknum = *cp2 - '1']) {
1635 						char *cp3 = tp[toknum];
1636 
1637 						while (cp3 != te[toknum]) {
1638 							*cp1++ = *cp3++;
1639 						}
1640 						match = 1;
1641 					}
1642 				}
1643 				else {
1644 					while (*cp2 && *cp2 != ',' &&
1645 					    *cp2 != ']') {
1646 						if (*cp2 == '\\') {
1647 							cp2++;
1648 						}
1649 						else if (*cp2 == '$' &&
1650    						        isdigit(*(cp2+1))) {
1651 							if (*++cp2 == '0') {
1652 							   char *cp3 = name;
1653 
1654 							   while (*cp3) {
1655 								*cp1++ = *cp3++;
1656 							   }
1657 							}
1658 							else if (toks[toknum =
1659 							    *cp2 - '1']) {
1660 							   char *cp3=tp[toknum];
1661 
1662 							   while (cp3 !=
1663 								  te[toknum]) {
1664 								*cp1++ = *cp3++;
1665 							   }
1666 							}
1667 						}
1668 						else if (*cp2) {
1669 							*cp1++ = *cp2++;
1670 						}
1671 					}
1672 					if (!*cp2) {
1673 						printf("nmap: unbalanced brackets\n");
1674 						return(name);
1675 					}
1676 					match = 1;
1677 					cp2--;
1678 				}
1679 				if (match) {
1680 					while (*++cp2 && *cp2 != ']') {
1681 					      if (*cp2 == '\\' && *(cp2 + 1)) {
1682 							cp2++;
1683 					      }
1684 					}
1685 					if (!*cp2) {
1686 						printf("nmap: unbalanced brackets\n");
1687 						return(name);
1688 					}
1689 					break;
1690 				}
1691 				switch (*++cp2) {
1692 					case ',':
1693 						goto LOOP;
1694 					case ']':
1695 						break;
1696 					default:
1697 						cp2--;
1698 						goto LOOP;
1699 				}
1700 				break;
1701 			case '$':
1702 				if (isdigit(*(cp2 + 1))) {
1703 					if (*++cp2 == '0') {
1704 						char *cp3 = name;
1705 
1706 						while (*cp3) {
1707 							*cp1++ = *cp3++;
1708 						}
1709 					}
1710 					else if (toks[toknum = *cp2 - '1']) {
1711 						char *cp3 = tp[toknum];
1712 
1713 						while (cp3 != te[toknum]) {
1714 							*cp1++ = *cp3++;
1715 						}
1716 					}
1717 					break;
1718 				}
1719 				/* intentional drop through */
1720 			default:
1721 				*cp1++ = *cp2;
1722 				break;
1723 		}
1724 		cp2++;
1725 	}
1726 	*cp1 = '\0';
1727 	if (!*new) {
1728 		return(name);
1729 	}
1730 	return(new);
1731 }
1732 
1733 setsunique()
1734 {
1735 	sunique = !sunique;
1736 	printf("Store unique %s.\n", onoff(sunique));
1737 	code = sunique;
1738 }
1739 
1740 setrunique()
1741 {
1742 	runique = !runique;
1743 	printf("Receive unique %s.\n", onoff(runique));
1744 	code = runique;
1745 }
1746 
1747 /* change directory to perent directory */
1748 cdup()
1749 {
1750 	(void) command("CDUP");
1751 }
1752 
1753 macdef(argc, argv)
1754 	int argc;
1755 	char *argv[];
1756 {
1757 	char *tmp;
1758 	int c;
1759 
1760 	if (macnum == 16) {
1761 		printf("Limit of 16 macros have already been defined\n");
1762 		code = -1;
1763 		return;
1764 	}
1765 	if (argc < 2) {
1766 		(void) strcat(line, " ");
1767 		printf("(macro name) ");
1768 		(void) gets(&line[strlen(line)]);
1769 		makeargv();
1770 		argc = margc;
1771 		argv = margv;
1772 	}
1773 	if (argc != 2) {
1774 		printf("Usage: %s macro_name\n",argv[0]);
1775 		code = -1;
1776 		return;
1777 	}
1778 	if (interactive) {
1779 		printf("Enter macro line by line, terminating it with a null line\n");
1780 	}
1781 	(void) strncpy(macros[macnum].mac_name, argv[1], 8);
1782 	if (macnum == 0) {
1783 		macros[macnum].mac_start = macbuf;
1784 	}
1785 	else {
1786 		macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
1787 	}
1788 	tmp = macros[macnum].mac_start;
1789 	while (tmp != macbuf+4096) {
1790 		if ((c = getchar()) == EOF) {
1791 			printf("macdef:end of file encountered\n");
1792 			code = -1;
1793 			return;
1794 		}
1795 		if ((*tmp = c) == '\n') {
1796 			if (tmp == macros[macnum].mac_start) {
1797 				macros[macnum++].mac_end = tmp;
1798 				code = 0;
1799 				return;
1800 			}
1801 			if (*(tmp-1) == '\0') {
1802 				macros[macnum++].mac_end = tmp - 1;
1803 				code = 0;
1804 				return;
1805 			}
1806 			*tmp = '\0';
1807 		}
1808 		tmp++;
1809 	}
1810 	while (1) {
1811 		while ((c = getchar()) != '\n' && c != EOF);
1812 		if (c == EOF || getchar() == '\n') {
1813 			printf("Macro not defined - 4k buffer exceeded\n");
1814 			code = -1;
1815 			return;
1816 		}
1817 	}
1818 }
1819