main.c revision 120945
1/* 2 * Copyright (c) 1985, 1989, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34/* 35 * FTP User Program -- Command Interface. 36 */ 37 38#include "ftp_locl.h" 39#include <getarg.h> 40 41RCSID("$Id: main.c,v 1.33 2002/10/29 09:47:51 joda Exp $"); 42 43static int help_flag; 44static int version_flag; 45static int debug_flag; 46 47struct getargs getargs[] = { 48 { NULL, 'd', arg_flag, &debug_flag, 49 "debug", NULL }, 50 { NULL, 'g', arg_negative_flag, &doglob, 51 "disables globbing", NULL}, 52 { NULL, 'i', arg_negative_flag, &interactive, 53 "Turn off interactive prompting", NULL}, 54 { NULL, 'l', arg_negative_flag, &lineedit, 55 "Turn off line editing", NULL}, 56 { NULL, 'n', arg_negative_flag, &autologin, 57 "Turn off auto-login", NULL}, 58 { NULL, 'p', arg_flag, &passivemode, 59 "passive mode", NULL}, 60 { NULL, 't', arg_counter, &trace, 61 "Packet tracing", NULL}, 62 { NULL, 'v', arg_counter, &verbose, 63 "verbosity", NULL}, 64 { NULL, 'K', arg_negative_flag, &use_kerberos, 65 "Disable kerberos authentication", NULL}, 66 { "version", 0, arg_flag, &version_flag }, 67 { "help", 'h', arg_flag, &help_flag }, 68}; 69 70static int num_args = sizeof(getargs) / sizeof(getargs[0]); 71 72static void 73usage(int ecode) 74{ 75 arg_printusage(getargs, num_args, NULL, "[host [port]]"); 76 exit(ecode); 77} 78 79int 80main(int argc, char **argv) 81{ 82 int top; 83 struct passwd *pw = NULL; 84 char homedir[MaxPathLen]; 85 struct servent *sp; 86 int optind = 0; 87 88 setprogname(argv[0]); 89 90 sp = getservbyname("ftp", "tcp"); 91 if (sp == 0) 92 errx(1, "ftp/tcp: unknown service"); 93 doglob = 1; 94 interactive = 1; 95 autologin = 1; 96 lineedit = 1; 97 passivemode = 0; /* passive mode not active */ 98 use_kerberos = 1; 99 100 if(getarg(getargs, num_args, argc, argv, &optind)) 101 usage(1); 102 if(help_flag) 103 usage(0); 104 if(version_flag) { 105 print_version(NULL); 106 exit(0); 107 } 108 109 if (debug_flag) { 110 options |= SO_DEBUG; 111 debug++; 112 } 113 114 argc -= optind; 115 argv += optind; 116 117 fromatty = isatty(fileno(stdin)); 118 if (fromatty) 119 verbose++; 120 cpend = 0; /* no pending replies */ 121 proxy = 0; /* proxy not active */ 122 crflag = 1; /* strip c.r. on ascii gets */ 123 sendport = -1; /* not using ports */ 124 /* 125 * Set up the home directory in case we're globbing. 126 */ 127 pw = k_getpwuid(getuid()); 128 if (pw != NULL) { 129 strlcpy(homedir, pw->pw_dir, sizeof(homedir)); 130 home = homedir; 131 } 132 if (argc > 0) { 133 char *xargv[5]; 134 135 if (setjmp(toplevel)) 136 exit(0); 137 signal(SIGINT, intr); 138 signal(SIGPIPE, lostpeer); 139 xargv[0] = (char*)getprogname(); 140 xargv[1] = argv[0]; 141 xargv[2] = argv[1]; 142 xargv[3] = argv[2]; 143 xargv[4] = NULL; 144 setpeer(argc+1, xargv); 145 } 146 if(setjmp(toplevel) == 0) 147 top = 1; 148 else 149 top = 0; 150 if (top) { 151 signal(SIGINT, intr); 152 signal(SIGPIPE, lostpeer); 153 } 154 for (;;) { 155 cmdscanner(top); 156 top = 1; 157 } 158} 159 160void 161intr(int sig) 162{ 163 164 longjmp(toplevel, 1); 165} 166 167#ifndef SHUT_RDWR 168#define SHUT_RDWR 2 169#endif 170 171RETSIGTYPE 172lostpeer(int sig) 173{ 174 175 if (connected) { 176 if (cout != NULL) { 177 shutdown(fileno(cout), SHUT_RDWR); 178 fclose(cout); 179 cout = NULL; 180 } 181 if (data >= 0) { 182 shutdown(data, SHUT_RDWR); 183 close(data); 184 data = -1; 185 } 186 connected = 0; 187 } 188 pswitch(1); 189 if (connected) { 190 if (cout != NULL) { 191 shutdown(fileno(cout), SHUT_RDWR); 192 fclose(cout); 193 cout = NULL; 194 } 195 connected = 0; 196 } 197 proxflag = 0; 198 pswitch(0); 199 sec_end(); 200 SIGRETURN(0); 201} 202 203/* 204char * 205tail(filename) 206 char *filename; 207{ 208 char *s; 209 210 while (*filename) { 211 s = strrchr(filename, '/'); 212 if (s == NULL) 213 break; 214 if (s[1]) 215 return (s + 1); 216 *s = '\0'; 217 } 218 return (filename); 219} 220*/ 221 222static char * 223simple_readline(char *prompt) 224{ 225 char buf[BUFSIZ]; 226 printf ("%s", prompt); 227 fflush (stdout); 228 if(fgets(buf, sizeof(buf), stdin) == NULL) 229 return NULL; 230 if (buf[strlen(buf) - 1] == '\n') 231 buf[strlen(buf) - 1] = '\0'; 232 return strdup(buf); 233} 234 235#ifndef HAVE_READLINE 236 237static char * 238readline(char *prompt) 239{ 240 return simple_readline (prompt); 241} 242 243static void 244add_history(char *p) 245{ 246} 247 248#else 249 250/* These should not really be here */ 251 252char *readline(char *); 253void add_history(char *); 254 255#endif 256 257/* 258 * Command parser. 259 */ 260void 261cmdscanner(int top) 262{ 263 struct cmd *c; 264 int l; 265 266 if (!top) 267 putchar('\n'); 268 for (;;) { 269 if (fromatty) { 270 char *p; 271 if (lineedit) 272 p = readline("ftp> "); 273 else 274 p = simple_readline("ftp> "); 275 if(p == NULL) { 276 printf("\n"); 277 quit(0, 0); 278 } 279 strlcpy(line, p, sizeof(line)); 280 if (lineedit) 281 add_history(p); 282 free(p); 283 } else{ 284 if (fgets(line, sizeof line, stdin) == NULL) 285 quit(0, 0); 286 } 287 /* XXX will break on long lines */ 288 l = strlen(line); 289 if (l == 0) 290 break; 291 if (line[--l] == '\n') { 292 if (l == 0) 293 break; 294 line[l] = '\0'; 295 } else if (l == sizeof(line) - 2) { 296 printf("sorry, input line too long\n"); 297 while ((l = getchar()) != '\n' && l != EOF) 298 /* void */; 299 break; 300 } /* else it was a line without a newline */ 301 makeargv(); 302 if (margc == 0) { 303 continue; 304 } 305 c = getcmd(margv[0]); 306 if (c == (struct cmd *)-1) { 307 printf("?Ambiguous command\n"); 308 continue; 309 } 310 if (c == 0) { 311 printf("?Invalid command\n"); 312 continue; 313 } 314 if (c->c_conn && !connected) { 315 printf("Not connected.\n"); 316 continue; 317 } 318 (*c->c_handler)(margc, margv); 319 if (bell && c->c_bell) 320 putchar('\007'); 321 if (c->c_handler != help) 322 break; 323 } 324 signal(SIGINT, intr); 325 signal(SIGPIPE, lostpeer); 326} 327 328struct cmd * 329getcmd(char *name) 330{ 331 char *p, *q; 332 struct cmd *c, *found; 333 int nmatches, longest; 334 335 longest = 0; 336 nmatches = 0; 337 found = 0; 338 for (c = cmdtab; (p = c->c_name); c++) { 339 for (q = name; *q == *p++; q++) 340 if (*q == 0) /* exact match? */ 341 return (c); 342 if (!*q) { /* the name was a prefix */ 343 if (q - name > longest) { 344 longest = q - name; 345 nmatches = 1; 346 found = c; 347 } else if (q - name == longest) 348 nmatches++; 349 } 350 } 351 if (nmatches > 1) 352 return ((struct cmd *)-1); 353 return (found); 354} 355 356/* 357 * Slice a string up into argc/argv. 358 */ 359 360int slrflag; 361 362void 363makeargv(void) 364{ 365 char **argp; 366 367 argp = margv; 368 stringbase = line; /* scan from first of buffer */ 369 argbase = argbuf; /* store from first of buffer */ 370 slrflag = 0; 371 for (margc = 0; ; margc++) { 372 /* Expand array if necessary */ 373 if (margc == margvlen) { 374 int i; 375 376 margv = (margvlen == 0) 377 ? (char **)malloc(20 * sizeof(char *)) 378 : (char **)realloc(margv, 379 (margvlen + 20)*sizeof(char *)); 380 if (margv == NULL) 381 errx(1, "cannot realloc argv array"); 382 for(i = margvlen; i < margvlen + 20; ++i) 383 margv[i] = NULL; 384 margvlen += 20; 385 argp = margv + margc; 386 } 387 388 if ((*argp++ = slurpstring()) == NULL) 389 break; 390 } 391 392} 393 394/* 395 * Parse string into argbuf; 396 * implemented with FSM to 397 * handle quoting and strings 398 */ 399char * 400slurpstring(void) 401{ 402 int got_one = 0; 403 char *sb = stringbase; 404 char *ap = argbase; 405 char *tmp = argbase; /* will return this if token found */ 406 407 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 408 switch (slrflag) { /* and $ as token for macro invoke */ 409 case 0: 410 slrflag++; 411 stringbase++; 412 return ((*sb == '!') ? "!" : "$"); 413 /* NOTREACHED */ 414 case 1: 415 slrflag++; 416 altarg = stringbase; 417 break; 418 default: 419 break; 420 } 421 } 422 423S0: 424 switch (*sb) { 425 426 case '\0': 427 goto OUT; 428 429 case ' ': 430 case '\t': 431 sb++; goto S0; 432 433 default: 434 switch (slrflag) { 435 case 0: 436 slrflag++; 437 break; 438 case 1: 439 slrflag++; 440 altarg = sb; 441 break; 442 default: 443 break; 444 } 445 goto S1; 446 } 447 448S1: 449 switch (*sb) { 450 451 case ' ': 452 case '\t': 453 case '\0': 454 goto OUT; /* end of token */ 455 456 case '\\': 457 sb++; goto S2; /* slurp next character */ 458 459 case '"': 460 sb++; goto S3; /* slurp quoted string */ 461 462 default: 463 *ap++ = *sb++; /* add character to token */ 464 got_one = 1; 465 goto S1; 466 } 467 468S2: 469 switch (*sb) { 470 471 case '\0': 472 goto OUT; 473 474 default: 475 *ap++ = *sb++; 476 got_one = 1; 477 goto S1; 478 } 479 480S3: 481 switch (*sb) { 482 483 case '\0': 484 goto OUT; 485 486 case '"': 487 sb++; goto S1; 488 489 default: 490 *ap++ = *sb++; 491 got_one = 1; 492 goto S3; 493 } 494 495OUT: 496 if (got_one) 497 *ap++ = '\0'; 498 argbase = ap; /* update storage pointer */ 499 stringbase = sb; /* update scan pointer */ 500 if (got_one) { 501 return (tmp); 502 } 503 switch (slrflag) { 504 case 0: 505 slrflag++; 506 break; 507 case 1: 508 slrflag++; 509 altarg = (char *) 0; 510 break; 511 default: 512 break; 513 } 514 return NULL; 515} 516 517#define HELPINDENT ((int) sizeof ("directory")) 518 519/* 520 * Help command. 521 * Call each command handler with argc == 0 and argv[0] == name. 522 */ 523void 524help(int argc, char **argv) 525{ 526 struct cmd *c; 527 528 if (argc == 1) { 529 int i, j, w, k; 530 int columns, width = 0, lines; 531 532 printf("Commands may be abbreviated. Commands are:\n\n"); 533 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 534 int len = strlen(c->c_name); 535 536 if (len > width) 537 width = len; 538 } 539 width = (width + 8) &~ 7; 540 columns = 80 / width; 541 if (columns == 0) 542 columns = 1; 543 lines = (NCMDS + columns - 1) / columns; 544 for (i = 0; i < lines; i++) { 545 for (j = 0; j < columns; j++) { 546 c = cmdtab + j * lines + i; 547 if (c->c_name && (!proxy || c->c_proxy)) { 548 printf("%s", c->c_name); 549 } 550 else if (c->c_name) { 551 for (k=0; k < strlen(c->c_name); k++) { 552 putchar(' '); 553 } 554 } 555 if (c + lines >= &cmdtab[NCMDS]) { 556 printf("\n"); 557 break; 558 } 559 w = strlen(c->c_name); 560 while (w < width) { 561 w = (w + 8) &~ 7; 562 putchar('\t'); 563 } 564 } 565 } 566 return; 567 } 568 while (--argc > 0) { 569 char *arg; 570 arg = *++argv; 571 c = getcmd(arg); 572 if (c == (struct cmd *)-1) 573 printf("?Ambiguous help command %s\n", arg); 574 else if (c == (struct cmd *)0) 575 printf("?Invalid help command %s\n", arg); 576 else 577 printf("%-*s\t%s\n", HELPINDENT, 578 c->c_name, c->c_help); 579 } 580} 581