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