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