main.c revision 1.14 1 /* $NetBSD: main.c,v 1.14 1996/12/06 02:06:52 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.14 1996/12/06 02:06:52 lukem Exp $";
47 #endif
48 #endif /* not lint */
49
50 /*
51 * FTP User Program -- Command Interface.
52 */
53 /*#include <sys/ioctl.h>*/
54 #include <sys/types.h>
55 #include <sys/socket.h>
56
57 #include <arpa/ftp.h>
58
59 #include <ctype.h>
60 #include <err.h>
61 #include <netdb.h>
62 #include <pwd.h>
63 #include <signal.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <unistd.h>
68
69 #include "ftp_var.h"
70
71 extern char *__progname; /* from crt0.o */
72
73 char *portnum; /* alternate port number */
74
75 int auto_fetch __P((int, char **));
76
77 int
78 main(argc, argv)
79 int argc;
80 char *argv[];
81 {
82 int ch, top;
83 struct passwd *pw = NULL;
84 char *cp, homedir[MAXPATHLEN];
85
86 sp = getservbyname("ftp", "tcp");
87 if (sp == 0)
88 errx(1, "ftp/tcp: unknown service");
89 doglob = 1;
90 interactive = 1;
91 autologin = 1;
92 passivemode = 0;
93 preserve = 1;
94 mark = HASHBYTES;
95
96 while ((ch = getopt(argc, argv, "adginpP:tv")) != EOF) {
97 switch (ch) {
98 case 'a':
99 anonftp = 1;
100 break;
101
102 case 'd':
103 options |= SO_DEBUG;
104 debug++;
105 break;
106
107 case 'g':
108 doglob = 0;
109 break;
110
111 case 'i':
112 interactive = 0;
113 break;
114
115 case 'n':
116 autologin = 0;
117 break;
118
119 case 'p':
120 passivemode = 1;
121 break;
122
123 case 'P':
124 portnum = optarg;
125 break;
126
127 case 't':
128 trace++;
129 break;
130
131 case 'v':
132 verbose++;
133 break;
134
135 default:
136 usage();
137 }
138 }
139 argc -= optind;
140 argv += optind;
141
142 fromatty = isatty(fileno(stdin));
143 if (fromatty)
144 verbose++;
145 cpend = 0; /* no pending replies */
146 proxy = 0; /* proxy not active */
147 crflag = 1; /* strip c.r. on ascii gets */
148 sendport = -1; /* not using ports */
149 /*
150 * Set up the home directory in case we're globbing.
151 */
152 cp = getlogin();
153 if (cp != NULL) {
154 pw = getpwnam(cp);
155 }
156 if (pw == NULL)
157 pw = getpwuid(getuid());
158 if (pw != NULL) {
159 home = homedir;
160 (void) strcpy(home, pw->pw_dir);
161 }
162
163 /* Handle "automatic" transfers. */
164 if (argc > 0 && strchr(argv[0], ':') != NULL) {
165 anonftp = 1;
166 exit(auto_fetch(argc, argv));
167 }
168
169 if (argc > 0) {
170 char *xargv[5];
171
172 if (setjmp(toplevel))
173 exit(0);
174 (void) signal(SIGINT, intr);
175 (void) signal(SIGPIPE, lostpeer);
176 xargv[0] = __progname;
177 xargv[1] = argv[0];
178 xargv[2] = argv[1];
179 xargv[3] = argv[2];
180 xargv[4] = NULL;
181 setpeer(argc+1, xargv);
182 }
183 top = setjmp(toplevel) == 0;
184 if (top) {
185 (void) signal(SIGINT, intr);
186 (void) signal(SIGPIPE, lostpeer);
187 }
188 for (;;) {
189 cmdscanner(top);
190 top = 1;
191 }
192 }
193
194 void
195 intr()
196 {
197
198 longjmp(toplevel, 1);
199 }
200
201 void
202 lostpeer()
203 {
204
205 if (connected) {
206 if (cout != NULL) {
207 (void) shutdown(fileno(cout), 1+1);
208 (void) fclose(cout);
209 cout = NULL;
210 }
211 if (data >= 0) {
212 (void) shutdown(data, 1+1);
213 (void) close(data);
214 data = -1;
215 }
216 connected = 0;
217 }
218 pswitch(1);
219 if (connected) {
220 if (cout != NULL) {
221 (void) shutdown(fileno(cout), 1+1);
222 (void) fclose(cout);
223 cout = NULL;
224 }
225 connected = 0;
226 }
227 proxflag = 0;
228 pswitch(0);
229 }
230
231 /*
232 char *
233 tail(filename)
234 char *filename;
235 {
236 char *s;
237
238 while (*filename) {
239 s = strrchr(filename, '/');
240 if (s == NULL)
241 break;
242 if (s[1])
243 return (s + 1);
244 if (s == filename)
245 break; XXX
246 *s = '\0';
247 }
248 return (filename);
249 }
250 */
251
252 /*
253 * Command parser.
254 */
255 void
256 cmdscanner(top)
257 int top;
258 {
259 struct cmd *c;
260 int l;
261
262 if (!top)
263 (void) putchar('\n');
264 for (;;) {
265 if (fromatty) {
266 printf("ftp> ");
267 (void) fflush(stdout);
268 }
269 if (fgets(line, sizeof line, stdin) == NULL)
270 quit(0, 0);
271 l = strlen(line);
272 if (l == 0)
273 break;
274 if (line[--l] == '\n') {
275 if (l == 0)
276 break;
277 line[l] = '\0';
278 } else if (l == sizeof(line) - 2) {
279 printf("sorry, input line too long\n");
280 while ((l = getchar()) != '\n' && l != EOF)
281 /* void */;
282 break;
283 } /* else it was a line without a newline */
284 makeargv();
285 if (margc == 0) {
286 continue;
287 }
288 c = getcmd(margv[0]);
289 if (c == (struct cmd *)-1) {
290 printf("?Ambiguous command\n");
291 continue;
292 }
293 if (c == 0) {
294 printf("?Invalid command\n");
295 continue;
296 }
297 if (c->c_conn && !connected) {
298 printf("Not connected.\n");
299 continue;
300 }
301 confirmrest = 0;
302 (*c->c_handler)(margc, margv);
303 if (bell && c->c_bell)
304 (void) putchar('\007');
305 if (c->c_handler != help)
306 break;
307 }
308 (void) signal(SIGINT, intr);
309 (void) signal(SIGPIPE, lostpeer);
310 }
311
312 struct cmd *
313 getcmd(name)
314 const char *name;
315 {
316 const char *p, *q;
317 struct cmd *c, *found;
318 int nmatches, longest;
319
320 if (name == NULL)
321 return (0);
322
323 longest = 0;
324 nmatches = 0;
325 found = 0;
326 for (c = cmdtab; (p = c->c_name) != NULL; c++) {
327 for (q = name; *q == *p++; q++)
328 if (*q == 0) /* exact match? */
329 return (c);
330 if (!*q) { /* the name was a prefix */
331 if (q - name > longest) {
332 longest = q - name;
333 nmatches = 1;
334 found = c;
335 } else if (q - name == longest)
336 nmatches++;
337 }
338 }
339 if (nmatches > 1)
340 return ((struct cmd *)-1);
341 return (found);
342 }
343
344 /*
345 * Slice a string up into argc/argv.
346 */
347
348 int slrflag;
349
350 void
351 makeargv()
352 {
353 char **argp;
354
355 argp = margv;
356 stringbase = line; /* scan from first of buffer */
357 argbase = argbuf; /* store from first of buffer */
358 slrflag = 0;
359 for (margc = 0; ; margc++) {
360 /* Expand array if necessary */
361 if (margc == margvlen) {
362 margv = (margvlen == 0)
363 ? (char **)malloc(20 * sizeof(char *))
364 : (char **)realloc(margv,
365 (margvlen + 20)*sizeof(char *));
366 if (margv == NULL)
367 errx(1, "cannot realloc argv array");
368 margvlen += 20;
369 argp = margv + margc;
370 }
371
372 if ((*argp++ = slurpstring()) == NULL)
373 break;
374 }
375
376 }
377
378 /*
379 * Parse string into argbuf;
380 * implemented with FSM to
381 * handle quoting and strings
382 */
383 char *
384 slurpstring()
385 {
386 int got_one = 0;
387 char *sb = stringbase;
388 char *ap = argbase;
389 char *tmp = argbase; /* will return this if token found */
390
391 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
392 switch (slrflag) { /* and $ as token for macro invoke */
393 case 0:
394 slrflag++;
395 stringbase++;
396 return ((*sb == '!') ? "!" : "$");
397 /* NOTREACHED */
398 case 1:
399 slrflag++;
400 altarg = stringbase;
401 break;
402 default:
403 break;
404 }
405 }
406
407 S0:
408 switch (*sb) {
409
410 case '\0':
411 goto OUT;
412
413 case ' ':
414 case '\t':
415 sb++; goto S0;
416
417 default:
418 switch (slrflag) {
419 case 0:
420 slrflag++;
421 break;
422 case 1:
423 slrflag++;
424 altarg = sb;
425 break;
426 default:
427 break;
428 }
429 goto S1;
430 }
431
432 S1:
433 switch (*sb) {
434
435 case ' ':
436 case '\t':
437 case '\0':
438 goto OUT; /* end of token */
439
440 case '\\':
441 sb++; goto S2; /* slurp next character */
442
443 case '"':
444 sb++; goto S3; /* slurp quoted string */
445
446 default:
447 *ap++ = *sb++; /* add character to token */
448 got_one = 1;
449 goto S1;
450 }
451
452 S2:
453 switch (*sb) {
454
455 case '\0':
456 goto OUT;
457
458 default:
459 *ap++ = *sb++;
460 got_one = 1;
461 goto S1;
462 }
463
464 S3:
465 switch (*sb) {
466
467 case '\0':
468 goto OUT;
469
470 case '"':
471 sb++; goto S1;
472
473 default:
474 *ap++ = *sb++;
475 got_one = 1;
476 goto S3;
477 }
478
479 OUT:
480 if (got_one)
481 *ap++ = '\0';
482 argbase = ap; /* update storage pointer */
483 stringbase = sb; /* update scan pointer */
484 if (got_one) {
485 return (tmp);
486 }
487 switch (slrflag) {
488 case 0:
489 slrflag++;
490 break;
491 case 1:
492 slrflag++;
493 altarg = (char *) 0;
494 break;
495 default:
496 break;
497 }
498 return ((char *)0);
499 }
500
501 #define HELPINDENT ((int) sizeof ("directory"))
502
503 /*
504 * Help command.
505 * Call each command handler with argc == 0 and argv[0] == name.
506 */
507 void
508 help(argc, argv)
509 int argc;
510 char *argv[];
511 {
512 struct cmd *c;
513
514 if (argc == 1) {
515 int i, j, w, k;
516 int columns, width = 0, lines;
517
518 printf("Commands may be abbreviated. Commands are:\n\n");
519 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
520 int len = strlen(c->c_name);
521
522 if (len > width)
523 width = len;
524 }
525 width = (width + 8) &~ 7;
526 columns = 80 / width;
527 if (columns == 0)
528 columns = 1;
529 lines = (NCMDS + columns - 1) / columns;
530 for (i = 0; i < lines; i++) {
531 for (j = 0; j < columns; j++) {
532 c = cmdtab + j * lines + i;
533 if (c->c_name && (!proxy || c->c_proxy)) {
534 printf("%s", c->c_name);
535 }
536 else if (c->c_name) {
537 for (k=0; k < strlen(c->c_name); k++) {
538 (void) putchar(' ');
539 }
540 }
541 if (c + lines >= &cmdtab[NCMDS]) {
542 printf("\n");
543 break;
544 }
545 w = strlen(c->c_name);
546 while (w < width) {
547 w = (w + 8) &~ 7;
548 (void) putchar('\t');
549 }
550 }
551 }
552 return;
553 }
554 while (--argc > 0) {
555 char *arg;
556 arg = *++argv;
557 c = getcmd(arg);
558 if (c == (struct cmd *)-1)
559 printf("?Ambiguous help command %s\n", arg);
560 else if (c == (struct cmd *)0)
561 printf("?Invalid help command %s\n", arg);
562 else
563 printf("%-*s\t%s\n", HELPINDENT,
564 c->c_name, c->c_help);
565 }
566 }
567
568 int
569 auto_fetch(argc, argv)
570 int argc;
571 char *argv[];
572 {
573 char *xargv[5];
574 char *cp, *host, *dir, *file;
575 int rval, xargc;
576 size_t urllen;
577
578 urllen = strlen(FTPURL);
579 rval = 0;
580
581 if (setjmp(toplevel))
582 exit(0);
583 (void) signal(SIGINT, intr);
584 (void) signal(SIGPIPE, lostpeer);
585
586 /*
587 * Loop through as long as there's files to fetch.
588 */
589 while (argc > 0 && strchr(argv[0], ':') != NULL) {
590 host = dir = file = portnum = NULL;
591
592 /*
593 * We muck with the string, so we make a copy.
594 */
595 host = strdup(argv[0]);
596 if (host == NULL)
597 errx(1, "Can't allocate memory for auto-fetch.");
598
599 /*
600 * Try URL-style arguments first, then host:file.
601 */
602 if (strncmp(host, FTPURL, urllen) == 0) {
603 host += urllen;
604 cp = strchr(host, '/');
605
606 /* Look for a port number after the nost name. */
607 portnum = strchr(host, ':');
608 if (portnum != NULL)
609 *portnum++ = '\0';
610 } else
611 cp = strchr(host, ':');
612
613 /*
614 * If cp is NULL, the file wasn't specified
615 * (URL looked something like ftp://host)
616 */
617 if (cp == NULL)
618 usage();
619
620 /*
621 * Extract the file and (if present) directory name.
622 */
623 *cp++ = '\0';
624 dir = cp;
625 cp = strrchr(cp, '/');
626 if (cp != NULL) {
627 *cp++ = '\0';
628 file = cp;
629 } else {
630 file = dir;
631 dir = NULL;
632 }
633 if (file == NULL || *file == '\0')
634 usage();
635
636 /*
637 * Set up the connection.
638 */
639 xargv[0] = __progname;
640 xargv[1] = host;
641 xargv[2] = NULL;
642 xargc = 2;
643 if (portnum != NULL) {
644 xargv[2] = portnum;
645 xargv[3] = NULL;
646 xargc = 3;
647 }
648 setpeer(xargc, xargv);
649 if (connected == 0) {
650 warnx("Can't connect to host `%s'.", host);
651 rval = 1;
652 free(host);
653 continue;
654 }
655
656 /* Always use binary transfers. */
657 setbinary(0, NULL);
658
659 /* Change directories, if necessary. */
660 if (dir != NULL) {
661 xargv[0] = "cd";
662 xargv[1] = dir;
663 xargv[2] = NULL;
664 cd(2, xargv);
665 /* XXX SHOULD CHECK FOR ERROR CONDITION! */
666 }
667
668 /* Fetch the file. */
669 xargv[0] = "get";
670 xargv[1] = file;
671 xargv[2] = NULL;
672 get(2, xargv);
673
674 disconnect(0, NULL);
675 free(host);
676
677 --argc, ++argv;
678 }
679
680 return (rval);
681 }
682
683 void
684 usage()
685 {
686 (void)fprintf(stderr,
687 "usage: %s [-adginptv] [-P port] [host [port]]\n"
688 " %s ftp://host[:port]/file\n"
689 " %s host:file\n",
690 __progname, __progname, __progname);
691 exit(1);
692 }
693