GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ftp/main.c Lines: 0 411 0.0 %
Date: 2016-12-06 Branches: 0 317 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: main.c,v 1.109 2016/07/13 16:35:47 jsing Exp $	*/
2
/*	$NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $	*/
3
4
/*
5
 * Copyright (C) 1997 and 1998 WIDE Project.
6
 * All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the project nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
/*
34
 * Copyright (c) 1985, 1989, 1993, 1994
35
 *	The Regents of the University of California.  All rights reserved.
36
 *
37
 * Redistribution and use in source and binary forms, with or without
38
 * modification, are permitted provided that the following conditions
39
 * are met:
40
 * 1. Redistributions of source code must retain the above copyright
41
 *    notice, this list of conditions and the following disclaimer.
42
 * 2. Redistributions in binary form must reproduce the above copyright
43
 *    notice, this list of conditions and the following disclaimer in the
44
 *    documentation and/or other materials provided with the distribution.
45
 * 3. Neither the name of the University nor the names of its contributors
46
 *    may be used to endorse or promote products derived from this software
47
 *    without specific prior written permission.
48
 *
49
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59
 * SUCH DAMAGE.
60
 */
61
62
/*
63
 * FTP User Program -- Command Interface.
64
 */
65
#include <sys/types.h>
66
#include <sys/socket.h>
67
68
#include <ctype.h>
69
#include <err.h>
70
#include <netdb.h>
71
#include <pwd.h>
72
#include <stdio.h>
73
#include <errno.h>
74
#include <stdlib.h>
75
#include <string.h>
76
#include <unistd.h>
77
78
#include <tls.h>
79
80
#include "cmds.h"
81
#include "ftp_var.h"
82
83
#ifndef SMALL
84
char * const ssl_verify_opts[] = {
85
#define SSL_CAFILE	0
86
	"cafile",
87
#define SSL_CAPATH	1
88
	"capath",
89
#define SSL_CIPHERS	2
90
	"ciphers",
91
#define SSL_DONTVERIFY	3
92
	"dont",
93
#define SSL_DOVERIFY	4
94
	"do",
95
#define SSL_VERIFYDEPTH	5
96
	"depth",
97
	NULL
98
};
99
100
struct tls_config *tls_config;
101
#endif /* !SMALL */
102
103
int family = PF_UNSPEC;
104
int pipeout;
105
106
int
107
main(volatile int argc, char *argv[])
108
{
109
	int ch, top, rval;
110
	struct passwd *pw = NULL;
111
	char *cp, homedir[PATH_MAX];
112
	char *outfile = NULL;
113
	const char *errstr;
114
	int dumb_terminal = 0;
115
#ifndef SMALL
116
	long long depth;
117
#endif
118
119
	ftpport = "ftp";
120
	httpport = "http";
121
#ifndef SMALL
122
	httpsport = "https";
123
#endif /* !SMALL */
124
	gateport = getenv("FTPSERVERPORT");
125
	if (gateport == NULL || *gateport == '\0')
126
		gateport = "ftpgate";
127
	doglob = 1;
128
	interactive = 1;
129
	autologin = 1;
130
	passivemode = 1;
131
	activefallback = 1;
132
	preserve = 1;
133
	verbose = 0;
134
	progress = 0;
135
	gatemode = 0;
136
#ifndef SMALL
137
	editing = 0;
138
	el = NULL;
139
	hist = NULL;
140
	cookiefile = NULL;
141
	resume = 0;
142
	srcaddr = NULL;
143
	marg_sl = sl_init();
144
#endif /* !SMALL */
145
	mark = HASHBYTES;
146
	epsv4 = 1;
147
	epsv4bad = 0;
148
149
	/* Set default operation mode based on FTPMODE environment variable */
150
	if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') {
151
		if (strcmp(cp, "passive") == 0) {
152
			passivemode = 1;
153
			activefallback = 0;
154
		} else if (strcmp(cp, "active") == 0) {
155
			passivemode = 0;
156
			activefallback = 0;
157
		} else if (strcmp(cp, "gate") == 0) {
158
			gatemode = 1;
159
		} else if (strcmp(cp, "auto") == 0) {
160
			passivemode = 1;
161
			activefallback = 1;
162
		} else
163
			warnx("unknown FTPMODE: %s.  Using defaults", cp);
164
	}
165
166
	if (strcmp(__progname, "gate-ftp") == 0)
167
		gatemode = 1;
168
	gateserver = getenv("FTPSERVER");
169
	if (gateserver == NULL)
170
		gateserver = "";
171
	if (gatemode) {
172
		if (*gateserver == '\0') {
173
			warnx(
174
"Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp");
175
			gatemode = 0;
176
		}
177
	}
178
179
	cp = getenv("TERM");
180
	dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") ||
181
	    !strcmp(cp, "emacs") || !strcmp(cp, "su"));
182
	fromatty = isatty(fileno(stdin));
183
	if (fromatty) {
184
		verbose = 1;		/* verbose if from a tty */
185
#ifndef SMALL
186
		if (!dumb_terminal)
187
			editing = 1;	/* editing mode on if tty is usable */
188
#endif /* !SMALL */
189
	}
190
191
	ttyout = stdout;
192
	if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc())
193
		progress = 1;		/* progress bar on if tty is usable */
194
195
#ifndef SMALL
196
	cookiefile = getenv("http_cookies");
197
	if (tls_init() != 0)
198
		errx(1, "tls init failed");
199
	if (tls_config == NULL) {
200
		tls_config = tls_config_new();
201
		if (tls_config == NULL)
202
			errx(1, "tls config failed");
203
		tls_config_set_protocols(tls_config, TLS_PROTOCOLS_ALL);
204
		if (tls_config_set_ciphers(tls_config, "all") != 0)
205
			errx(1, "tls set ciphers failed");
206
	}
207
#endif /* !SMALL */
208
209
	httpuseragent = NULL;
210
211
	while ((ch = getopt(argc, argv,
212
		    "46AaCc:dD:Eegik:Mmno:pP:r:S:s:tU:vV")) != -1) {
213
		switch (ch) {
214
		case '4':
215
			family = PF_INET;
216
			break;
217
		case '6':
218
			family = PF_INET6;
219
			break;
220
		case 'A':
221
			activefallback = 0;
222
			passivemode = 0;
223
			break;
224
225
		case 'a':
226
			anonftp = 1;
227
			break;
228
229
		case 'C':
230
#ifndef SMALL
231
			resume = 1;
232
#endif /* !SMALL */
233
			break;
234
235
		case 'c':
236
#ifndef SMALL
237
			cookiefile = optarg;
238
#endif /* !SMALL */
239
			break;
240
241
		case 'D':
242
			action = optarg;
243
			break;
244
		case 'd':
245
#ifndef SMALL
246
			options |= SO_DEBUG;
247
			debug++;
248
#endif /* !SMALL */
249
			break;
250
251
		case 'E':
252
			epsv4 = 0;
253
			break;
254
255
		case 'e':
256
#ifndef SMALL
257
			editing = 0;
258
#endif /* !SMALL */
259
			break;
260
261
		case 'g':
262
			doglob = 0;
263
			break;
264
265
		case 'i':
266
			interactive = 0;
267
			break;
268
269
		case 'k':
270
			keep_alive_timeout = strtonum(optarg, 0, INT_MAX,
271
			    &errstr);
272
			if (errstr != NULL) {
273
				warnx("keep alive amount is %s: %s", errstr,
274
					optarg);
275
				usage();
276
			}
277
			break;
278
		case 'M':
279
			progress = 0;
280
			break;
281
		case 'm':
282
			progress = -1;
283
			break;
284
285
		case 'n':
286
			autologin = 0;
287
			break;
288
289
		case 'o':
290
			outfile = optarg;
291
			if (*outfile == '\0') {
292
				pipeout = 0;
293
				outfile = NULL;
294
				ttyout = stdout;
295
			} else {
296
				pipeout = strcmp(outfile, "-") == 0;
297
				ttyout = pipeout ? stderr : stdout;
298
			}
299
			break;
300
301
		case 'p':
302
			passivemode = 1;
303
			activefallback = 0;
304
			break;
305
306
		case 'P':
307
			ftpport = optarg;
308
			break;
309
310
		case 'r':
311
			retry_connect = strtonum(optarg, 0, INT_MAX, &errstr);
312
			if (errstr != NULL) {
313
				warnx("retry amount is %s: %s", errstr,
314
					optarg);
315
				usage();
316
			}
317
			break;
318
319
		case 'S':
320
#ifndef SMALL
321
			cp = optarg;
322
			while (*cp) {
323
				char	*str;
324
				switch (getsubopt(&cp, ssl_verify_opts, &str)) {
325
				case SSL_CAFILE:
326
					if (str == NULL)
327
						errx(1, "missing CA file");
328
					if (tls_config_set_ca_file(
329
					    tls_config, str) != 0)
330
						errx(1, "tls ca file failed");
331
					break;
332
				case SSL_CAPATH:
333
					if (str == NULL)
334
						errx(1, "missing CA directory"
335
						    " path");
336
					if (tls_config_set_ca_path(
337
					    tls_config, str) != 0)
338
						errx(1, "tls ca path failed");
339
					break;
340
				case SSL_CIPHERS:
341
					if (str == NULL)
342
						errx(1, "missing cipher list");
343
					if (tls_config_set_ciphers(
344
					    tls_config, str) != 0)
345
						errx(1, "tls ciphers failed");
346
					break;
347
				case SSL_DONTVERIFY:
348
					tls_config_insecure_noverifycert(
349
					    tls_config);
350
					tls_config_insecure_noverifyname(
351
					    tls_config);
352
					break;
353
				case SSL_DOVERIFY:
354
					tls_config_verify(tls_config);
355
					break;
356
				case SSL_VERIFYDEPTH:
357
					if (str == NULL)
358
						errx(1, "missing depth");
359
					depth = strtonum(str, 0, INT_MAX,
360
					    &errstr);
361
					if (errstr)
362
						errx(1, "certificate "
363
						    "validation depth is %s",
364
						    errstr);
365
					tls_config_set_verify_depth(
366
					    tls_config, (int)depth);
367
					break;
368
				default:
369
					errx(1, "unknown -S suboption `%s'",
370
					    suboptarg ? suboptarg : "");
371
					/* NOTREACHED */
372
				}
373
			}
374
#endif
375
			break;
376
377
		case 's':
378
#ifndef SMALL
379
			srcaddr = optarg;
380
#endif /* !SMALL */
381
			break;
382
383
		case 't':
384
			trace = 1;
385
			break;
386
387
#ifndef SMALL
388
		case 'U':
389
			free (httpuseragent);
390
			if (strcspn(optarg, "\r\n") != strlen(optarg))
391
				errx(1, "Invalid User-Agent: %s.", optarg);
392
			if (asprintf(&httpuseragent, "User-Agent: %s",
393
			    optarg) == -1)
394
				errx(1, "Can't allocate memory for HTTP(S) "
395
				    "User-Agent");
396
			break;
397
#endif /* !SMALL */
398
399
		case 'v':
400
			verbose = 1;
401
			break;
402
403
		case 'V':
404
			verbose = 0;
405
			break;
406
407
		default:
408
			usage();
409
		}
410
	}
411
	argc -= optind;
412
	argv += optind;
413
414
#ifndef SMALL
415
	cookie_load();
416
#endif /* !SMALL */
417
	if (httpuseragent == NULL)
418
		httpuseragent = HTTP_USER_AGENT;
419
420
	cpend = 0;	/* no pending replies */
421
	proxy = 0;	/* proxy not active */
422
	crflag = 1;	/* strip c.r. on ascii gets */
423
	sendport = -1;	/* not using ports */
424
	/*
425
	 * Set up the home directory in case we're globbing.
426
	 */
427
	cp = getlogin();
428
	if (cp != NULL) {
429
		pw = getpwnam(cp);
430
	}
431
	if (pw == NULL)
432
		pw = getpwuid(getuid());
433
	if (pw != NULL) {
434
		(void)strlcpy(homedir, pw->pw_dir, sizeof homedir);
435
		home = homedir;
436
	}
437
438
	setttywidth(0);
439
	(void)signal(SIGWINCH, setttywidth);
440
441
	if (argc > 0) {
442
		if (isurl(argv[0])) {
443
			if (pipeout) {
444
#ifndef SMALL
445
				if (pledge("stdio rpath dns tty inet proc exec fattr",
446
				    NULL) == -1)
447
					err(1, "pledge");
448
#else
449
				if (pledge("stdio rpath dns tty inet fattr",
450
				    NULL) == -1)
451
					err(1, "pledge");
452
#endif
453
			} else {
454
#ifndef SMALL
455
				if (pledge("stdio rpath wpath cpath dns tty inet proc exec fattr",
456
				    NULL) == -1)
457
					err(1, "pledge");
458
#else
459
				if (pledge("stdio rpath wpath cpath dns tty inet fattr",
460
				    NULL) == -1)
461
					err(1, "pledge");
462
#endif
463
			}
464
465
			rval = auto_fetch(argc, argv, outfile);
466
			if (rval >= 0)		/* -1 == connected and cd-ed */
467
				exit(rval);
468
		} else {
469
#ifndef SMALL
470
			char *xargv[5];
471
472
			if (setjmp(toplevel))
473
				exit(0);
474
			(void)signal(SIGINT, (sig_t)intr);
475
			(void)signal(SIGPIPE, (sig_t)lostpeer);
476
			xargv[0] = __progname;
477
			xargv[1] = argv[0];
478
			xargv[2] = argv[1];
479
			xargv[3] = argv[2];
480
			xargv[4] = NULL;
481
			do {
482
				setpeer(argc+1, xargv);
483
				if (!retry_connect)
484
					break;
485
				if (!connected) {
486
					macnum = 0;
487
					fputs("Retrying...\n", ttyout);
488
					sleep(retry_connect);
489
				}
490
			} while (!connected);
491
			retry_connect = 0; /* connected, stop hiding msgs */
492
#endif /* !SMALL */
493
		}
494
	}
495
#ifndef SMALL
496
	controlediting();
497
	top = setjmp(toplevel) == 0;
498
	if (top) {
499
		(void)signal(SIGINT, (sig_t)intr);
500
		(void)signal(SIGPIPE, (sig_t)lostpeer);
501
	}
502
	for (;;) {
503
		cmdscanner(top);
504
		top = 1;
505
	}
506
#else /* !SMALL */
507
	usage();
508
#endif /* !SMALL */
509
}
510
511
void
512
intr(void)
513
{
514
	int save_errno = errno;
515
516
	write(fileno(ttyout), "\n\r", 2);
517
	alarmtimer(0);
518
519
	errno = save_errno;
520
	longjmp(toplevel, 1);
521
}
522
523
void
524
lostpeer(void)
525
{
526
	int save_errno = errno;
527
528
	alarmtimer(0);
529
	if (connected) {
530
		if (cout != NULL) {
531
			(void)shutdown(fileno(cout), SHUT_RDWR);
532
			(void)fclose(cout);
533
			cout = NULL;
534
		}
535
		if (data >= 0) {
536
			(void)shutdown(data, SHUT_RDWR);
537
			(void)close(data);
538
			data = -1;
539
		}
540
		connected = 0;
541
	}
542
	pswitch(1);
543
	if (connected) {
544
		if (cout != NULL) {
545
			(void)shutdown(fileno(cout), SHUT_RDWR);
546
			(void)fclose(cout);
547
			cout = NULL;
548
		}
549
		connected = 0;
550
	}
551
	proxflag = 0;
552
	pswitch(0);
553
	errno = save_errno;
554
}
555
556
#ifndef SMALL
557
/*
558
 * Generate a prompt
559
 */
560
char *
561
prompt(void)
562
{
563
	return ("ftp> ");
564
}
565
566
/*
567
 * Command parser.
568
 */
569
void
570
cmdscanner(int top)
571
{
572
	struct cmd *c;
573
	int num;
574
	HistEvent hev;
575
576
	if (!top && !editing)
577
		(void)putc('\n', ttyout);
578
	for (;;) {
579
		if (!editing) {
580
			if (fromatty) {
581
				fputs(prompt(), ttyout);
582
				(void)fflush(ttyout);
583
			}
584
			if (fgets(line, sizeof(line), stdin) == NULL)
585
				quit(0, 0);
586
			num = strlen(line);
587
			if (num == 0)
588
				break;
589
			if (line[--num] == '\n') {
590
				if (num == 0)
591
					break;
592
				line[num] = '\0';
593
			} else if (num == sizeof(line) - 2) {
594
				fputs("sorry, input line too long.\n", ttyout);
595
				while ((num = getchar()) != '\n' && num != EOF)
596
					/* void */;
597
				break;
598
			} /* else it was a line without a newline */
599
		} else {
600
			const char *buf;
601
			cursor_pos = NULL;
602
603
			if ((buf = el_gets(el, &num)) == NULL || num == 0) {
604
				putc('\n', ttyout);
605
				fflush(ttyout);
606
				quit(0, 0);
607
			}
608
			if (buf[--num] == '\n') {
609
				if (num == 0)
610
					break;
611
			}
612
			if (num >= sizeof(line)) {
613
				fputs("sorry, input line too long.\n", ttyout);
614
				break;
615
			}
616
			memcpy(line, buf, (size_t)num);
617
			line[num] = '\0';
618
			history(hist, &hev, H_ENTER, buf);
619
		}
620
621
		makeargv();
622
		if (margc == 0)
623
			continue;
624
		c = getcmd(margv[0]);
625
		if (c == (struct cmd *)-1) {
626
			fputs("?Ambiguous command.\n", ttyout);
627
			continue;
628
		}
629
		if (c == 0) {
630
			/*
631
			 * Give editline(3) a shot at unknown commands.
632
			 * XXX - bogus commands with a colon in
633
			 *       them will not elicit an error.
634
			 */
635
			if (editing &&
636
			    el_parse(el, margc, (const char **)margv) != 0)
637
				fputs("?Invalid command.\n", ttyout);
638
			continue;
639
		}
640
		if (c->c_conn && !connected) {
641
			fputs("Not connected.\n", ttyout);
642
			continue;
643
		}
644
		confirmrest = 0;
645
		(*c->c_handler)(margc, margv);
646
		if (bell && c->c_bell)
647
			(void)putc('\007', ttyout);
648
		if (c->c_handler != help)
649
			break;
650
	}
651
	(void)signal(SIGINT, (sig_t)intr);
652
	(void)signal(SIGPIPE, (sig_t)lostpeer);
653
}
654
655
struct cmd *
656
getcmd(const char *name)
657
{
658
	const char *p, *q;
659
	struct cmd *c, *found;
660
	int nmatches, longest;
661
662
	if (name == NULL)
663
		return (0);
664
665
	longest = 0;
666
	nmatches = 0;
667
	found = 0;
668
	for (c = cmdtab; (p = c->c_name) != NULL; c++) {
669
		for (q = name; *q == *p++; q++)
670
			if (*q == 0)		/* exact match? */
671
				return (c);
672
		if (!*q) {			/* the name was a prefix */
673
			if (q - name > longest) {
674
				longest = q - name;
675
				nmatches = 1;
676
				found = c;
677
			} else if (q - name == longest)
678
				nmatches++;
679
		}
680
	}
681
	if (nmatches > 1)
682
		return ((struct cmd *)-1);
683
	return (found);
684
}
685
686
/*
687
 * Slice a string up into argc/argv.
688
 */
689
690
int slrflag;
691
692
void
693
makeargv(void)
694
{
695
	char *argp;
696
697
	stringbase = line;		/* scan from first of buffer */
698
	argbase = argbuf;		/* store from first of buffer */
699
	slrflag = 0;
700
	marg_sl->sl_cur = 0;		/* reset to start of marg_sl */
701
	for (margc = 0; ; margc++) {
702
		argp = slurpstring();
703
		sl_add(marg_sl, argp);
704
		if (argp == NULL)
705
			break;
706
	}
707
	if (cursor_pos == line) {
708
		cursor_argc = 0;
709
		cursor_argo = 0;
710
	} else if (cursor_pos != NULL) {
711
		cursor_argc = margc;
712
		cursor_argo = strlen(margv[margc-1]);
713
	}
714
}
715
716
#define INC_CHKCURSOR(x)	{ (x)++ ; \
717
				if (x == cursor_pos) { \
718
					cursor_argc = margc; \
719
					cursor_argo = ap-argbase; \
720
					cursor_pos = NULL; \
721
				} }
722
723
/*
724
 * Parse string into argbuf;
725
 * implemented with FSM to
726
 * handle quoting and strings
727
 */
728
char *
729
slurpstring(void)
730
{
731
	int got_one = 0;
732
	char *sb = stringbase;
733
	char *ap = argbase;
734
	char *tmp = argbase;		/* will return this if token found */
735
736
	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
737
		switch (slrflag) {	/* and $ as token for macro invoke */
738
			case 0:
739
				slrflag++;
740
				INC_CHKCURSOR(stringbase);
741
				return ((*sb == '!') ? "!" : "$");
742
				/* NOTREACHED */
743
			case 1:
744
				slrflag++;
745
				altarg = stringbase;
746
				break;
747
			default:
748
				break;
749
		}
750
	}
751
752
S0:
753
	switch (*sb) {
754
755
	case '\0':
756
		goto OUT;
757
758
	case ' ':
759
	case '\t':
760
		INC_CHKCURSOR(sb);
761
		goto S0;
762
763
	default:
764
		switch (slrflag) {
765
			case 0:
766
				slrflag++;
767
				break;
768
			case 1:
769
				slrflag++;
770
				altarg = sb;
771
				break;
772
			default:
773
				break;
774
		}
775
		goto S1;
776
	}
777
778
S1:
779
	switch (*sb) {
780
781
	case ' ':
782
	case '\t':
783
	case '\0':
784
		goto OUT;	/* end of token */
785
786
	case '\\':
787
		INC_CHKCURSOR(sb);
788
		goto S2;	/* slurp next character */
789
790
	case '"':
791
		INC_CHKCURSOR(sb);
792
		goto S3;	/* slurp quoted string */
793
794
	default:
795
		*ap = *sb;	/* add character to token */
796
		ap++;
797
		INC_CHKCURSOR(sb);
798
		got_one = 1;
799
		goto S1;
800
	}
801
802
S2:
803
	switch (*sb) {
804
805
	case '\0':
806
		goto OUT;
807
808
	default:
809
		*ap = *sb;
810
		ap++;
811
		INC_CHKCURSOR(sb);
812
		got_one = 1;
813
		goto S1;
814
	}
815
816
S3:
817
	switch (*sb) {
818
819
	case '\0':
820
		goto OUT;
821
822
	case '"':
823
		INC_CHKCURSOR(sb);
824
		goto S1;
825
826
	default:
827
		*ap = *sb;
828
		ap++;
829
		INC_CHKCURSOR(sb);
830
		got_one = 1;
831
		goto S3;
832
	}
833
834
OUT:
835
	if (got_one)
836
		*ap++ = '\0';
837
	argbase = ap;			/* update storage pointer */
838
	stringbase = sb;		/* update scan pointer */
839
	if (got_one) {
840
		return (tmp);
841
	}
842
	switch (slrflag) {
843
		case 0:
844
			slrflag++;
845
			break;
846
		case 1:
847
			slrflag++;
848
			altarg = (char *) 0;
849
			break;
850
		default:
851
			break;
852
	}
853
	return (NULL);
854
}
855
856
/*
857
 * Help command.
858
 * Call each command handler with argc == 0 and argv[0] == name.
859
 */
860
void
861
help(int argc, char *argv[])
862
{
863
	struct cmd *c;
864
865
	if (argc == 1) {
866
		StringList *buf;
867
868
		buf = sl_init();
869
		fprintf(ttyout, "%sommands may be abbreviated.  Commands are:\n\n",
870
		    proxy ? "Proxy c" : "C");
871
		for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
872
			if (c->c_name && (!proxy || c->c_proxy))
873
				sl_add(buf, c->c_name);
874
		list_vertical(buf);
875
		sl_free(buf, 0);
876
		return;
877
	}
878
879
#define HELPINDENT ((int) sizeof("disconnect"))
880
881
	while (--argc > 0) {
882
		char *arg;
883
884
		arg = *++argv;
885
		c = getcmd(arg);
886
		if (c == (struct cmd *)-1)
887
			fprintf(ttyout, "?Ambiguous help command %s\n", arg);
888
		else if (c == NULL)
889
			fprintf(ttyout, "?Invalid help command %s\n", arg);
890
		else
891
			fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
892
				c->c_name, c->c_help);
893
	}
894
}
895
#endif /* !SMALL */
896
897
void
898
usage(void)
899
{
900
	fprintf(stderr, "usage: "
901
#ifndef SMALL
902
	    "%1$s [-46AadEegiMmnptVv] [-D title] [-k seconds] [-P port] "
903
	    "[-r seconds]\n"
904
	    "           [-s srcaddr] [host [port]]\n"
905
	    "       %1$s [-C] [-o output] [-s srcaddr]\n"
906
	    "           ftp://[user:password@]host[:port]/file[/] ...\n"
907
	    "       %1$s [-C] [-c cookie] [-o output] [-S ssl_options] "
908
	    "[-s srcaddr]\n"
909
	    "           [-U useragent] "
910
	    "http[s]://[user:password@]host[:port]/file ...\n"
911
	    "       %1$s [-C] [-o output] [-s srcaddr] file:file ...\n"
912
	    "       %1$s [-C] [-o output] [-s srcaddr] host:/file[/] ...\n",
913
#else /* !SMALL */
914
	    "%1$s [-o output] ftp://[user:password@]host[:port]/file[/] ...\n"
915
	    "       %1$s [-o output] http://host[:port]/file ...\n"
916
	    "       %1$s [-o output] file:file ...\n"
917
	    "       %1$s [-o output] host:/file[/] ...\n",
918
#endif /* !SMALL */
919
	    __progname);
920
	exit(1);
921
}