main.c revision 1.22 1 /* $NetBSD: main.c,v 1.22 1997/06/10 07:04:43 lukem Exp $ */
2
3 /*
4 * Copyright (c) 1985, 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #ifndef lint
37 static char copyright[] =
38 "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
39 The Regents of the University of California. All rights reserved.\n";
40 #endif /* not lint */
41
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94";
45 #else
46 static char rcsid[] = "$NetBSD: main.c,v 1.22 1997/06/10 07:04:43 lukem Exp $";
47 #endif
48 #endif /* not lint */
49
50 /*
51 * FTP User Program -- Command Interface.
52 */
53 #include <sys/types.h>
54 #include <sys/socket.h>
55
56 #include <err.h>
57 #include <netdb.h>
58 #include <pwd.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63
64 #include "ftp_var.h"
65
66 int
67 main(argc, argv)
68 int argc;
69 char *argv[];
70 {
71 struct servent *sp;
72 int ch, top, port, rval;
73 struct passwd *pw = NULL;
74 char *cp, homedir[MAXPATHLEN];
75 int dumbterm;
76
77 sp = getservbyname("ftp", "tcp");
78 if (sp == 0)
79 ftpport = htons(FTP_PORT); /* good fallback */
80 else
81 ftpport = sp->s_port;
82 sp = getservbyname("http", "tcp");
83 if (sp == 0)
84 httpport = htons(HTTP_PORT); /* good fallback */
85 else
86 httpport = sp->s_port;
87 doglob = 1;
88 interactive = 1;
89 autologin = 1;
90 passivemode = 0;
91 preserve = 1;
92 verbose = 0;
93 progress = 0;
94 #ifndef SMALL
95 editing = 0;
96 el = NULL;
97 hist = NULL;
98 #endif
99 mark = HASHBYTES;
100 marg_sl = sl_init();
101
102 cp = strrchr(argv[0], '/');
103 cp = (cp == NULL) ? argv[0] : cp + 1;
104 if (strcmp(cp, "pftp") == 0)
105 passivemode = 1;
106
107 cp = getenv("TERM");
108 if (cp == NULL || strcmp(cp, "dumb") == 0)
109 dumbterm = 1;
110 else
111 dumbterm = 0;
112 fromatty = isatty(fileno(stdin));
113 if (fromatty) {
114 verbose = 1; /* verbose if from a tty */
115 #ifndef SMALL
116 if (! dumbterm)
117 editing = 1; /* editing mode on if tty is usable */
118 #endif
119 }
120 if (isatty(fileno(stdout)) && !dumbterm)
121 progress = 1; /* progress bar on if tty is usable */
122
123 while ((ch = getopt(argc, argv, "adeginpP:tvV")) != -1) {
124 switch (ch) {
125 case 'a':
126 anonftp = 1;
127 break;
128
129 case 'd':
130 options |= SO_DEBUG;
131 debug++;
132 break;
133
134 case 'e':
135 #ifndef SMALL
136 editing = 0;
137 #endif
138 break;
139
140 case 'g':
141 doglob = 0;
142 break;
143
144 case 'i':
145 interactive = 0;
146 break;
147
148 case 'n':
149 autologin = 0;
150 break;
151
152 case 'p':
153 passivemode = 1;
154 break;
155
156 case 'P':
157 port = atoi(optarg);
158 if (port <= 0)
159 warnx("bad port number: %s (ignored)", optarg);
160 else
161 ftpport = htons(port);
162 break;
163
164 case 't':
165 trace = 1;
166 break;
167
168 case 'v':
169 verbose = 1;
170 break;
171
172 case 'V':
173 verbose = 0;
174 break;
175
176 default:
177 usage();
178 }
179 }
180 argc -= optind;
181 argv += optind;
182
183 cpend = 0; /* no pending replies */
184 proxy = 0; /* proxy not active */
185 crflag = 1; /* strip c.r. on ascii gets */
186 sendport = -1; /* not using ports */
187 /*
188 * Set up the home directory in case we're globbing.
189 */
190 cp = getlogin();
191 if (cp != NULL) {
192 pw = getpwnam(cp);
193 }
194 if (pw == NULL)
195 pw = getpwuid(getuid());
196 if (pw != NULL) {
197 home = homedir;
198 (void)strcpy(home, pw->pw_dir);
199 }
200
201 setttywidth(0);
202 (void)signal(SIGWINCH, setttywidth);
203
204 if (argc > 0) {
205 if (strchr(argv[0], ':') != NULL) {
206 anonftp = 1; /* Handle "automatic" transfers. */
207 rval = auto_fetch(argc, argv);
208 if (rval >= 0) /* -1 == connected and cd-ed */
209 exit(rval);
210 } else {
211 char *xargv[5];
212
213 if (setjmp(toplevel))
214 exit(0);
215 (void)signal(SIGINT, (sig_t)intr);
216 (void)signal(SIGPIPE, (sig_t)lostpeer);
217 xargv[0] = __progname;
218 xargv[1] = argv[0];
219 xargv[2] = argv[1];
220 xargv[3] = argv[2];
221 xargv[4] = NULL;
222 setpeer(argc+1, xargv);
223 }
224 }
225 #ifndef SMALL
226 controlediting();
227 #endif /* !SMALL */
228 top = setjmp(toplevel) == 0;
229 if (top) {
230 (void)signal(SIGINT, (sig_t)intr);
231 (void)signal(SIGPIPE, (sig_t)lostpeer);
232 }
233 for (;;) {
234 cmdscanner(top);
235 top = 1;
236 }
237 }
238
239 void
240 intr()
241 {
242
243 alarmtimer(0);
244 longjmp(toplevel, 1);
245 }
246
247 void
248 lostpeer()
249 {
250
251 alarmtimer(0);
252 if (connected) {
253 if (cout != NULL) {
254 (void)shutdown(fileno(cout), 1+1);
255 (void)fclose(cout);
256 cout = NULL;
257 }
258 if (data >= 0) {
259 (void)shutdown(data, 1+1);
260 (void)close(data);
261 data = -1;
262 }
263 connected = 0;
264 }
265 pswitch(1);
266 if (connected) {
267 if (cout != NULL) {
268 (void)shutdown(fileno(cout), 1+1);
269 (void)fclose(cout);
270 cout = NULL;
271 }
272 connected = 0;
273 }
274 proxflag = 0;
275 pswitch(0);
276 }
277
278 /*
279 * Generate a prompt
280 */
281 char *
282 prompt()
283 {
284 return ("ftp> ");
285 }
286
287 /*
288 * Command parser.
289 */
290 void
291 cmdscanner(top)
292 int top;
293 {
294 struct cmd *c;
295 int num;
296
297 if (!top
298 #ifndef SMALL
299 && !editing
300 #endif /* !SMALL */
301 )
302 (void)putchar('\n');
303 for (;;) {
304 #ifndef SMALL
305 if (!editing) {
306 #endif /* !SMALL */
307 if (fromatty) {
308 fputs(prompt(), stdout);
309 (void)fflush(stdout);
310 }
311 if (fgets(line, sizeof(line), stdin) == NULL)
312 quit(0, 0);
313 num = strlen(line);
314 if (num == 0)
315 break;
316 if (line[--num] == '\n') {
317 if (num == 0)
318 break;
319 line[num] = '\0';
320 } else if (num == sizeof(line) - 2) {
321 puts("sorry, input line too long.");
322 while ((num = getchar()) != '\n' && num != EOF)
323 /* void */;
324 break;
325 } /* else it was a line without a newline */
326 #ifndef SMALL
327 } else {
328 const char *buf;
329 cursor_pos = NULL;
330
331 if ((buf = el_gets(el, &num)) == NULL || num == 0)
332 quit(0, 0);
333 if (line[--num] == '\n') {
334 if (num == 0)
335 break;
336 } else if (num >= sizeof(line)) {
337 puts("sorry, input line too long.");
338 break;
339 }
340 memcpy(line, buf, num);
341 line[num] = '\0';
342 history(hist, H_ENTER, buf);
343 }
344 #endif /* !SMALL */
345
346 makeargv();
347 if (margc == 0)
348 continue;
349 #if 0 && !defined(SMALL) /* XXX: don't want el_parse */
350 /*
351 * el_parse returns -1 to signal that it's not been handled
352 * internally.
353 */
354 if (el_parse(el, margc, margv) != -1)
355 continue;
356 #endif /* !SMALL */
357 c = getcmd(margv[0]);
358 if (c == (struct cmd *)-1) {
359 puts("?Ambiguous command.");
360 continue;
361 }
362 if (c == 0) {
363 puts("?Invalid command.");
364 continue;
365 }
366 if (c->c_conn && !connected) {
367 puts("Not connected.");
368 continue;
369 }
370 confirmrest = 0;
371 (*c->c_handler)(margc, margv);
372 if (bell && c->c_bell)
373 (void)putchar('\007');
374 if (c->c_handler != help)
375 break;
376 }
377 (void)signal(SIGINT, (sig_t)intr);
378 (void)signal(SIGPIPE, (sig_t)lostpeer);
379 }
380
381 struct cmd *
382 getcmd(name)
383 const char *name;
384 {
385 const char *p, *q;
386 struct cmd *c, *found;
387 int nmatches, longest;
388
389 if (name == NULL)
390 return (0);
391
392 longest = 0;
393 nmatches = 0;
394 found = 0;
395 for (c = cmdtab; (p = c->c_name) != NULL; c++) {
396 for (q = name; *q == *p++; q++)
397 if (*q == 0) /* exact match? */
398 return (c);
399 if (!*q) { /* the name was a prefix */
400 if (q - name > longest) {
401 longest = q - name;
402 nmatches = 1;
403 found = c;
404 } else if (q - name == longest)
405 nmatches++;
406 }
407 }
408 if (nmatches > 1)
409 return ((struct cmd *)-1);
410 return (found);
411 }
412
413 /*
414 * Slice a string up into argc/argv.
415 */
416
417 int slrflag;
418
419 void
420 makeargv()
421 {
422 char *argp;
423
424 stringbase = line; /* scan from first of buffer */
425 argbase = argbuf; /* store from first of buffer */
426 slrflag = 0;
427 marg_sl->sl_cur = 0; /* reset to start of marg_sl */
428 for (margc = 0; ; margc++) {
429 argp = slurpstring();
430 sl_add(marg_sl, argp);
431 if (argp == NULL)
432 break;
433 }
434 #ifndef SMALL
435 if (cursor_pos == line) {
436 cursor_argc = 0;
437 cursor_argo = 0;
438 } else if (cursor_pos != NULL) {
439 cursor_argc = margc;
440 cursor_argo = strlen(margv[margc-1]);
441 }
442 #endif /* !SMALL */
443 }
444
445 #ifdef SMALL
446 #define INC_CHKCURSOR(x) (x)++
447 #else /* !SMALL */
448 #define INC_CHKCURSOR(x) { (x)++ ; \
449 if (x == cursor_pos) { \
450 cursor_argc = margc; \
451 cursor_argo = ap-argbase; \
452 cursor_pos = NULL; \
453 } }
454
455 #endif /* !SMALL */
456
457 /*
458 * Parse string into argbuf;
459 * implemented with FSM to
460 * handle quoting and strings
461 */
462 char *
463 slurpstring()
464 {
465 int got_one = 0;
466 char *sb = stringbase;
467 char *ap = argbase;
468 char *tmp = argbase; /* will return this if token found */
469
470 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
471 switch (slrflag) { /* and $ as token for macro invoke */
472 case 0:
473 slrflag++;
474 INC_CHKCURSOR(stringbase);
475 return ((*sb == '!') ? "!" : "$");
476 /* NOTREACHED */
477 case 1:
478 slrflag++;
479 altarg = stringbase;
480 break;
481 default:
482 break;
483 }
484 }
485
486 S0:
487 switch (*sb) {
488
489 case '\0':
490 goto OUT;
491
492 case ' ':
493 case '\t':
494 INC_CHKCURSOR(sb);
495 goto S0;
496
497 default:
498 switch (slrflag) {
499 case 0:
500 slrflag++;
501 break;
502 case 1:
503 slrflag++;
504 altarg = sb;
505 break;
506 default:
507 break;
508 }
509 goto S1;
510 }
511
512 S1:
513 switch (*sb) {
514
515 case ' ':
516 case '\t':
517 case '\0':
518 goto OUT; /* end of token */
519
520 case '\\':
521 INC_CHKCURSOR(sb);
522 goto S2; /* slurp next character */
523
524 case '"':
525 INC_CHKCURSOR(sb);
526 goto S3; /* slurp quoted string */
527
528 default:
529 *ap = *sb; /* add character to token */
530 ap++;
531 INC_CHKCURSOR(sb);
532 got_one = 1;
533 goto S1;
534 }
535
536 S2:
537 switch (*sb) {
538
539 case '\0':
540 goto OUT;
541
542 default:
543 *ap = *sb;
544 ap++;
545 INC_CHKCURSOR(sb);
546 got_one = 1;
547 goto S1;
548 }
549
550 S3:
551 switch (*sb) {
552
553 case '\0':
554 goto OUT;
555
556 case '"':
557 INC_CHKCURSOR(sb);
558 goto S1;
559
560 default:
561 *ap = *sb;
562 ap++;
563 INC_CHKCURSOR(sb);
564 got_one = 1;
565 goto S3;
566 }
567
568 OUT:
569 if (got_one)
570 *ap++ = '\0';
571 argbase = ap; /* update storage pointer */
572 stringbase = sb; /* update scan pointer */
573 if (got_one) {
574 return (tmp);
575 }
576 switch (slrflag) {
577 case 0:
578 slrflag++;
579 break;
580 case 1:
581 slrflag++;
582 altarg = (char *) 0;
583 break;
584 default:
585 break;
586 }
587 return ((char *)0);
588 }
589
590 /*
591 * Help command.
592 * Call each command handler with argc == 0 and argv[0] == name.
593 */
594 void
595 help(argc, argv)
596 int argc;
597 char *argv[];
598 {
599 struct cmd *c;
600
601 if (argc == 1) {
602 StringList *buf;
603
604 buf = sl_init();
605 printf("%sommands may be abbreviated. Commands are:\n\n",
606 proxy ? "Proxy c" : "C");
607 for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
608 if (c->c_name && (!proxy || c->c_proxy))
609 sl_add(buf, c->c_name);
610 list_vertical(buf);
611 sl_free(buf, 0);
612 return;
613 }
614
615 #define HELPINDENT ((int) sizeof("disconnect"))
616
617 while (--argc > 0) {
618 char *arg;
619
620 arg = *++argv;
621 c = getcmd(arg);
622 if (c == (struct cmd *)-1)
623 printf("?Ambiguous help command %s\n", arg);
624 else if (c == (struct cmd *)0)
625 printf("?Invalid help command %s\n", arg);
626 else
627 printf("%-*s\t%s\n", HELPINDENT,
628 c->c_name, c->c_help);
629 }
630 }
631
632 void
633 usage()
634 {
635 (void)fprintf(stderr,
636 "usage: %s [-adeginptvV] [host [port]]\n"
637 " %s host:path[/]\n"
638 " %s ftp://host[:port]/path[/]\n"
639 " %s http://host[:port]/file\n",
640 __progname, __progname, __progname, __progname);
641 exit(1);
642 }
643