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