xref: /csrg-svn/usr.bin/ftp/cmds.c (revision 13613)
1 #ifndef lint
2 static char sccsid[] = "@(#)cmds.c	4.8 (Berkeley) 07/02/83";
3 #endif
4 
5 /*
6  * FTP User Program -- Command Routines.
7  */
8 #include <sys/param.h>
9 #include <sys/stat.h>
10 #include <sys/socket.h>
11 
12 #include <arpa/ftp.h>
13 
14 #include <signal.h>
15 #include <stdio.h>
16 #include <errno.h>
17 #include <netdb.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 	register int i;
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 	for (i = 1; i < argc; i++) {
261 		register char **cpp, **gargs;
262 
263 		if (!doglob) {
264 			if (confirm(argv[0], argv[i]))
265 				sendrequest("STOR", argv[i], argv[i]);
266 			continue;
267 		}
268 		gargs = glob(argv[i]);
269 		if (globerr != NULL) {
270 			printf("%s\n", globerr);
271 			if (gargs)
272 				blkfree(gargs);
273 			continue;
274 		}
275 		for (cpp = gargs; cpp && *cpp != NULL; cpp++)
276 			if (confirm(argv[0], *cpp))
277 				sendrequest("STOR", *cpp, *cpp);
278 		if (gargs != NULL)
279 			blkfree(gargs);
280 	}
281 }
282 
283 /*
284  * Receive one file.
285  */
286 get(argc, argv)
287 	char *argv[];
288 {
289 
290 	if (argc == 2)
291 		argc++, argv[2] = argv[1];
292 	if (argc < 2) {
293 		strcat(line, " ");
294 		printf("(remote-file) ");
295 		gets(&line[strlen(line)]);
296 		makeargv();
297 		argc = margc;
298 		argv = margv;
299 	}
300 	if (argc < 2) {
301 usage:
302 		printf("%s remote-file [ local-file ]\n", argv[0]);
303 		return;
304 	}
305 	if (argc < 3) {
306 		strcat(line, " ");
307 		printf("(local-file) ");
308 		gets(&line[strlen(line)]);
309 		makeargv();
310 		argc = margc;
311 		argv = margv;
312 	}
313 	if (argc < 3)
314 		goto usage;
315 	if (!globulize(&argv[2]))
316 		return;
317 	recvrequest("RETR", argv[2], argv[1], "w");
318 }
319 
320 /*
321  * Get multiple files.
322  */
323 mget(argc, argv)
324 	char *argv[];
325 {
326 	char *cp;
327 
328 	if (argc < 2) {
329 		strcat(line, " ");
330 		printf("(remote-files) ");
331 		gets(&line[strlen(line)]);
332 		makeargv();
333 		argc = margc;
334 		argv = margv;
335 	}
336 	if (argc < 2) {
337 		printf("%s remote-files\n", argv[0]);
338 		return;
339 	}
340 	while ((cp = remglob(argc, argv)) != NULL)
341 		if (confirm(argv[0], cp))
342 			recvrequest("RETR", cp, cp, "w");
343 }
344 
345 char *
346 remglob(argc, argv)
347 	char *argv[];
348 {
349 	char temp[16];
350 	static char buf[MAXPATHLEN];
351 	static FILE *ftemp = NULL;
352 	static char **args;
353 	int oldverbose, oldhash;
354 	char *cp, *mode;
355 
356 	if (!doglob) {
357 		if (args == NULL)
358 			args = argv;
359 		if ((cp = *++args) == NULL)
360 			args = NULL;
361 		return (cp);
362 	}
363 	if (ftemp == NULL) {
364 		strcpy(temp, "/tmp/ftpXXXXXX");
365 		mktemp(temp);
366 		oldverbose = verbose, verbose = 0;
367 		oldhash = hash, hash = 0;
368 		for (mode = "w"; *++argv != NULL; mode = "a")
369 			recvrequest ("NLST", temp, *argv, mode);
370 		verbose = oldverbose; hash = oldhash;
371 		ftemp = fopen(temp, "r");
372 		unlink(temp);
373 		if (ftemp == NULL) {
374 			printf("can't find list of remote files, oops\n");
375 			return (NULL);
376 		}
377 	}
378 	if (fgets(buf, sizeof (buf), ftemp) == NULL) {
379 		fclose(ftemp), ftemp = NULL;
380 		return (NULL);
381 	}
382 	if ((cp = index(buf, '\n')) != NULL)
383 		*cp = '\0';
384 	return (buf);
385 }
386 
387 char *
388 onoff(bool)
389 	int bool;
390 {
391 
392 	return (bool ? "on" : "off");
393 }
394 
395 /*
396  * Show status.
397  */
398 status(argc, argv)
399 	char *argv[];
400 {
401 
402 	if (connected)
403 		printf("Connected to %s.\n", hostname);
404 	else
405 		printf("Not connected.\n");
406 	printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
407 		modename, typename, formname, structname);
408 	printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
409 		onoff(verbose), onoff(bell), onoff(interactive),
410 		onoff(doglob));
411 	printf("Hash mark printing: %s; Use of PORT cmds: %s; Linger: %s\n",
412 		onoff(hash), onoff(sendport), onoff(linger));
413 }
414 
415 /*
416  * Set beep on cmd completed mode.
417  */
418 /*VARARGS*/
419 setbell()
420 {
421 
422 	bell = !bell;
423 	printf("Bell mode %s.\n", onoff(bell));
424 }
425 
426 /*
427  * Turn on packet tracing.
428  */
429 /*VARARGS*/
430 settrace()
431 {
432 
433 	trace = !trace;
434 	printf("Packet tracing %s.\n", onoff(trace));
435 }
436 
437 /*
438  * Toggle hash mark printing during transfers.
439  */
440 /*VARARGS*/
441 sethash()
442 {
443 
444 	hash = !hash;
445 	printf("Hash mark printing %s", onoff(hash));
446 	if (hash)
447 		printf(" (%d bytes/hash mark)", BUFSIZ);
448 	printf(".\n");
449 }
450 
451 /*
452  * Turn on printing of server echo's.
453  */
454 /*VARARGS*/
455 setverbose()
456 {
457 
458 	verbose = !verbose;
459 	printf("Verbose mode %s.\n", onoff(verbose));
460 }
461 
462 /*
463  * Toggle PORT cmd use before each data connection.
464  */
465 /*VARARGS*/
466 setport()
467 {
468 
469 	sendport = !sendport;
470 	printf("Use of PORT cmds %s.\n", onoff(sendport));
471 }
472 
473 /*
474  * Turn on interactive prompting
475  * during mget, mput, and mdelete.
476  */
477 /*VARARGS*/
478 setprompt()
479 {
480 
481 	interactive = !interactive;
482 	printf("Interactive mode %s.\n", onoff(interactive));
483 }
484 
485 /*
486  * Toggle metacharacter interpretation
487  * on local file names.
488  */
489 /*VARARGS*/
490 setglob()
491 {
492 
493 	doglob = !doglob;
494 	printf("Globbing %s.\n", onoff(doglob));
495 }
496 
497 /*
498  * Set debugging mode on/off and/or
499  * set level of debugging.
500  */
501 /*VARARGS*/
502 setdebug(argc, argv)
503 	char *argv[];
504 {
505 	int val;
506 
507 	if (argc > 1) {
508 		val = atoi(argv[1]);
509 		if (val < 0) {
510 			printf("%s: bad debugging value.\n", argv[1]);
511 			return;
512 		}
513 	} else
514 		val = !debug;
515 	debug = val;
516 	if (debug)
517 		options |= SO_DEBUG;
518 	else
519 		options &= ~SO_DEBUG;
520 	printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
521 }
522 
523 /*
524  * Set linger on data connections on/off.
525  */
526 /*VARARGS*/
527 setlinger(argc, argv)
528 	char *argv[];
529 {
530 
531 	if (argc == 1)
532 		linger = !linger;
533 	else
534 		linger = atoi(argv[1]);
535 	if (argc == 1 || linger == 0) {
536 		printf("Linger on data connection close %s.\n", onoff(linger));
537 		return;
538 	}
539 	printf("Will linger for %d seconds on close of data connections.\n",
540 	   linger);
541 }
542 
543 /*
544  * Set current working directory
545  * on remote machine.
546  */
547 cd(argc, argv)
548 	char *argv[];
549 {
550 
551 	if (argc < 2) {
552 		strcat(line, " ");
553 		printf("(remote-directory) ");
554 		gets(&line[strlen(line)]);
555 		makeargv();
556 		argc = margc;
557 		argv = margv;
558 	}
559 	if (argc < 2) {
560 		printf("%s remote-directory\n", argv[0]);
561 		return;
562 	}
563 	(void) command("CWD %s", argv[1]);
564 }
565 
566 /*
567  * Set current working directory
568  * on local machine.
569  */
570 lcd(argc, argv)
571 	char *argv[];
572 {
573 	char buf[MAXPATHLEN];
574 
575 	if (argc < 2)
576 		argc++, argv[1] = home;
577 	if (argc != 2) {
578 		printf("%s local-directory\n", argv[0]);
579 		return;
580 	}
581 	if (!globulize(&argv[1]))
582 		return;
583 	if (chdir(argv[1]) < 0) {
584 		perror(argv[1]);
585 		return;
586 	}
587 	printf("Local directory now %s\n", getwd(buf));
588 }
589 
590 /*
591  * Delete a single file.
592  */
593 delete(argc, argv)
594 	char *argv[];
595 {
596 
597 	if (argc < 2) {
598 		strcat(line, " ");
599 		printf("(remote-file) ");
600 		gets(&line[strlen(line)]);
601 		makeargv();
602 		argc = margc;
603 		argv = margv;
604 	}
605 	if (argc < 2) {
606 		printf("%s remote-file\n", argv[0]);
607 		return;
608 	}
609 	(void) command("DELE %s", argv[1]);
610 }
611 
612 /*
613  * Delete multiple files.
614  */
615 mdelete(argc, argv)
616 	char *argv[];
617 {
618 	char *cp;
619 
620 	if (argc < 2) {
621 		strcat(line, " ");
622 		printf("(remote-files) ");
623 		gets(&line[strlen(line)]);
624 		makeargv();
625 		argc = margc;
626 		argv = margv;
627 	}
628 	if (argc < 2) {
629 		printf("%s remote-files\n", argv[0]);
630 		return;
631 	}
632 	while ((cp = remglob(argc, argv)) != NULL)
633 		if (confirm(argv[0], cp))
634 			(void) command("DELE %s", cp);
635 }
636 
637 /*
638  * Rename a remote file.
639  */
640 renamefile(argc, argv)
641 	char *argv[];
642 {
643 
644 	if (argc < 2) {
645 		strcat(line, " ");
646 		printf("(from-name) ");
647 		gets(&line[strlen(line)]);
648 		makeargv();
649 		argc = margc;
650 		argv = margv;
651 	}
652 	if (argc < 2) {
653 usage:
654 		printf("%s from-name to-name\n", argv[0]);
655 		return;
656 	}
657 	if (argc < 3) {
658 		strcat(line, " ");
659 		printf("(to-name) ");
660 		gets(&line[strlen(line)]);
661 		makeargv();
662 		argc = margc;
663 		argv = margv;
664 	}
665 	if (argc < 3)
666 		goto usage;
667 	if (command("RNFR %s", argv[1]) == CONTINUE)
668 		(void) command("RNTO %s", argv[2]);
669 }
670 
671 /*
672  * Get a directory listing
673  * of remote files.
674  */
675 ls(argc, argv)
676 	char *argv[];
677 {
678 	char *cmd;
679 
680 	if (argc < 2)
681 		argc++, argv[1] = NULL;
682 	if (argc < 3)
683 		argc++, argv[2] = "-";
684 	if (argc > 3) {
685 		printf("usage: %s remote-directory local-file\n", argv[0]);
686 		return;
687 	}
688 	cmd = argv[0][0] == 'l' ? "NLST" : "LIST";
689 	if (strcmp(argv[2], "-") && !globulize(&argv[2]))
690 		return;
691 	recvrequest(cmd, argv[2], argv[1], "w");
692 }
693 
694 /*
695  * Get a directory listing
696  * of multiple remote files.
697  */
698 mls(argc, argv)
699 	char *argv[];
700 {
701 	char *cmd, *mode, *cp, *dest;
702 
703 	if (argc < 2) {
704 		strcat(line, " ");
705 		printf("(remote-files) ");
706 		gets(&line[strlen(line)]);
707 		makeargv();
708 		argc = margc;
709 		argv = margv;
710 	}
711 	if (argc < 3) {
712 		strcat(line, " ");
713 		printf("(local-file) ");
714 		gets(&line[strlen(line)]);
715 		makeargv();
716 		argc = margc;
717 		argv = margv;
718 	}
719 	if (argc < 3) {
720 		printf("%s remote-files local-file\n", argv[0]);
721 		return;
722 	}
723 	dest = argv[argc - 1];
724 	argv[argc - 1] = NULL;
725 	if (strcmp(dest, "-"))
726 		if (globulize(&dest) && confirm("local-file", dest))
727 			return;
728 	cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
729 	for (mode = "w"; cp = remglob(argc, argv); mode = "a")
730 		if (confirm(argv[0], cp))
731 			recvrequest(cmd, dest, cp, mode);
732 }
733 
734 /*
735  * Do a shell escape
736  */
737 shell(argc, argv)
738 	char *argv[];
739 {
740 	int pid, status, (*old1)(), (*old2)();
741 	char shellnam[40], *shell, *namep;
742 	char **cpp, **gargs;
743 
744 	old1 = signal (SIGINT, SIG_IGN);
745 	old2 = signal (SIGQUIT, SIG_IGN);
746 	if ((pid = fork()) == 0) {
747 		for (pid = 3; pid < 20; pid++)
748 			close(pid);
749 		signal(SIGINT, SIG_DFL);
750 		signal(SIGQUIT, SIG_DFL);
751 		if (argc <= 1) {
752 			shell = getenv("SHELL");
753 			if (shell == NULL)
754 				shell = "/bin/sh";
755 			namep = rindex(shell,'/');
756 			if (namep == NULL)
757 				namep = shell;
758 			strcpy(shellnam,"-");
759 			strcat(shellnam, ++namep);
760 			if (strcmp(namep, "sh") != 0)
761 				shellnam[0] = '+';
762 			if (debug) {
763 				printf ("%s\n", shell);
764 				fflush (stdout);
765 			}
766 			execl(shell, shellnam, 0);
767 			perror(shell);
768 			exit(1);
769 		}
770 		cpp = &argv[1];
771 		if (argc > 2) {
772 			if ((gargs = glob(cpp)) != NULL)
773 				cpp = gargs;
774 			if (globerr != NULL) {
775 				printf("%s\n", globerr);
776 				exit(1);
777 			}
778 		}
779 		if (debug) {
780 			register char **zip = cpp;
781 
782 			printf("%s", *zip);
783 			while (*++zip != NULL)
784 				printf(" %s", *zip);
785 			printf("\n");
786 			fflush(stdout);
787 		}
788 		execvp(argv[1], cpp);
789 		perror(argv[1]);
790 		exit(1);
791 	}
792 	if (pid > 0)
793 		while (wait(&status) != pid)
794 			;
795 	signal(SIGINT, old1);
796 	signal(SIGQUIT, old2);
797 	if (pid == -1)
798 		perror("Try again later");
799 	return (0);
800 }
801 
802 /*
803  * Send new user information (re-login)
804  */
805 user(argc, argv)
806 	int argc;
807 	char **argv;
808 {
809 	char acct[80], *getpass();
810 	int n;
811 
812 	if (argc < 2) {
813 		strcat(line, " ");
814 		printf("(username) ");
815 		gets(&line[strlen(line)]);
816 		makeargv();
817 		argc = margc;
818 		argv = margv;
819 	}
820 	if (argc > 4) {
821 		printf("usage: %s username [password] [account]\n", argv[0]);
822 		return (0);
823 	}
824 	n = command("USER %s", argv[1]);
825 	if (n == CONTINUE) {
826 		if (argc < 3 )
827 			argv[2] = getpass("Password: "), argc++;
828 		n = command("PASS %s", argv[2]);
829 	}
830 	if (n == CONTINUE) {
831 		if (argc < 4) {
832 			printf("Account: "); (void) fflush(stdout);
833 			(void) fgets(acct, sizeof(acct) - 1, stdin);
834 			acct[strlen(acct) - 1] = '\0';
835 			argv[3] = acct; argc++;
836 		}
837 		n = command("ACCT %s", acct);
838 	}
839 	if (n != COMPLETE) {
840 		fprintf(stderr, "Login failed.\n");
841 		return (0);
842 	}
843 	return (1);
844 }
845 
846 /*
847  * Print working directory.
848  */
849 /*VARARGS*/
850 pwd()
851 {
852 
853 	(void) command("XPWD");
854 }
855 
856 /*
857  * Make a directory.
858  */
859 makedir(argc, argv)
860 	char *argv[];
861 {
862 
863 	if (argc < 2) {
864 		strcat(line, " ");
865 		printf("(directory-name) ");
866 		gets(&line[strlen(line)]);
867 		makeargv();
868 		argc = margc;
869 		argv = margv;
870 	}
871 	if (argc < 2) {
872 		printf("%s directory-name\n", argv[0]);
873 		return;
874 	}
875 	(void) command("XMKD %s", argv[1]);
876 }
877 
878 /*
879  * Remove a directory.
880  */
881 removedir(argc, argv)
882 	char *argv[];
883 {
884 
885 	if (argc < 2) {
886 		strcat(line, " ");
887 		printf("(directory-name) ");
888 		gets(&line[strlen(line)]);
889 		makeargv();
890 		argc = margc;
891 		argv = margv;
892 	}
893 	if (argc < 2) {
894 		printf("%s directory-name\n", argv[0]);
895 		return;
896 	}
897 	(void) command("XRMD %s", argv[1]);
898 }
899 
900 /*
901  * Send a line, verbatim, to the remote machine.
902  */
903 quote(argc, argv)
904 	char *argv[];
905 {
906 	int i;
907 	char buf[BUFSIZ];
908 
909 	if (argc < 2) {
910 		strcat(line, " ");
911 		printf("(command line to send) ");
912 		gets(&line[strlen(line)]);
913 		makeargv();
914 		argc = margc;
915 		argv = margv;
916 	}
917 	if (argc < 2) {
918 		printf("usage: %s line-to-send\n", argv[0]);
919 		return;
920 	}
921 	strcpy(buf, argv[1]);
922 	for (i = 2; i < argc; i++) {
923 		strcat(buf, " ");
924 		strcat(buf, argv[i]);
925 	}
926 	(void) command(buf);
927 }
928 
929 /*
930  * Ask the other side for help.
931  */
932 rmthelp(argc, argv)
933 	char *argv[];
934 {
935 	int oldverbose = verbose;
936 
937 	verbose = 1;
938 	(void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
939 	verbose = oldverbose;
940 }
941 
942 /*
943  * Terminate session and exit.
944  */
945 /*VARARGS*/
946 quit()
947 {
948 
949 	disconnect();
950 	exit(0);
951 }
952 
953 /*
954  * Terminate session, but don't exit.
955  */
956 disconnect()
957 {
958 	extern FILE *cout;
959 	extern int data;
960 
961 	if (!connected)
962 		return;
963 	(void) command("QUIT");
964 	(void) fclose(cout);
965 	cout = NULL;
966 	connected = 0;
967 	data = -1;
968 }
969 
970 confirm(cmd, file)
971 	char *cmd, *file;
972 {
973 	char line[BUFSIZ];
974 
975 	if (!interactive)
976 		return (1);
977 	printf("%s %s? ", cmd, file);
978 	fflush(stdout);
979 	gets(line);
980 	return (*line != 'n' && *line != 'N');
981 }
982 
983 fatal(msg)
984 	char *msg;
985 {
986 
987 	fprintf(stderr, "ftp: %s\n");
988 	exit(1);
989 }
990 
991 /*
992  * Glob a local file name specification with
993  * the expectation of a single return value.
994  * Can't control multiple values being expanded
995  * from the expression, we return only the first.
996  */
997 globulize(cpp)
998 	char **cpp;
999 {
1000 	char **globbed;
1001 
1002 	if (!doglob)
1003 		return (1);
1004 	globbed = glob(*cpp);
1005 	if (globerr != NULL) {
1006 		printf("%s: %s\n", *cpp, globerr);
1007 		if (globbed)
1008 			blkfree(globbed);
1009 		return (0);
1010 	}
1011 	if (globbed) {
1012 		*cpp = *globbed++;
1013 		/* don't waste too much memory */
1014 		if (*globbed)
1015 			blkfree(globbed);
1016 	}
1017 	return (1);
1018 }
1019