main.c revision 120945
1/*
2 * Copyright (c) 1985, 1989, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/*
35 * FTP User Program -- Command Interface.
36 */
37
38#include "ftp_locl.h"
39#include <getarg.h>
40
41RCSID("$Id: main.c,v 1.33 2002/10/29 09:47:51 joda Exp $");
42
43static int help_flag;
44static int version_flag;
45static int debug_flag;
46
47struct getargs getargs[] = {
48    { NULL,	'd', arg_flag, &debug_flag,
49      "debug", NULL },
50    { NULL,	'g', arg_negative_flag, &doglob,
51      "disables globbing", NULL},
52    { NULL,	'i', arg_negative_flag, &interactive,
53      "Turn off interactive prompting", NULL},
54    { NULL,	'l', arg_negative_flag, &lineedit,
55      "Turn off line editing", NULL},
56    { NULL,   'n', arg_negative_flag, &autologin,
57      "Turn off auto-login", NULL},
58    { NULL,	'p', arg_flag, &passivemode,
59      "passive mode", NULL},
60    { NULL,	't', arg_counter, &trace,
61      "Packet tracing", NULL},
62    { NULL,	'v', arg_counter, &verbose,
63      "verbosity", NULL},
64    { NULL,	'K', arg_negative_flag, &use_kerberos,
65      "Disable kerberos authentication", NULL},
66    { "version", 0,  arg_flag, &version_flag },
67    { "help",	'h', arg_flag, &help_flag },
68};
69
70static int num_args = sizeof(getargs) / sizeof(getargs[0]);
71
72static void
73usage(int ecode)
74{
75    arg_printusage(getargs, num_args, NULL, "[host [port]]");
76    exit(ecode);
77}
78
79int
80main(int argc, char **argv)
81{
82	int top;
83	struct passwd *pw = NULL;
84	char homedir[MaxPathLen];
85	struct servent *sp;
86	int optind = 0;
87
88	setprogname(argv[0]);
89
90	sp = getservbyname("ftp", "tcp");
91	if (sp == 0)
92		errx(1, "ftp/tcp: unknown service");
93	doglob = 1;
94	interactive = 1;
95	autologin = 1;
96	lineedit = 1;
97	passivemode = 0; /* passive mode not active */
98        use_kerberos = 1;
99
100	if(getarg(getargs, num_args, argc, argv, &optind))
101		usage(1);
102	if(help_flag)
103		usage(0);
104	if(version_flag) {
105		print_version(NULL);
106		exit(0);
107	}
108
109	if (debug_flag) {
110		options |= SO_DEBUG;
111		debug++;
112	}
113
114	argc -= optind;
115	argv += optind;
116
117	fromatty = isatty(fileno(stdin));
118	if (fromatty)
119		verbose++;
120	cpend = 0;	/* no pending replies */
121	proxy = 0;	/* proxy not active */
122	crflag = 1;	/* strip c.r. on ascii gets */
123	sendport = -1;	/* not using ports */
124	/*
125	 * Set up the home directory in case we're globbing.
126	 */
127	pw = k_getpwuid(getuid());
128	if (pw != NULL) {
129		strlcpy(homedir, pw->pw_dir, sizeof(homedir));
130		home = homedir;
131	}
132	if (argc > 0) {
133	    char *xargv[5];
134
135	    if (setjmp(toplevel))
136		exit(0);
137	    signal(SIGINT, intr);
138	    signal(SIGPIPE, lostpeer);
139	    xargv[0] = (char*)getprogname();
140	    xargv[1] = argv[0];
141	    xargv[2] = argv[1];
142	    xargv[3] = argv[2];
143	    xargv[4] = NULL;
144	    setpeer(argc+1, xargv);
145	}
146	if(setjmp(toplevel) == 0)
147	    top = 1;
148	else
149	    top = 0;
150	if (top) {
151	    signal(SIGINT, intr);
152	    signal(SIGPIPE, lostpeer);
153	}
154	for (;;) {
155	    cmdscanner(top);
156	    top = 1;
157	}
158}
159
160void
161intr(int sig)
162{
163
164	longjmp(toplevel, 1);
165}
166
167#ifndef SHUT_RDWR
168#define SHUT_RDWR 2
169#endif
170
171RETSIGTYPE
172lostpeer(int sig)
173{
174
175    if (connected) {
176	if (cout != NULL) {
177	    shutdown(fileno(cout), SHUT_RDWR);
178	    fclose(cout);
179	    cout = NULL;
180	}
181	if (data >= 0) {
182	    shutdown(data, SHUT_RDWR);
183	    close(data);
184	    data = -1;
185	}
186	connected = 0;
187    }
188    pswitch(1);
189    if (connected) {
190	if (cout != NULL) {
191	    shutdown(fileno(cout), SHUT_RDWR);
192	    fclose(cout);
193	    cout = NULL;
194	}
195	connected = 0;
196    }
197    proxflag = 0;
198    pswitch(0);
199    sec_end();
200    SIGRETURN(0);
201}
202
203/*
204char *
205tail(filename)
206	char *filename;
207{
208	char *s;
209
210	while (*filename) {
211		s = strrchr(filename, '/');
212		if (s == NULL)
213			break;
214		if (s[1])
215			return (s + 1);
216		*s = '\0';
217	}
218	return (filename);
219}
220*/
221
222static char *
223simple_readline(char *prompt)
224{
225    char buf[BUFSIZ];
226    printf ("%s", prompt);
227    fflush (stdout);
228    if(fgets(buf, sizeof(buf), stdin) == NULL)
229	return NULL;
230    if (buf[strlen(buf) - 1] == '\n')
231	buf[strlen(buf) - 1] = '\0';
232    return strdup(buf);
233}
234
235#ifndef HAVE_READLINE
236
237static char *
238readline(char *prompt)
239{
240    return simple_readline (prompt);
241}
242
243static void
244add_history(char *p)
245{
246}
247
248#else
249
250/* These should not really be here */
251
252char *readline(char *);
253void add_history(char *);
254
255#endif
256
257/*
258 * Command parser.
259 */
260void
261cmdscanner(int top)
262{
263    struct cmd *c;
264    int l;
265
266    if (!top)
267	putchar('\n');
268    for (;;) {
269	if (fromatty) {
270	    char *p;
271	    if (lineedit)
272		p = readline("ftp> ");
273	    else
274		p = simple_readline("ftp> ");
275	    if(p == NULL) {
276		printf("\n");
277		quit(0, 0);
278	    }
279	    strlcpy(line, p, sizeof(line));
280	    if (lineedit)
281		add_history(p);
282	    free(p);
283	} else{
284	    if (fgets(line, sizeof line, stdin) == NULL)
285		quit(0, 0);
286	}
287	/* XXX will break on long lines */
288	l = strlen(line);
289	if (l == 0)
290	    break;
291	if (line[--l] == '\n') {
292	    if (l == 0)
293		break;
294	    line[l] = '\0';
295	} else if (l == sizeof(line) - 2) {
296	    printf("sorry, input line too long\n");
297	    while ((l = getchar()) != '\n' && l != EOF)
298		/* void */;
299	    break;
300	} /* else it was a line without a newline */
301	makeargv();
302	if (margc == 0) {
303	    continue;
304	}
305	c = getcmd(margv[0]);
306	if (c == (struct cmd *)-1) {
307	    printf("?Ambiguous command\n");
308	    continue;
309	}
310	if (c == 0) {
311	    printf("?Invalid command\n");
312	    continue;
313	}
314	if (c->c_conn && !connected) {
315	    printf("Not connected.\n");
316	    continue;
317	}
318	(*c->c_handler)(margc, margv);
319	if (bell && c->c_bell)
320	    putchar('\007');
321	if (c->c_handler != help)
322	    break;
323    }
324    signal(SIGINT, intr);
325    signal(SIGPIPE, lostpeer);
326}
327
328struct cmd *
329getcmd(char *name)
330{
331	char *p, *q;
332	struct cmd *c, *found;
333	int nmatches, longest;
334
335	longest = 0;
336	nmatches = 0;
337	found = 0;
338	for (c = cmdtab; (p = c->c_name); c++) {
339		for (q = name; *q == *p++; q++)
340			if (*q == 0)		/* exact match? */
341				return (c);
342		if (!*q) {			/* the name was a prefix */
343			if (q - name > longest) {
344				longest = q - name;
345				nmatches = 1;
346				found = c;
347			} else if (q - name == longest)
348				nmatches++;
349		}
350	}
351	if (nmatches > 1)
352		return ((struct cmd *)-1);
353	return (found);
354}
355
356/*
357 * Slice a string up into argc/argv.
358 */
359
360int slrflag;
361
362void
363makeargv(void)
364{
365	char **argp;
366
367	argp = margv;
368	stringbase = line;		/* scan from first of buffer */
369	argbase = argbuf;		/* store from first of buffer */
370	slrflag = 0;
371	for (margc = 0; ; margc++) {
372		/* Expand array if necessary */
373		if (margc == margvlen) {
374			int i;
375
376			margv = (margvlen == 0)
377				? (char **)malloc(20 * sizeof(char *))
378				: (char **)realloc(margv,
379					(margvlen + 20)*sizeof(char *));
380			if (margv == NULL)
381				errx(1, "cannot realloc argv array");
382			for(i = margvlen; i < margvlen + 20; ++i)
383				margv[i] = NULL;
384			margvlen += 20;
385			argp = margv + margc;
386		}
387
388		if ((*argp++ = slurpstring()) == NULL)
389			break;
390	}
391
392}
393
394/*
395 * Parse string into argbuf;
396 * implemented with FSM to
397 * handle quoting and strings
398 */
399char *
400slurpstring(void)
401{
402	int got_one = 0;
403	char *sb = stringbase;
404	char *ap = argbase;
405	char *tmp = argbase;		/* will return this if token found */
406
407	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
408		switch (slrflag) {	/* and $ as token for macro invoke */
409			case 0:
410				slrflag++;
411				stringbase++;
412				return ((*sb == '!') ? "!" : "$");
413				/* NOTREACHED */
414			case 1:
415				slrflag++;
416				altarg = stringbase;
417				break;
418			default:
419				break;
420		}
421	}
422
423S0:
424	switch (*sb) {
425
426	case '\0':
427		goto OUT;
428
429	case ' ':
430	case '\t':
431		sb++; goto S0;
432
433	default:
434		switch (slrflag) {
435			case 0:
436				slrflag++;
437				break;
438			case 1:
439				slrflag++;
440				altarg = sb;
441				break;
442			default:
443				break;
444		}
445		goto S1;
446	}
447
448S1:
449	switch (*sb) {
450
451	case ' ':
452	case '\t':
453	case '\0':
454		goto OUT;	/* end of token */
455
456	case '\\':
457		sb++; goto S2;	/* slurp next character */
458
459	case '"':
460		sb++; goto S3;	/* slurp quoted string */
461
462	default:
463		*ap++ = *sb++;	/* add character to token */
464		got_one = 1;
465		goto S1;
466	}
467
468S2:
469	switch (*sb) {
470
471	case '\0':
472		goto OUT;
473
474	default:
475		*ap++ = *sb++;
476		got_one = 1;
477		goto S1;
478	}
479
480S3:
481	switch (*sb) {
482
483	case '\0':
484		goto OUT;
485
486	case '"':
487		sb++; goto S1;
488
489	default:
490		*ap++ = *sb++;
491		got_one = 1;
492		goto S3;
493	}
494
495OUT:
496	if (got_one)
497		*ap++ = '\0';
498	argbase = ap;			/* update storage pointer */
499	stringbase = sb;		/* update scan pointer */
500	if (got_one) {
501		return (tmp);
502	}
503	switch (slrflag) {
504		case 0:
505			slrflag++;
506			break;
507		case 1:
508			slrflag++;
509			altarg = (char *) 0;
510			break;
511		default:
512			break;
513	}
514	return NULL;
515}
516
517#define HELPINDENT ((int) sizeof ("directory"))
518
519/*
520 * Help command.
521 * Call each command handler with argc == 0 and argv[0] == name.
522 */
523void
524help(int argc, char **argv)
525{
526	struct cmd *c;
527
528	if (argc == 1) {
529		int i, j, w, k;
530		int columns, width = 0, lines;
531
532		printf("Commands may be abbreviated.  Commands are:\n\n");
533		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
534			int len = strlen(c->c_name);
535
536			if (len > width)
537				width = len;
538		}
539		width = (width + 8) &~ 7;
540		columns = 80 / width;
541		if (columns == 0)
542			columns = 1;
543		lines = (NCMDS + columns - 1) / columns;
544		for (i = 0; i < lines; i++) {
545			for (j = 0; j < columns; j++) {
546				c = cmdtab + j * lines + i;
547				if (c->c_name && (!proxy || c->c_proxy)) {
548					printf("%s", c->c_name);
549				}
550				else if (c->c_name) {
551					for (k=0; k < strlen(c->c_name); k++) {
552						putchar(' ');
553					}
554				}
555				if (c + lines >= &cmdtab[NCMDS]) {
556					printf("\n");
557					break;
558				}
559				w = strlen(c->c_name);
560				while (w < width) {
561					w = (w + 8) &~ 7;
562					putchar('\t');
563				}
564			}
565		}
566		return;
567	}
568	while (--argc > 0) {
569		char *arg;
570		arg = *++argv;
571		c = getcmd(arg);
572		if (c == (struct cmd *)-1)
573			printf("?Ambiguous help command %s\n", arg);
574		else if (c == (struct cmd *)0)
575			printf("?Invalid help command %s\n", arg);
576		else
577			printf("%-*s\t%s\n", HELPINDENT,
578				c->c_name, c->c_help);
579	}
580}
581