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