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