main.c revision 1.17 1 /* $NetBSD: main.c,v 1.17 1997/02/01 10:45:07 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.17 1997/02/01 10:45:07 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 <string.h>
61 #include <unistd.h>
62
63 #include "ftp_var.h"
64
65 int
66 main(argc, argv)
67 int argc;
68 char *argv[];
69 {
70 struct servent *sp;
71 int ch, top, port, rval;
72 struct passwd *pw = NULL;
73 char *cp, homedir[MAXPATHLEN];
74
75 sp = getservbyname("ftp", "tcp");
76 if (sp == 0)
77 ftpport = htons(FTP_PORT); /* good fallback */
78 else
79 ftpport = sp->s_port;
80 sp = getservbyname("http", "tcp");
81 if (sp == 0)
82 httpport = htons(HTTP_PORT); /* good fallback */
83 else
84 httpport = sp->s_port;
85 doglob = 1;
86 interactive = 1;
87 autologin = 1;
88 passivemode = 0;
89 preserve = 1;
90 verbose = 0;
91 progress = 0;
92 mark = HASHBYTES;
93 marg_sl = sl_init();
94
95 cp = strrchr(argv[0], '/');
96 cp = (cp == NULL) ? argv[0] : cp + 1;
97 if (strcmp(cp, "pftp") == 0)
98 passivemode = 1;
99
100 fromatty = isatty(fileno(stdin));
101 if (fromatty)
102 verbose = 1; /* verbose if from a tty */
103 if (isatty(fileno(stdout)))
104 progress = 1; /* progress bar on if going to a tty */
105
106 while ((ch = getopt(argc, argv, "adginpP:tvV")) != EOF) {
107 switch (ch) {
108 case 'a':
109 anonftp = 1;
110 break;
111
112 case 'd':
113 options |= SO_DEBUG;
114 debug++;
115 break;
116
117 case 'g':
118 doglob = 0;
119 break;
120
121 case 'i':
122 interactive = 0;
123 break;
124
125 case 'n':
126 autologin = 0;
127 break;
128
129 case 'p':
130 passivemode = 1;
131 break;
132
133 case 'P':
134 port = atoi(optarg);
135 if (port <= 0)
136 warnx("bad port number: %s", optarg);
137 else
138 ftpport = htons(port);
139 break;
140
141 case 't':
142 trace = 1;
143 break;
144
145 case 'v':
146 verbose = 1;
147 break;
148
149 case 'V':
150 verbose = 0;
151 break;
152
153 default:
154 usage();
155 }
156 }
157 argc -= optind;
158 argv += optind;
159
160 cpend = 0; /* no pending replies */
161 proxy = 0; /* proxy not active */
162 crflag = 1; /* strip c.r. on ascii gets */
163 sendport = -1; /* not using ports */
164 /*
165 * Set up the home directory in case we're globbing.
166 */
167 cp = getlogin();
168 if (cp != NULL) {
169 pw = getpwnam(cp);
170 }
171 if (pw == NULL)
172 pw = getpwuid(getuid());
173 if (pw != NULL) {
174 home = homedir;
175 (void) strcpy(home, pw->pw_dir);
176 }
177
178 #ifndef SMALLFTP
179 editing = 0; /* command line editing off */
180 if (fromatty) {
181 editing = 1; /* editing mode on if a tty */
182 el = el_init(__progname, stdin, stdout); /* init editline */
183
184 hist = history_init(); /* init the builtin history */
185 history(hist, H_EVENT, 100); /* remember 100 events */
186 el_set(el, EL_HIST, history, hist); /* use history */
187
188 el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
189 el_set(el, EL_PROMPT, prompt); /* set the prompt function */
190
191 /* add local file completion, bind to TAB */
192 el_set(el, EL_ADDFN, "ftp-complete",
193 "Context sensitive argument completion",
194 complete);
195 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
196
197 el_source(el, NULL); /* read ~/.editrc */
198 }
199 #endif /* !SMALLFTP */
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, intr);
216 (void) signal(SIGPIPE, 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 top = setjmp(toplevel) == 0;
226 if (top) {
227 (void) signal(SIGINT, intr);
228 (void) signal(SIGPIPE, lostpeer);
229 }
230 for (;;) {
231 cmdscanner(top);
232 top = 1;
233 }
234 }
235
236 void
237 intr()
238 {
239
240 alarmtimer(0);
241 longjmp(toplevel, 1);
242 }
243
244 void
245 lostpeer()
246 {
247
248 alarmtimer(0);
249 if (connected) {
250 if (cout != NULL) {
251 (void) shutdown(fileno(cout), 1+1);
252 (void) fclose(cout);
253 cout = NULL;
254 }
255 if (data >= 0) {
256 (void) shutdown(data, 1+1);
257 (void) close(data);
258 data = -1;
259 }
260 connected = 0;
261 }
262 pswitch(1);
263 if (connected) {
264 if (cout != NULL) {
265 (void) shutdown(fileno(cout), 1+1);
266 (void) fclose(cout);
267 cout = NULL;
268 }
269 connected = 0;
270 }
271 proxflag = 0;
272 pswitch(0);
273 }
274
275 /*
276 * Generate a prompt
277 */
278 char *
279 prompt()
280 {
281 return ("ftp> ");
282 }
283
284 /*
285 * Command parser.
286 */
287 void
288 cmdscanner(top)
289 int top;
290 {
291 struct cmd *c;
292 int num;
293
294 if (!top
295 #ifndef SMALLFTP
296 && !editing
297 #endif /* !SMALLFTP */
298 )
299 (void) putchar('\n');
300 for (;;) {
301 #ifndef SMALLFTP
302 if (!editing) {
303 #endif /* !SMALLFTP */
304 if (fromatty) {
305 printf("%s", prompt());
306 (void) fflush(stdout);
307 }
308 if (fgets(line, sizeof(line), stdin) == NULL)
309 quit(0, 0);
310 num = strlen(line);
311 if (num == 0)
312 break;
313 if (line[--num] == '\n') {
314 if (num == 0)
315 break;
316 line[num] = '\0';
317 } else if (num == sizeof(line) - 2) {
318 printf("sorry, input line too long\n");
319 while ((num = getchar()) != '\n' && num != EOF)
320 /* void */;
321 break;
322 } /* else it was a line without a newline */
323 #ifndef SMALLFTP
324 } else {
325 const char *buf;
326 cursor_pos = NULL;
327
328 if ((buf = el_gets(el, &num)) == NULL || num == 0)
329 quit(0, 0);
330 if (line[--num] == '\n') {
331 if (num == 0)
332 break;
333 } else if (num >= sizeof(line)) {
334 printf("sorry, input line too long\n");
335 break;
336 }
337 memcpy(line, buf, num);
338 line[num] = '\0';
339 history(hist, H_ENTER, buf);
340 }
341 #endif /* !SMALLFTP */
342
343 makeargv();
344 if (margc == 0)
345 continue;
346 #if 0 && !defined(SMALLFTP) /* XXX: don't want el_parse */
347 /*
348 * el_parse returns -1 to signal that it's not been handled
349 * internally.
350 */
351 if (el_parse(el, margc, margv) != -1)
352 continue;
353 #endif /* !SMALLFTP */
354 c = getcmd(margv[0]);
355 if (c == (struct cmd *)-1) {
356 printf("?Ambiguous command\n");
357 continue;
358 }
359 if (c == 0) {
360 printf("?Invalid command\n");
361 continue;
362 }
363 if (c->c_conn && !connected) {
364 printf("Not connected.\n");
365 continue;
366 }
367 confirmrest = 0;
368 (*c->c_handler)(margc, margv);
369 if (bell && c->c_bell)
370 (void) putchar('\007');
371 if (c->c_handler != help)
372 break;
373 }
374 (void) signal(SIGINT, intr);
375 (void) signal(SIGPIPE, lostpeer);
376 }
377
378 struct cmd *
379 getcmd(name)
380 const char *name;
381 {
382 const char *p, *q;
383 struct cmd *c, *found;
384 int nmatches, longest;
385
386 if (name == NULL)
387 return (0);
388
389 longest = 0;
390 nmatches = 0;
391 found = 0;
392 for (c = cmdtab; (p = c->c_name) != NULL; c++) {
393 for (q = name; *q == *p++; q++)
394 if (*q == 0) /* exact match? */
395 return (c);
396 if (!*q) { /* the name was a prefix */
397 if (q - name > longest) {
398 longest = q - name;
399 nmatches = 1;
400 found = c;
401 } else if (q - name == longest)
402 nmatches++;
403 }
404 }
405 if (nmatches > 1)
406 return ((struct cmd *)-1);
407 return (found);
408 }
409
410 /*
411 * Slice a string up into argc/argv.
412 */
413
414 int slrflag;
415
416 void
417 makeargv()
418 {
419 char *argp;
420
421 stringbase = line; /* scan from first of buffer */
422 argbase = argbuf; /* store from first of buffer */
423 slrflag = 0;
424 marg_sl->sl_cur = 0; /* reset to start of marg_sl */
425 for (margc = 0; ; margc++) {
426 argp = slurpstring();
427 sl_add(marg_sl, argp);
428 if (argp == NULL)
429 break;
430 }
431 #ifndef SMALLFTP
432 if (cursor_pos == line) {
433 cursor_argc = 0;
434 cursor_argo = 0;
435 } else if (cursor_pos != NULL) {
436 cursor_argc = margc;
437 cursor_argo = strlen(margv[margc-1]);
438 }
439 #endif /* !SMALLFTP */
440 }
441
442 #ifdef SMALLFTP
443 #define INC_CHKCURSOR(x) (x)++
444 #else /* !SMALLFTP */
445 #define INC_CHKCURSOR(x) { (x)++ ; \
446 if (x == cursor_pos) { \
447 cursor_argc = margc; \
448 cursor_argo = ap-argbase; \
449 cursor_pos = NULL; \
450 } }
451
452 #endif /* !SMALLFTP */
453
454 /*
455 * Parse string into argbuf;
456 * implemented with FSM to
457 * handle quoting and strings
458 */
459 char *
460 slurpstring()
461 {
462 int got_one = 0;
463 char *sb = stringbase;
464 char *ap = argbase;
465 char *tmp = argbase; /* will return this if token found */
466
467 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
468 switch (slrflag) { /* and $ as token for macro invoke */
469 case 0:
470 slrflag++;
471 INC_CHKCURSOR(stringbase);
472 return ((*sb == '!') ? "!" : "$");
473 /* NOTREACHED */
474 case 1:
475 slrflag++;
476 altarg = stringbase;
477 break;
478 default:
479 break;
480 }
481 }
482
483 S0:
484 switch (*sb) {
485
486 case '\0':
487 goto OUT;
488
489 case ' ':
490 case '\t':
491 INC_CHKCURSOR(sb);
492 goto S0;
493
494 default:
495 switch (slrflag) {
496 case 0:
497 slrflag++;
498 break;
499 case 1:
500 slrflag++;
501 altarg = sb;
502 break;
503 default:
504 break;
505 }
506 goto S1;
507 }
508
509 S1:
510 switch (*sb) {
511
512 case ' ':
513 case '\t':
514 case '\0':
515 goto OUT; /* end of token */
516
517 case '\\':
518 INC_CHKCURSOR(sb);
519 goto S2; /* slurp next character */
520
521 case '"':
522 INC_CHKCURSOR(sb);
523 goto S3; /* slurp quoted string */
524
525 default:
526 *ap = *sb; /* add character to token */
527 ap++;
528 INC_CHKCURSOR(sb);
529 got_one = 1;
530 goto S1;
531 }
532
533 S2:
534 switch (*sb) {
535
536 case '\0':
537 goto OUT;
538
539 default:
540 *ap = *sb;
541 ap++;
542 INC_CHKCURSOR(sb);
543 got_one = 1;
544 goto S1;
545 }
546
547 S3:
548 switch (*sb) {
549
550 case '\0':
551 goto OUT;
552
553 case '"':
554 INC_CHKCURSOR(sb);
555 goto S1;
556
557 default:
558 *ap = *sb;
559 ap++;
560 INC_CHKCURSOR(sb);
561 got_one = 1;
562 goto S3;
563 }
564
565 OUT:
566 if (got_one)
567 *ap++ = '\0';
568 argbase = ap; /* update storage pointer */
569 stringbase = sb; /* update scan pointer */
570 if (got_one) {
571 return (tmp);
572 }
573 switch (slrflag) {
574 case 0:
575 slrflag++;
576 break;
577 case 1:
578 slrflag++;
579 altarg = (char *) 0;
580 break;
581 default:
582 break;
583 }
584 return ((char *)0);
585 }
586
587 /*
588 * Help command.
589 * Call each command handler with argc == 0 and argv[0] == name.
590 */
591 void
592 help(argc, argv)
593 int argc;
594 char *argv[];
595 {
596 struct cmd *c;
597
598 if (argc == 1) {
599 StringList *buf;
600
601 buf = sl_init();
602 printf("%sommands may be abbreviated. Commands are:\n\n",
603 proxy ? "Proxy c" : "C");
604 for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
605 if (c->c_name && (!proxy || c->c_proxy))
606 sl_add(buf, c->c_name);
607 list_vertical(buf);
608 sl_free(buf, 0);
609 return;
610 }
611
612 #define HELPINDENT ((int) sizeof ("disconnect"))
613
614 while (--argc > 0) {
615 char *arg;
616
617 arg = *++argv;
618 c = getcmd(arg);
619 if (c == (struct cmd *)-1)
620 printf("?Ambiguous help command %s\n", arg);
621 else if (c == (struct cmd *)0)
622 printf("?Invalid help command %s\n", arg);
623 else
624 printf("%-*s\t%s\n", HELPINDENT,
625 c->c_name, c->c_help);
626 }
627 }
628
629 void
630 usage()
631 {
632 (void)fprintf(stderr,
633 "usage: %s [-adginptvV] [host [port]]\n"
634 " %s host:path[/]\n"
635 " %s ftp://host[:port]/path[/]\n"
636 " %s http://host[:port]/file\n",
637 __progname, __progname, __progname, __progname);
638 exit(1);
639 }
640