main.c revision 1.73 1 /* $NetBSD: main.c,v 1.73 2000/07/18 07:16:56 lukem Exp $ */
2
3 /*-
4 * Copyright (c) 1996-2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Copyright (c) 1985, 1989, 1993, 1994
41 * The Regents of the University of California. All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by the University of
54 * California, Berkeley and its contributors.
55 * 4. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
70 */
71
72 /*
73 * Copyright (C) 1997 and 1998 WIDE Project.
74 * All rights reserved.
75 *
76 * Redistribution and use in source and binary forms, with or without
77 * modification, are permitted provided that the following conditions
78 * are met:
79 * 1. Redistributions of source code must retain the above copyright
80 * notice, this list of conditions and the following disclaimer.
81 * 2. Redistributions in binary form must reproduce the above copyright
82 * notice, this list of conditions and the following disclaimer in the
83 * documentation and/or other materials provided with the distribution.
84 * 3. Neither the name of the project nor the names of its contributors
85 * may be used to endorse or promote products derived from this software
86 * without specific prior written permission.
87 *
88 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
89 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
90 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
91 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
92 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
93 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
94 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
95 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
96 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
97 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
98 * SUCH DAMAGE.
99 */
100
101 #include <sys/cdefs.h>
102 #ifndef lint
103 __COPYRIGHT("@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
104 The Regents of the University of California. All rights reserved.\n");
105 #endif /* not lint */
106
107 #ifndef lint
108 #if 0
109 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94";
110 #else
111 __RCSID("$NetBSD: main.c,v 1.73 2000/07/18 07:16:56 lukem Exp $");
112 #endif
113 #endif /* not lint */
114
115 /*
116 * FTP User Program -- Command Interface.
117 */
118 #include <sys/types.h>
119 #include <sys/socket.h>
120
121 #include <err.h>
122 #include <errno.h>
123 #include <netdb.h>
124 #include <paths.h>
125 #include <pwd.h>
126 #include <stdio.h>
127 #include <stdlib.h>
128 #include <string.h>
129 #include <unistd.h>
130
131 #define GLOBAL /* force GLOBAL decls in ftp_var.h to be declared */
132 #include "ftp_var.h"
133
134 #define FTP_PROXY "ftp_proxy" /* env var with FTP proxy location */
135 #define HTTP_PROXY "http_proxy" /* env var with HTTP proxy location */
136 #define NO_PROXY "no_proxy" /* env var with list of non-proxied
137 * hosts, comma or space separated */
138
139 static void setupoption(char *, char *, char *);
140 int main(int, char *[]);
141
142 int
143 main(int argc, char *argv[])
144 {
145 int ch, rval;
146 struct passwd *pw = NULL;
147 char *cp, *ep, *anonuser, *anonpass, *upload_path;
148 int dumbterm, s, len, isupload;
149
150 ftpport = "ftp";
151 httpport = "http";
152 gateport = NULL;
153 cp = getenv("FTPSERVERPORT");
154 if (cp != NULL)
155 gateport = cp;
156 else
157 gateport = "ftpgate";
158 doglob = 1;
159 interactive = 1;
160 autologin = 1;
161 passivemode = 1;
162 activefallback = 1;
163 preserve = 1;
164 verbose = 0;
165 progress = 0;
166 gatemode = 0;
167 data = -1;
168 outfile = NULL;
169 restartautofetch = 0;
170 #ifndef NO_EDITCOMPLETE
171 editing = 0;
172 el = NULL;
173 hist = NULL;
174 #endif
175 bytes = 0;
176 mark = HASHBYTES;
177 rate_get = 0;
178 rate_get_incr = DEFAULTINCR;
179 rate_put = 0;
180 rate_put_incr = DEFAULTINCR;
181 #ifdef INET6
182 epsv4 = 1;
183 #else
184 epsv4 = 0;
185 #endif
186 epsv4bad = 0;
187 upload_path = NULL;
188 isupload = 0;
189 reply_callback = NULL;
190
191 /*
192 * Get the default socket buffer sizes if we don't already have them.
193 * It doesn't matter which socket we do this to, because on the first
194 * call no socket buffer sizes will have been modified, so we are
195 * guaranteed to get the system defaults.
196 */
197 s = socket(AF_INET, SOCK_STREAM, 0);
198 if (s == -1)
199 err(1, "can't create socket");
200 len = sizeof(rcvbuf_size);
201 if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *) &rcvbuf_size, &len)
202 < 0)
203 err(1, "unable to get default rcvbuf size");
204 len = sizeof(sndbuf_size);
205 if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *) &sndbuf_size, &len)
206 < 0)
207 err(1, "unable to get default sndbuf size");
208 (void)close(s);
209 /* sanity check returned buffer sizes */
210 if (rcvbuf_size <= 0)
211 rcvbuf_size = 8192;
212 if (sndbuf_size <= 0)
213 sndbuf_size = 8192;
214
215 marg_sl = xsl_init();
216 if ((tmpdir = getenv("TMPDIR")) == NULL)
217 tmpdir = _PATH_TMP;
218
219 /* Set default operation mode based on FTPMODE environment variable */
220 if ((cp = getenv("FTPMODE")) != NULL) {
221 if (strcasecmp(cp, "passive") == 0) {
222 passivemode = 1;
223 activefallback = 0;
224 } else if (strcasecmp(cp, "active") == 0) {
225 passivemode = 0;
226 activefallback = 0;
227 } else if (strcasecmp(cp, "gate") == 0) {
228 gatemode = 1;
229 } else if (strcasecmp(cp, "auto") == 0) {
230 passivemode = 1;
231 activefallback = 1;
232 } else
233 warnx("unknown $FTPMODE '%s'; using defaults", cp);
234 }
235
236 if (strcmp(__progname, "pftp") == 0) {
237 passivemode = 1;
238 activefallback = 0;
239 } else if (strcmp(__progname, "gate-ftp") == 0)
240 gatemode = 1;
241
242 gateserver = getenv("FTPSERVER");
243 if (gateserver == NULL || *gateserver == '\0')
244 gateserver = GATE_SERVER;
245 if (gatemode) {
246 if (*gateserver == '\0') {
247 warnx(
248 "Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp");
249 gatemode = 0;
250 }
251 }
252
253 cp = getenv("TERM");
254 if (cp == NULL || strcmp(cp, "dumb") == 0)
255 dumbterm = 1;
256 else
257 dumbterm = 0;
258 fromatty = isatty(fileno(stdin));
259 ttyout = stdout;
260 if (isatty(fileno(ttyout))) {
261 verbose = 1; /* verbose if to a tty */
262 if (! dumbterm) {
263 #ifndef NO_EDITCOMPLETE
264 if (fromatty) /* editing mode on if tty is usable */
265 editing = 1;
266 #endif
267 #ifndef NO_PROGRESS
268 if (foregroundproc())
269 progress = 1; /* progress bar on if fg */
270 #endif
271 }
272 }
273
274 while ((ch = getopt(argc, argv, "Aadefgino:pP:r:RtT:u:vV")) != -1) {
275 switch (ch) {
276 case 'A':
277 activefallback = 0;
278 passivemode = 0;
279 break;
280
281 case 'a':
282 anonftp = 1;
283 break;
284
285 case 'd':
286 options |= SO_DEBUG;
287 debug++;
288 break;
289
290 case 'e':
291 #ifndef NO_EDITCOMPLETE
292 editing = 0;
293 #endif
294 break;
295
296 case 'f':
297 flushcache = 1;
298 break;
299
300 case 'g':
301 doglob = 0;
302 break;
303
304 case 'i':
305 interactive = 0;
306 break;
307
308 case 'n':
309 autologin = 0;
310 break;
311
312 case 'o':
313 outfile = optarg;
314 if (strcmp(outfile, "-") == 0)
315 ttyout = stderr;
316 break;
317
318 case 'p':
319 passivemode = 1;
320 activefallback = 0;
321 break;
322
323 case 'P':
324 ftpport = optarg;
325 break;
326
327 case 'r':
328 retry_connect = strtol(optarg, &ep, 10);
329 if (retry_connect < 1 || *ep != '\0')
330 errx(1, "bad retry value: %s", optarg);
331 break;
332
333 case 'R':
334 restartautofetch = 1;
335 break;
336
337 case 't':
338 trace = 1;
339 break;
340
341 case 'T':
342 {
343 int targc;
344 char *targv[6], *oac;
345
346 /* look for `dir,max[,incr]' */
347 targc = 0;
348 targv[targc++] = "-T";
349 oac = xstrdup(optarg);
350
351 while ((cp = strsep(&oac, ",")) != NULL) {
352 if (*cp == '\0') {
353 warnx("bad throttle value: %s", optarg);
354 usage();
355 /* NOTREACHED */
356 }
357 targv[targc++] = cp;
358 if (targc >= 5)
359 break;
360 }
361 if (parserate(targc, targv, 1) == -1)
362 usage();
363 free(oac);
364 break;
365 }
366
367 case 'u':
368 {
369 isupload = 1;
370 interactive = 0;
371 upload_path = xstrdup(optarg);
372
373 break;
374 }
375
376 case 'v':
377 progress = verbose = 1;
378 break;
379
380 case 'V':
381 progress = verbose = 0;
382 break;
383
384 default:
385 usage();
386 }
387 }
388 /* set line buffering on ttyout */
389 setvbuf(ttyout, NULL, _IOLBF, 0);
390 argc -= optind;
391 argv += optind;
392
393 cpend = 0; /* no pending replies */
394 proxy = 0; /* proxy not active */
395 crflag = 1; /* strip c.r. on ascii gets */
396 sendport = -1; /* not using ports */
397 /*
398 * Set up the home directory in case we're globbing.
399 */
400 cp = getlogin();
401 if (cp != NULL)
402 pw = getpwnam(cp);
403 if (pw == NULL)
404 pw = getpwuid(getuid());
405 if (pw != NULL) {
406 (void)strlcpy(home, pw->pw_dir, sizeof(home));
407 anonuser = pw->pw_name;
408 } else {
409 (void)strlcpy(home, "/", sizeof(home));
410 anonuser = "anonymous";
411 }
412
413 /*
414 * Every anonymous FTP server I've encountered will accept the
415 * string "username@", and will append the hostname itself. We
416 * do this by default since many servers are picky about not
417 * having a FQDN in the anonymous password.
418 * - thorpej (at) netbsd.org
419 */
420 len = strlen(anonuser) + 2;
421 anonpass = xmalloc(len);
422 (void)strlcpy(anonpass, anonuser, len);
423 (void)strlcat(anonpass, "@", len);
424
425 /*
426 * set all the defaults for options defined in
427 * struct option optiontab[] declared in cmdtab.c
428 */
429 setupoption("anonpass", getenv("FTPANONPASS"), anonpass);
430 setupoption("ftp_proxy", getenv(FTP_PROXY), "");
431 setupoption("http_proxy", getenv(HTTP_PROXY), "");
432 setupoption("no_proxy", getenv(NO_PROXY), "");
433 setupoption("pager", getenv("PAGER"), DEFAULTPAGER);
434 setupoption("prompt", getenv("FTPPROMPT"), DEFAULTPROMPT);
435 setupoption("rprompt", getenv("FTPRPROMPT"), DEFAULTRPROMPT);
436
437 free(anonpass);
438
439 setttywidth(0);
440 #ifdef SIGINFO
441 (void)xsignal(SIGINFO, psummary);
442 #endif
443 (void)xsignal(SIGQUIT, psummary);
444 (void)xsignal(SIGUSR1, crankrate);
445 (void)xsignal(SIGUSR2, crankrate);
446 (void)xsignal(SIGWINCH, setttywidth);
447
448 #ifdef __GNUC__ /* to shut up gcc warnings */
449 (void)&argc;
450 (void)&argv;
451 #endif
452
453 if (argc > 0) {
454 if (isupload) {
455 rval = auto_put(argc, argv, upload_path);
456 exit(rval);
457 } else if (strchr(argv[0], ':') != NULL
458 && ! isipv6addr(argv[0])) {
459 rval = auto_fetch(argc, argv);
460 if (rval >= 0) /* -1 == connected and cd-ed */
461 exit(rval);
462 } else {
463 char *xargv[4], *user, *host;
464
465 if (sigsetjmp(toplevel, 1))
466 exit(0);
467 (void)xsignal(SIGINT, intr);
468 (void)xsignal(SIGPIPE, lostpeer);
469 user = NULL;
470 host = argv[0];
471 cp = strchr(host, '@');
472 if (cp) {
473 *cp = '\0';
474 user = host;
475 host = cp + 1;
476 }
477 xargv[0] = __progname;
478 xargv[1] = host;
479 xargv[2] = argv[1];
480 xargv[3] = NULL;
481 do {
482 int oautologin;
483
484 oautologin = autologin;
485 if (user != NULL) {
486 anonftp = 0;
487 autologin = 0;
488 }
489 setpeer(argc+1, xargv);
490 autologin = oautologin;
491 if (connected == 1 && user != NULL)
492 (void)ftp_login(host, user, NULL);
493 if (!retry_connect)
494 break;
495 if (!connected) {
496 macnum = 0;
497 fprintf(ttyout,
498 "Retrying in %d seconds...\n",
499 retry_connect);
500 sleep(retry_connect);
501 }
502 } while (!connected);
503 retry_connect = 0; /* connected, stop hiding msgs */
504 }
505 }
506 if (isupload)
507 usage();
508
509 #ifndef NO_EDITCOMPLETE
510 controlediting();
511 #endif /* !NO_EDITCOMPLETE */
512
513 (void)sigsetjmp(toplevel, 1);
514 (void)xsignal(SIGINT, intr);
515 (void)xsignal(SIGPIPE, lostpeer);
516 for (;;)
517 cmdscanner();
518 }
519
520 /*
521 * Generate a prompt
522 */
523 char *
524 prompt(void)
525 {
526 static char **prompt;
527 static char buf[MAXPATHLEN];
528
529 if (prompt == NULL) {
530 struct option *o;
531
532 o = getoption("prompt");
533 if (o == NULL)
534 errx(1, "no such option `prompt'");
535 prompt = &(o->value);
536 }
537 formatbuf(buf, sizeof(buf), *prompt ? *prompt : DEFAULTPROMPT);
538 return (buf);
539 }
540
541 /*
542 * Generate an rprompt
543 */
544 char *
545 rprompt(void)
546 {
547 static char **rprompt;
548 static char buf[MAXPATHLEN];
549
550 if (rprompt == NULL) {
551 struct option *o;
552
553 o = getoption("rprompt");
554 if (o == NULL)
555 errx(1, "no such option `rprompt'");
556 rprompt = &(o->value);
557 }
558 formatbuf(buf, sizeof(buf), *rprompt ? *rprompt : DEFAULTRPROMPT);
559 return (buf);
560 }
561
562 /*
563 * Command parser.
564 */
565 void
566 cmdscanner(void)
567 {
568 struct cmd *c;
569 char *p;
570 int num;
571
572 for (;;) {
573 #ifndef NO_EDITCOMPLETE
574 if (!editing) {
575 #endif /* !NO_EDITCOMPLETE */
576 if (fromatty) {
577 fputs(prompt(), ttyout);
578 p = rprompt();
579 if (*p)
580 fprintf(ttyout, "%s ", p);
581 (void)fflush(ttyout);
582 }
583 if (fgets(line, sizeof(line), stdin) == NULL) {
584 if (fromatty)
585 putc('\n', ttyout);
586 quit(0, NULL);
587 }
588 num = strlen(line);
589 if (num == 0)
590 break;
591 if (line[--num] == '\n') {
592 if (num == 0)
593 break;
594 line[num] = '\0';
595 } else if (num == sizeof(line) - 2) {
596 fputs("sorry, input line too long.\n", ttyout);
597 while ((num = getchar()) != '\n' && num != EOF)
598 /* void */;
599 break;
600 } /* else it was a line without a newline */
601 #ifndef NO_EDITCOMPLETE
602 } else {
603 const char *buf;
604 HistEvent ev;
605 cursor_pos = NULL;
606
607 if ((buf = el_gets(el, &num)) == NULL || num == 0) {
608 if (fromatty)
609 putc('\n', ttyout);
610 quit(0, NULL);
611 }
612 if (buf[--num] == '\n') {
613 if (num == 0)
614 break;
615 } else if (num >= sizeof(line)) {
616 fputs("sorry, input line too long.\n", ttyout);
617 break;
618 }
619 memcpy(line, buf, num);
620 line[num] = '\0';
621 history(hist, &ev, H_ENTER, buf);
622 }
623 #endif /* !NO_EDITCOMPLETE */
624
625 makeargv();
626 if (margc == 0)
627 continue;
628 c = getcmd(margv[0]);
629 if (c == (struct cmd *)-1) {
630 fputs("?Ambiguous command.\n", ttyout);
631 continue;
632 }
633 if (c == NULL) {
634 #if !defined(NO_EDITCOMPLETE)
635 /*
636 * attempt to el_parse() unknown commands.
637 * any command containing a ':' would be parsed
638 * as "[prog:]cmd ...", and will result in a
639 * false positive if prog != "ftp", so treat
640 * such commands as invalid.
641 */
642 if (strchr(margv[0], ':') != NULL ||
643 el_parse(el, margc, margv) != 0)
644 #endif /* !NO_EDITCOMPLETE */
645 fputs("?Invalid command.\n", ttyout);
646 continue;
647 }
648 if (c->c_conn && !connected) {
649 fputs("Not connected.\n", ttyout);
650 continue;
651 }
652 confirmrest = 0;
653 (*c->c_handler)(margc, margv);
654 if (bell && c->c_bell)
655 (void)putc('\007', ttyout);
656 if (c->c_handler != help)
657 break;
658 }
659 (void)xsignal(SIGINT, intr);
660 (void)xsignal(SIGPIPE, lostpeer);
661 }
662
663 struct cmd *
664 getcmd(const char *name)
665 {
666 const char *p, *q;
667 struct cmd *c, *found;
668 int nmatches, longest;
669
670 if (name == NULL)
671 return (0);
672
673 longest = 0;
674 nmatches = 0;
675 found = 0;
676 for (c = cmdtab; (p = c->c_name) != NULL; c++) {
677 for (q = name; *q == *p++; q++)
678 if (*q == 0) /* exact match? */
679 return (c);
680 if (!*q) { /* the name was a prefix */
681 if (q - name > longest) {
682 longest = q - name;
683 nmatches = 1;
684 found = c;
685 } else if (q - name == longest)
686 nmatches++;
687 }
688 }
689 if (nmatches > 1)
690 return ((struct cmd *)-1);
691 return (found);
692 }
693
694 /*
695 * Slice a string up into argc/argv.
696 */
697
698 int slrflag;
699
700 void
701 makeargv(void)
702 {
703 char *argp;
704
705 stringbase = line; /* scan from first of buffer */
706 argbase = argbuf; /* store from first of buffer */
707 slrflag = 0;
708 marg_sl->sl_cur = 0; /* reset to start of marg_sl */
709 for (margc = 0; ; margc++) {
710 argp = slurpstring();
711 xsl_add(marg_sl, argp);
712 if (argp == NULL)
713 break;
714 }
715 #ifndef NO_EDITCOMPLETE
716 if (cursor_pos == line) {
717 cursor_argc = 0;
718 cursor_argo = 0;
719 } else if (cursor_pos != NULL) {
720 cursor_argc = margc;
721 cursor_argo = strlen(margv[margc-1]);
722 }
723 #endif /* !NO_EDITCOMPLETE */
724 }
725
726 #ifdef NO_EDITCOMPLETE
727 #define INC_CHKCURSOR(x) (x)++
728 #else /* !NO_EDITCOMPLETE */
729 #define INC_CHKCURSOR(x) { (x)++ ; \
730 if (x == cursor_pos) { \
731 cursor_argc = margc; \
732 cursor_argo = ap-argbase; \
733 cursor_pos = NULL; \
734 } }
735
736 #endif /* !NO_EDITCOMPLETE */
737
738 /*
739 * Parse string into argbuf;
740 * implemented with FSM to
741 * handle quoting and strings
742 */
743 char *
744 slurpstring(void)
745 {
746 int got_one = 0;
747 char *sb = stringbase;
748 char *ap = argbase;
749 char *tmp = argbase; /* will return this if token found */
750
751 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
752 switch (slrflag) { /* and $ as token for macro invoke */
753 case 0:
754 slrflag++;
755 INC_CHKCURSOR(stringbase);
756 return ((*sb == '!') ? "!" : "$");
757 /* NOTREACHED */
758 case 1:
759 slrflag++;
760 altarg = stringbase;
761 break;
762 default:
763 break;
764 }
765 }
766
767 S0:
768 switch (*sb) {
769
770 case '\0':
771 goto OUT;
772
773 case ' ':
774 case '\t':
775 INC_CHKCURSOR(sb);
776 goto S0;
777
778 default:
779 switch (slrflag) {
780 case 0:
781 slrflag++;
782 break;
783 case 1:
784 slrflag++;
785 altarg = sb;
786 break;
787 default:
788 break;
789 }
790 goto S1;
791 }
792
793 S1:
794 switch (*sb) {
795
796 case ' ':
797 case '\t':
798 case '\0':
799 goto OUT; /* end of token */
800
801 case '\\':
802 INC_CHKCURSOR(sb);
803 goto S2; /* slurp next character */
804
805 case '"':
806 INC_CHKCURSOR(sb);
807 goto S3; /* slurp quoted string */
808
809 default:
810 *ap = *sb; /* add character to token */
811 ap++;
812 INC_CHKCURSOR(sb);
813 got_one = 1;
814 goto S1;
815 }
816
817 S2:
818 switch (*sb) {
819
820 case '\0':
821 goto OUT;
822
823 default:
824 *ap = *sb;
825 ap++;
826 INC_CHKCURSOR(sb);
827 got_one = 1;
828 goto S1;
829 }
830
831 S3:
832 switch (*sb) {
833
834 case '\0':
835 goto OUT;
836
837 case '"':
838 INC_CHKCURSOR(sb);
839 goto S1;
840
841 default:
842 *ap = *sb;
843 ap++;
844 INC_CHKCURSOR(sb);
845 got_one = 1;
846 goto S3;
847 }
848
849 OUT:
850 if (got_one)
851 *ap++ = '\0';
852 argbase = ap; /* update storage pointer */
853 stringbase = sb; /* update scan pointer */
854 if (got_one) {
855 return (tmp);
856 }
857 switch (slrflag) {
858 case 0:
859 slrflag++;
860 break;
861 case 1:
862 slrflag++;
863 altarg = NULL;
864 break;
865 default:
866 break;
867 }
868 return (NULL);
869 }
870
871 /*
872 * Help/usage command.
873 * Call each command handler with argc == 0 and argv[0] == name.
874 */
875 void
876 help(int argc, char *argv[])
877 {
878 struct cmd *c;
879 char *nargv[1], *p, *cmd;
880 int isusage;
881
882 cmd = argv[0];
883 isusage = (strcmp(cmd, "usage") == 0);
884 if (argc == 0 || (isusage && argc == 1)) {
885 fprintf(ttyout, "usage: %s [command [...]]\n", cmd);
886 return;
887 }
888 if (argc == 1) {
889 StringList *buf;
890
891 buf = xsl_init();
892 fprintf(ttyout,
893 "%sommands may be abbreviated. Commands are:\n\n",
894 proxy ? "Proxy c" : "C");
895 for (c = cmdtab; (p = c->c_name) != NULL; c++)
896 if (!proxy || c->c_proxy)
897 xsl_add(buf, p);
898 list_vertical(buf);
899 sl_free(buf, 0);
900 return;
901 }
902
903 #define HELPINDENT ((int) sizeof("disconnect"))
904
905 while (--argc > 0) {
906 char *arg;
907
908 arg = *++argv;
909 c = getcmd(arg);
910 if (c == (struct cmd *)-1)
911 fprintf(ttyout, "?Ambiguous %s command `%s'\n",
912 cmd, arg);
913 else if (c == NULL)
914 fprintf(ttyout, "?Invalid %s command `%s'\n",
915 cmd, arg);
916 else {
917 if (isusage) {
918 nargv[0] = arg;
919 (*c->c_handler)(0, nargv);
920 } else
921 fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
922 c->c_name, c->c_help);
923 }
924 }
925 }
926
927 struct option *
928 getoption(const char *name)
929 {
930 const char *p;
931 struct option *c;
932
933 if (name == NULL)
934 return (NULL);
935 for (c = optiontab; (p = c->name) != NULL; c++) {
936 if (strcasecmp(p, name) == 0)
937 return (c);
938 }
939 return (NULL);
940 }
941
942 char *
943 getoptionvalue(const char *name)
944 {
945 struct option *c;
946
947 if (name == NULL)
948 errx(1, "getoptionvalue() invoked with NULL name");
949 c = getoption(name);
950 if (c != NULL)
951 return (c->value);
952 errx(1, "getoptionvalue() invoked with unknown option `%s'", name);
953 /* NOTREACHED */
954 }
955
956 static void
957 setupoption(char *name, char *value, char *defaultvalue)
958 {
959 char *nargv[3];
960 int overbose;
961
962 nargv[0] = "setupoption()";
963 nargv[1] = name;
964 nargv[2] = (value ? value : defaultvalue);
965 overbose = verbose;
966 verbose = 0;
967 setoption(3, nargv);
968 verbose = overbose;
969 }
970
971 void
972 usage(void)
973 {
974 (void)fprintf(stderr,
975 "usage: %s [-AadefginpRtvV] [-o outfile] [-P port] [-r retry]\n"
976 " [-T dir,max[,inc][[user@]host [port]]] [host:path[/]]\n"
977 " [file:///file] [ftp://[user[:pass]@]host[:port]/path[/]]\n"
978 " [http://[user[:pass]@]host[:port]/path] [...]\n"
979 " %s -u url file [...]\n", __progname, __progname);
980 exit(1);
981 }
982