xref: /original-bsd/usr.bin/ftp/cmds.c (revision 3708840b)
1 #ifndef lint
2 static char sccsid[] = "@(#)cmds.c	4.6 (Berkeley) 05/11/83";
3 #endif
4 
5 /*
6  * FTP User Program -- Command Routines.
7  */
8 #include <sys/param.h>
9 #include <sys/socket.h>
10 
11 #include <arpa/ftp.h>
12 
13 #include <signal.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include <netdb.h>
17 #include <stat.h>
18 
19 #include "ftp_var.h"
20 
21 extern	char *globerr;
22 extern	char **glob();
23 extern	char *home;
24 extern	short gflag;
25 extern	char *remglob();
26 extern	char *getenv();
27 extern	char *index();
28 extern	char *rindex();
29 
30 /*
31  * Connect to peer server and
32  * auto-login, if possible.
33  */
34 setpeer(argc, argv)
35 	int argc;
36 	char *argv[];
37 {
38 	struct hostent *host, *hookup();
39 	int port;
40 
41 	if (connected) {
42 		printf("Already connected to %s, use disconnect first.\n",
43 			hostname);
44 		return;
45 	}
46 	if (argc < 2) {
47 		strcat(line, " ");
48 		printf("(to) ");
49 		gets(&line[strlen(line)]);
50 		makeargv();
51 		argc = margc;
52 		argv = margv;
53 	}
54 	if (argc > 3) {
55 		printf("usage: %s host-name [port]\n", argv[0]);
56 		return;
57 	}
58 	port = sp->s_port;
59 	if (argc > 2) {
60 		port = atoi(argv[2]);
61 		if (port <= 0) {
62 			printf("%s: bad port number-- %s\n", argv[1], argv[2]);
63 			printf ("usage: %s host-name [port]\n", argv[0]);
64 			return;
65 		}
66 		port = htons(port);
67 	}
68 	host = hookup(argv[1], port);
69 	if (host) {
70 		connected = 1;
71 		if (autologin)
72 			login(host);
73 	}
74 }
75 
76 struct	types {
77 	char	*t_name;
78 	char	*t_mode;
79 	int	t_type;
80 	char	*t_arg;
81 } types[] = {
82 	{ "ascii",	"A",	TYPE_A,	0 },
83 	{ "binary",	"I",	TYPE_I,	0 },
84 	{ "image",	"I",	TYPE_I,	0 },
85 	{ "ebcdic",	"E",	TYPE_E,	0 },
86 	{ "tenex",	"L",	TYPE_L,	bytename },
87 	0
88 };
89 
90 /*
91  * Set transfer type.
92  */
93 settype(argc, argv)
94 	char *argv[];
95 {
96 	register struct types *p;
97 	int comret;
98 
99 	if (argc > 2) {
100 		char *sep;
101 
102 		printf("usage: %s [", argv[0]);
103 		sep = " ";
104 		for (p = types; p->t_name; p++) {
105 			printf("%s%s", sep, p->t_name);
106 			if (*sep == ' ')
107 				sep = " | ";
108 		}
109 		printf(" ]\n");
110 		return;
111 	}
112 	if (argc < 2) {
113 		printf("Using %s mode to transfer files.\n", typename);
114 		return;
115 	}
116 	for (p = types; p->t_name; p++)
117 		if (strcmp(argv[1], p->t_name) == 0)
118 			break;
119 	if (p->t_name == 0) {
120 		printf("%s: unknown mode\n", argv[1]);
121 		return;
122 	}
123 	if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
124 		comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
125 	else
126 		comret = command("TYPE %s", p->t_mode);
127 	if (comret == COMPLETE) {
128 		strcpy(typename, p->t_name);
129 		type = p->t_type;
130 	}
131 }
132 
133 /*
134  * Set binary transfer type.
135  */
136 /*VARARGS*/
137 setbinary()
138 {
139 
140 	call(settype, "type", "binary", 0);
141 }
142 
143 /*
144  * Set ascii transfer type.
145  */
146 /*VARARGS*/
147 setascii()
148 {
149 
150 	call(settype, "type", "ascii", 0);
151 }
152 
153 /*
154  * Set tenex transfer type.
155  */
156 /*VARARGS*/
157 settenex()
158 {
159 
160 	call(settype, "type", "tenex", 0);
161 }
162 
163 /*
164  * Set ebcdic transfer type.
165  */
166 /*VARARGS*/
167 setebcdic()
168 {
169 
170 	call(settype, "type", "ebcdic", 0);
171 }
172 
173 /*
174  * Set file transfer mode.
175  */
176 setmode(argc, argv)
177 	char *argv[];
178 {
179 
180 	printf("We only support %s mode, sorry.\n", modename);
181 }
182 
183 /*
184  * Set file transfer format.
185  */
186 setform(argc, argv)
187 	char *argv[];
188 {
189 
190 	printf("We only support %s format, sorry.\n", formname);
191 }
192 
193 /*
194  * Set file transfer structure.
195  */
196 setstruct(argc, argv)
197 	char *argv[];
198 {
199 
200 	printf("We only support %s structure, sorry.\n", structname);
201 }
202 
203 put(argc, argv)
204 	int argc;
205 	char *argv[];
206 {
207 	char *cmd;
208 
209 	if (argc == 2)
210 		argc++, argv[2] = argv[1];
211 	if (argc < 2) {
212 		strcat(line, " ");
213 		printf("(local-file) ");
214 		gets(&line[strlen(line)]);
215 		makeargv();
216 		argc = margc;
217 		argv = margv;
218 	}
219 	if (argc < 2) {
220 usage:
221 		printf("%s local-file remote-file\n", argv[0]);
222 		return;
223 	}
224 	if (argc < 3) {
225 		strcat(line, " ");
226 		printf("(remote-file) ");
227 		gets(&line[strlen(line)]);
228 		makeargv();
229 		argc = margc;
230 		argv = margv;
231 	}
232 	if (argc < 3)
233 		goto usage;
234 	if (!globulize(&argv[1]))
235 		return;
236 	cmd = (argv[0][0] == 'a') ? "APPE" : "STOR";
237 	sendrequest(cmd, argv[1], argv[2]);
238 }
239 
240 /*
241  * Send multiple files.
242  */
243 mput(argc, argv)
244 	char *argv[];
245 {
246 	char **cpp, **gargs = NULL;
247 
248 	if (argc < 2) {
249 		strcat(line, " ");
250 		printf("(local-files) ");
251 		gets(&line[strlen(line)]);
252 		makeargv();
253 		argc = margc;
254 		argv = margv;
255 	}
256 	if (argc < 2) {
257 		printf("%s local-files\n", argv[0]);
258 		return;
259 	}
260 	cpp = argv + 1;
261 	if (doglob) {
262 		gargs = glob(cpp);
263 		if (globerr != NULL) {
264 			printf("%s\n", globerr);
265 			if (gargs)
266 				blkfree(gargs);
267 			return;
268 		}
269 	}
270 	if (gargs != NULL)
271 		cpp = gargs;
272 	for (; *cpp != NULL; cpp++)
273 		if (confirm(argv[0], *cpp))
274 			sendrequest("STOR", *cpp, *cpp);
275 	if (gargs != NULL)
276 		blkfree(gargs);
277 }
278 
279 /*
280  * Receive one file.
281  */
282 get(argc, argv)
283 	char *argv[];
284 {
285 
286 	if (argc == 2)
287 		argc++, argv[2] = argv[1];
288 	if (argc < 2) {
289 		strcat(line, " ");
290 		printf("(remote-file) ");
291 		gets(&line[strlen(line)]);
292 		makeargv();
293 		argc = margc;
294 		argv = margv;
295 	}
296 	if (argc < 2) {
297 usage:
298 		printf("%s remote-file [ local-file ]\n", argv[0]);
299 		return;
300 	}
301 	if (argc < 3) {
302 		strcat(line, " ");
303 		printf("(local-file) ");
304 		gets(&line[strlen(line)]);
305 		makeargv();
306 		argc = margc;
307 		argv = margv;
308 	}
309 	if (argc < 3)
310 		goto usage;
311 	if (!globulize(&argv[2]))
312 		return;
313 	recvrequest("RETR", argv[2], argv[1], "w");
314 }
315 
316 /*
317  * Get multiple files.
318  */
319 mget(argc, argv)
320 	char *argv[];
321 {
322 	char *cp;
323 
324 	if (argc < 2) {
325 		strcat(line, " ");
326 		printf("(remote-files) ");
327 		gets(&line[strlen(line)]);
328 		makeargv();
329 		argc = margc;
330 		argv = margv;
331 	}
332 	if (argc < 2) {
333 		printf("%s remote-files\n", argv[0]);
334 		return;
335 	}
336 	while ((cp = remglob(argc, argv)) != NULL)
337 		if (confirm(argv[0], cp))
338 			recvrequest("RETR", cp, cp, "w");
339 }
340 
341 char *
342 remglob(argc, argv)
343 	char *argv[];
344 {
345 	char temp[16];
346 	static char buf[MAXPATHLEN];
347 	static FILE *ftemp = NULL;
348 	static char **args;
349 	int oldverbose;
350 	char *cp, *mode;
351 
352 	if (!doglob) {
353 		if (args == NULL)
354 			args = argv;
355 		if ((cp = *++args) == NULL)
356 			args = NULL;
357 		return (cp);
358 	}
359 	if (ftemp == NULL) {
360 		strcpy(temp, "/tmp/ftpXXXXXX");
361 		mktemp(temp);
362 		oldverbose = verbose, verbose = 0;
363 		for (mode = "w"; *++argv != NULL; mode = "a")
364 			recvrequest ("NLST", temp, *argv, mode);
365 		verbose = oldverbose;
366 		ftemp = fopen(temp, "r");
367 		unlink(temp);
368 		if (ftemp == NULL) {
369 			printf("can't find list of remote files, oops\n");
370 			return (NULL);
371 		}
372 	}
373 	if (fgets(buf, sizeof (buf), ftemp) == NULL) {
374 		fclose(ftemp), ftemp = NULL;
375 		return (NULL);
376 	}
377 	if ((cp = index(buf, '\n')) != NULL)
378 		*cp = '\0';
379 	return (buf);
380 }
381 
382 char *
383 onoff(bool)
384 	int bool;
385 {
386 
387 	return (bool ? "on" : "off");
388 }
389 
390 /*
391  * Show status.
392  */
393 status(argc, argv)
394 	char *argv[];
395 {
396 
397 	if (connected)
398 		printf("Connected to %s.\n", hostname);
399 	else
400 		printf("Not connected.\n");
401 	printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
402 		modename, typename, formname, structname);
403 	printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
404 		onoff(verbose), onoff(bell), onoff(interactive),
405 		onoff(doglob));
406 	printf("Hash mark printing: %s; Use of PORT cmds: %s\n",
407 		onoff(hash), onoff(sendport));
408 }
409 
410 /*
411  * Set beep on cmd completed mode.
412  */
413 /*VARARGS*/
414 setbell()
415 {
416 
417 	bell = !bell;
418 	printf("Bell mode %s.\n", onoff(bell));
419 }
420 
421 /*
422  * Turn on packet tracing.
423  */
424 /*VARARGS*/
425 settrace()
426 {
427 
428 	trace = !trace;
429 	printf("Packet tracing %s.\n", onoff(trace));
430 }
431 
432 /*
433  * Toggle hash mark printing during transfers.
434  */
435 /*VARARGS*/
436 sethash()
437 {
438 
439 	hash = !hash;
440 	printf("Hash mark printing %s", onoff(hash));
441 	if (hash)
442 		printf(" (%d bytes/hash mark)", BUFSIZ);
443 	printf(".\n");
444 }
445 
446 /*
447  * Turn on printing of server echo's.
448  */
449 /*VARARGS*/
450 setverbose()
451 {
452 
453 	verbose = !verbose;
454 	printf("Verbose mode %s.\n", onoff(verbose));
455 }
456 
457 /*
458  * Toggle PORT cmd use before each data connection.
459  */
460 /*VARARGS*/
461 setport()
462 {
463 
464 	sendport = !sendport;
465 	printf("Use of PORT cmds %s.\n", onoff(sendport));
466 }
467 
468 /*
469  * Turn on interactive prompting
470  * during mget, mput, and mdelete.
471  */
472 /*VARARGS*/
473 setprompt()
474 {
475 
476 	interactive = !interactive;
477 	printf("Interactive mode %s.\n", onoff(interactive));
478 }
479 
480 /*
481  * Toggle metacharacter interpretation
482  * on local file names.
483  */
484 /*VARARGS*/
485 setglob()
486 {
487 
488 	doglob = !doglob;
489 	printf("Globbing %s.\n", onoff(doglob));
490 }
491 
492 /*
493  * Set debugging mode on/off and/or
494  * set level of debugging.
495  */
496 /*VARARGS*/
497 setdebug(argc, argv)
498 	char *argv[];
499 {
500 	int val;
501 
502 	if (argc > 1) {
503 		val = atoi(argv[1]);
504 		if (val < 0) {
505 			printf("%s: bad debugging value.\n", argv[1]);
506 			return;
507 		}
508 	} else
509 		val = !debug;
510 	debug = val;
511 	if (debug)
512 		options |= SO_DEBUG;
513 	else
514 		options &= ~SO_DEBUG;
515 	printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
516 }
517 
518 /*
519  * Set current working directory
520  * on remote machine.
521  */
522 cd(argc, argv)
523 	char *argv[];
524 {
525 
526 	if (argc < 2) {
527 		strcat(line, " ");
528 		printf("(remote-directory) ");
529 		gets(&line[strlen(line)]);
530 		makeargv();
531 		argc = margc;
532 		argv = margv;
533 	}
534 	if (argc < 2) {
535 		printf("%s remote-directory\n", argv[0]);
536 		return;
537 	}
538 	(void) command("CWD %s", argv[1]);
539 }
540 
541 /*
542  * Set current working directory
543  * on local machine.
544  */
545 lcd(argc, argv)
546 	char *argv[];
547 {
548 	char buf[MAXPATHLEN];
549 
550 	if (argc < 2)
551 		argc++, argv[1] = home;
552 	if (argc != 2) {
553 		printf("%s local-directory\n", argv[0]);
554 		return;
555 	}
556 	if (!globulize(&argv[1]))
557 		return;
558 	if (chdir(argv[1]) < 0) {
559 		perror(argv[1]);
560 		return;
561 	}
562 	printf("Local directory now %s\n", getwd(buf));
563 }
564 
565 /*
566  * Delete a single file.
567  */
568 delete(argc, argv)
569 	char *argv[];
570 {
571 
572 	if (argc < 2) {
573 		strcat(line, " ");
574 		printf("(remote-file) ");
575 		gets(&line[strlen(line)]);
576 		makeargv();
577 		argc = margc;
578 		argv = margv;
579 	}
580 	if (argc < 2) {
581 		printf("%s remote-file\n", argv[0]);
582 		return;
583 	}
584 	(void) command("DELE %s", argv[1]);
585 }
586 
587 /*
588  * Delete multiple files.
589  */
590 mdelete(argc, argv)
591 	char *argv[];
592 {
593 	char *cp;
594 
595 	if (argc < 2) {
596 		strcat(line, " ");
597 		printf("(remote-files) ");
598 		gets(&line[strlen(line)]);
599 		makeargv();
600 		argc = margc;
601 		argv = margv;
602 	}
603 	if (argc < 2) {
604 		printf("%s remote-files\n", argv[0]);
605 		return;
606 	}
607 	while ((cp = remglob(argc, argv)) != NULL)
608 		if (confirm(argv[0], cp))
609 			(void) command("DELE %s", cp);
610 }
611 
612 /*
613  * Rename a remote file.
614  */
615 renamefile(argc, argv)
616 	char *argv[];
617 {
618 
619 	if (argc < 2) {
620 		strcat(line, " ");
621 		printf("(from-name) ");
622 		gets(&line[strlen(line)]);
623 		makeargv();
624 		argc = margc;
625 		argv = margv;
626 	}
627 	if (argc < 2) {
628 usage:
629 		printf("%s from-name to-name\n", argv[0]);
630 		return;
631 	}
632 	if (argc < 3) {
633 		strcat(line, " ");
634 		printf("(to-name) ");
635 		gets(&line[strlen(line)]);
636 		makeargv();
637 		argc = margc;
638 		argv = margv;
639 	}
640 	if (argc < 3)
641 		goto usage;
642 	if (command("RNFR %s", argv[1]) == CONTINUE)
643 		(void) command("RNTO %s", argv[2]);
644 }
645 
646 /*
647  * Get a directory listing
648  * of remote files.
649  */
650 ls(argc, argv)
651 	char *argv[];
652 {
653 	char *cmd;
654 
655 	if (argc < 2)
656 		argc++, argv[1] = NULL;
657 	if (argc < 3)
658 		argc++, argv[2] = "-";
659 	if (argc > 3) {
660 		printf("usage: %s remote-directory local-file\n", argv[0]);
661 		return;
662 	}
663 	cmd = argv[0][0] == 'l' ? "NLST" : "LIST";
664 	if (strcmp(argv[2], "-") && !globulize(&argv[2]))
665 		return;
666 	recvrequest(cmd, argv[2], argv[1], "w");
667 }
668 
669 /*
670  * Get a directory listing
671  * of multiple remote files.
672  */
673 mls(argc, argv)
674 	char *argv[];
675 {
676 	char *cmd, *mode;
677 	int i, dest;
678 
679 	if (argc < 2)
680 		argc++, argv[1] = NULL;
681 	if (argc < 3)
682 		argc++, argv[2] = "-";
683 	dest = argc - 1;
684 	cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
685 	if (strcmp(argv[dest], "-") != 0)
686 		if (globulize(&argv[dest]) && confirm("local-file", argv[dest]))
687 			return;
688 	for (i = 1, mode = "w"; i < dest; i++, mode = "a")
689 		recvrequest(cmd, argv[dest], argv[i], mode);
690 }
691 
692 /*
693  * Do a shell escape
694  */
695 shell(argc, argv)
696 	char *argv[];
697 {
698 	int pid, status, (*old1)(), (*old2)();
699 	char shellnam[40], *shell, *namep;
700 	char **cpp, **gargs;
701 
702 	old1 = signal (SIGINT, SIG_IGN);
703 	old2 = signal (SIGQUIT, SIG_IGN);
704 	if ((pid = fork()) == 0) {
705 		for (pid = 3; pid < 20; pid++)
706 			close(pid);
707 		signal(SIGINT, SIG_DFL);
708 		signal(SIGQUIT, SIG_DFL);
709 		if (argc <= 1) {
710 			shell = getenv("SHELL");
711 			if (shell == NULL)
712 				shell = "/bin/sh";
713 			namep = rindex(shell,'/');
714 			if (namep == NULL)
715 				namep = shell;
716 			strcpy(shellnam,"-");
717 			strcat(shellnam, ++namep);
718 			if (strcmp(namep, "sh") != 0)
719 				shellnam[0] = '+';
720 			if (debug) {
721 				printf ("%s\n", shell);
722 				fflush (stdout);
723 			}
724 			execl(shell, shellnam, 0);
725 			perror(shell);
726 			exit(1);
727 		}
728 		cpp = &argv[1];
729 		if (argc > 2) {
730 			if ((gargs = glob(cpp)) != NULL)
731 				cpp = gargs;
732 			if (globerr != NULL) {
733 				printf("%s\n", globerr);
734 				exit(1);
735 			}
736 		}
737 		if (debug) {
738 			register char **zip = cpp;
739 
740 			printf("%s", *zip);
741 			while (*++zip != NULL)
742 				printf(" %s", *zip);
743 			printf("\n");
744 			fflush(stdout);
745 		}
746 		execvp(argv[1], cpp);
747 		perror(argv[1]);
748 		exit(1);
749 	}
750 	if (pid > 0)
751 		while (wait(&status) != pid)
752 			;
753 	signal(SIGINT, old1);
754 	signal(SIGQUIT, old2);
755 	if (pid == -1)
756 		perror("Try again later");
757 	return (0);
758 }
759 
760 /*
761  * Send new user information (re-login)
762  */
763 user(argc, argv)
764 	int argc;
765 	char **argv;
766 {
767 	char acct[80], *getpass();
768 	int n;
769 
770 	if (argc < 2) {
771 		strcat(line, " ");
772 		printf("(username) ");
773 		gets(&line[strlen(line)]);
774 		makeargv();
775 		argc = margc;
776 		argv = margv;
777 	}
778 	if (argc > 4) {
779 		printf("usage: %s username [password] [account]\n", argv[0]);
780 		return (0);
781 	}
782 	n = command("USER %s", argv[1]);
783 	if (n == CONTINUE) {
784 		if (argc < 3 )
785 			argv[2] = getpass("Password: "), argc++;
786 		n = command("PASS %s", argv[2]);
787 	}
788 	if (n == CONTINUE) {
789 		if (argc < 4) {
790 			printf("Account: "); (void) fflush(stdout);
791 			(void) fgets(acct, sizeof(acct) - 1, stdin);
792 			acct[strlen(acct) - 1] = '\0';
793 			argv[3] = acct; argc++;
794 		}
795 		n = command("ACCT %s", acct);
796 	}
797 	if (n != COMPLETE) {
798 		fprintf(stderr, "Login failed.\n");
799 		return (0);
800 	}
801 	return (1);
802 }
803 
804 /*
805  * Print working directory.
806  */
807 /*VARARGS*/
808 pwd()
809 {
810 
811 	(void) command("XPWD");
812 }
813 
814 /*
815  * Make a directory.
816  */
817 makedir(argc, argv)
818 	char *argv[];
819 {
820 
821 	if (argc < 2) {
822 		strcat(line, " ");
823 		printf("(directory-name) ");
824 		gets(&line[strlen(line)]);
825 		makeargv();
826 		argc = margc;
827 		argv = margv;
828 	}
829 	if (argc < 2) {
830 		printf("%s directory-name\n", argv[0]);
831 		return;
832 	}
833 	(void) command("XMKD %s", argv[1]);
834 }
835 
836 /*
837  * Remove a directory.
838  */
839 removedir(argc, argv)
840 	char *argv[];
841 {
842 
843 	if (argc < 2) {
844 		strcat(line, " ");
845 		printf("(directory-name) ");
846 		gets(&line[strlen(line)]);
847 		makeargv();
848 		argc = margc;
849 		argv = margv;
850 	}
851 	if (argc < 2) {
852 		printf("%s directory-name\n", argv[0]);
853 		return;
854 	}
855 	(void) command("XRMD %s", argv[1]);
856 }
857 
858 /*
859  * Send a line, verbatim, to the remote machine.
860  */
861 quote(argc, argv)
862 	char *argv[];
863 {
864 	int i;
865 	char buf[BUFSIZ];
866 
867 	if (argc < 2) {
868 		strcat(line, " ");
869 		printf("(command line to send) ");
870 		gets(&line[strlen(line)]);
871 		makeargv();
872 		argc = margc;
873 		argv = margv;
874 	}
875 	if (argc < 2) {
876 		printf("usage: %s line-to-send\n", argv[0]);
877 		return;
878 	}
879 	strcpy(buf, argv[1]);
880 	for (i = 2; i < argc; i++) {
881 		strcat(buf, " ");
882 		strcat(buf, argv[i]);
883 	}
884 	(void) command(buf);
885 }
886 
887 /*
888  * Ask the other side for help.
889  */
890 rmthelp(argc, argv)
891 	char *argv[];
892 {
893 	int oldverbose = verbose;
894 
895 	verbose = 1;
896 	(void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
897 	verbose = oldverbose;
898 }
899 
900 /*
901  * Terminate session and exit.
902  */
903 /*VARARGS*/
904 quit()
905 {
906 
907 	disconnect();
908 	exit(0);
909 }
910 
911 /*
912  * Terminate session, but don't exit.
913  */
914 disconnect()
915 {
916 	extern FILE *cout;
917 	extern int data;
918 
919 	if (!connected)
920 		return;
921 	(void) command("QUIT");
922 	(void) fclose(cout);
923 	cout = NULL;
924 	connected = 0;
925 	data = -1;
926 }
927 
928 confirm(cmd, file)
929 	char *cmd, *file;
930 {
931 	char line[BUFSIZ];
932 
933 	if (!interactive)
934 		return (1);
935 	printf("%s %s? ", cmd, file);
936 	fflush(stdout);
937 	gets(line);
938 	return (*line != 'n' && *line != 'N');
939 }
940 
941 fatal(msg)
942 	char *msg;
943 {
944 
945 	fprintf(stderr, "ftp: %s\n");
946 	exit(1);
947 }
948 
949 /*
950  * Glob a local file name specification with
951  * the expectation of a single return value.
952  * Can't control multiple values being expanded
953  * from the expression, we return only the first.
954  */
955 globulize(cpp)
956 	char **cpp;
957 {
958 	char **globbed;
959 
960 	if (!doglob)
961 		return (1);
962 	globbed = glob(*cpp);
963 	if (globerr != NULL) {
964 		printf("%s: %s\n", *cpp, globerr);
965 		if (globbed)
966 			blkfree(globbed);
967 		return (0);
968 	}
969 	if (globbed) {
970 		*cpp = *globbed++;
971 		/* don't waste too much memory */
972 		if (*globbed)
973 			blkfree(globbed);
974 	}
975 	return (1);
976 }
977