1 /* 2 * Copyright (c) 1992, Mark D. Baushke 3 * 4 * You may distribute under the terms of the GNU General Public License as 5 * specified in the README file that comes with the CVS source distribution. 6 * 7 * Name of Root 8 * 9 * Determine the path to the CVSROOT and set "Root" accordingly. 10 */ 11 12 #include "cvs.h" 13 #include "getline.h" 14 15 /* Printable names for things in the current_parsed_root->method enum variable. 16 Watch out if the enum is changed in cvs.h! */ 17 18 char *method_names[] = { 19 "undefined", "local", "server (ssh)", "pserver", "kserver", "gserver", "ext", "fork" 20 }; 21 22 #ifndef DEBUG 23 24 char * 25 Name_Root (dir, update_dir) 26 char *dir; 27 char *update_dir; 28 { 29 FILE *fpin; 30 char *ret, *xupdate_dir; 31 char *root = NULL; 32 size_t root_allocated = 0; 33 char *tmp; 34 char *cvsadm; 35 char *cp; 36 37 if (update_dir && *update_dir) 38 xupdate_dir = update_dir; 39 else 40 xupdate_dir = "."; 41 42 if (dir != NULL) 43 { 44 cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); 45 (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); 46 tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 47 (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 48 } 49 else 50 { 51 cvsadm = xstrdup (CVSADM); 52 tmp = xstrdup (CVSADM_ROOT); 53 } 54 55 /* 56 * Do not bother looking for a readable file if there is no cvsadm 57 * directory present. 58 * 59 * It is possible that not all repositories will have a CVS/Root 60 * file. This is ok, but the user will need to specify -d 61 * /path/name or have the environment variable CVSROOT set in 62 * order to continue. */ 63 if ((!isdir (cvsadm)) || (!isreadable (tmp))) 64 { 65 ret = NULL; 66 goto out; 67 } 68 69 /* 70 * The assumption here is that the CVS Root is always contained in the 71 * first line of the "Root" file. 72 */ 73 fpin = open_file (tmp, "r"); 74 75 if (get_line (&root, &root_allocated, fpin) < 0) 76 { 77 /* FIXME: should be checking for end of file separately; errno 78 is not set in that case. */ 79 error (0, 0, "in directory %s:", xupdate_dir); 80 error (0, errno, "cannot read %s", CVSADM_ROOT); 81 error (0, 0, "please correct this problem"); 82 ret = NULL; 83 goto out; 84 } 85 (void) fclose (fpin); 86 if ((cp = strrchr (root, '\n')) != NULL) 87 *cp = '\0'; /* strip the newline */ 88 89 /* 90 * root now contains a candidate for CVSroot. It must be an 91 * absolute pathname or specify a remote server. 92 */ 93 94 if ( 95 #ifdef CLIENT_SUPPORT 96 (strchr (root, ':') == NULL) && 97 #endif 98 ! isabsolute (root)) 99 { 100 error (0, 0, "in directory %s:", xupdate_dir); 101 error (0, 0, 102 "ignoring %s because it does not contain an absolute pathname.", 103 CVSADM_ROOT); 104 ret = NULL; 105 goto out; 106 } 107 108 #ifdef CLIENT_SUPPORT 109 if ((strchr (root, ':') == NULL) && !isdir (root)) 110 #else /* ! CLIENT_SUPPORT */ 111 if (!isdir (root)) 112 #endif /* CLIENT_SUPPORT */ 113 { 114 error (0, 0, "in directory %s:", xupdate_dir); 115 error (0, 0, 116 "ignoring %s because it specifies a non-existent repository %s", 117 CVSADM_ROOT, root); 118 ret = NULL; 119 goto out; 120 } 121 122 /* allocate space to return and fill it in */ 123 strip_trailing_slashes (root); 124 ret = xstrdup (root); 125 out: 126 free (cvsadm); 127 free (tmp); 128 if (root != NULL) 129 free (root); 130 return (ret); 131 } 132 133 /* 134 * Write the CVS/Root file so that the environment variable CVSROOT 135 * and/or the -d option to cvs will be validated or not necessary for 136 * future work. 137 */ 138 void 139 Create_Root (dir, rootdir) 140 char *dir; 141 char *rootdir; 142 { 143 FILE *fout; 144 char *tmp; 145 146 if (noexec) 147 return; 148 149 /* record the current cvs root */ 150 151 if (rootdir != NULL) 152 { 153 if (dir != NULL) 154 { 155 tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 156 (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 157 } 158 else 159 tmp = xstrdup (CVSADM_ROOT); 160 161 fout = open_file (tmp, "w+"); 162 if (fprintf (fout, "%s\n", rootdir) < 0) 163 error (1, errno, "write to %s failed", tmp); 164 if (fclose (fout) == EOF) 165 error (1, errno, "cannot close %s", tmp); 166 free (tmp); 167 } 168 } 169 170 #endif /* ! DEBUG */ 171 172 173 /* The root_allow_* stuff maintains a list of legal CVSROOT 174 directories. Then we can check against them when a remote user 175 hands us a CVSROOT directory. */ 176 177 static int root_allow_count; 178 static char **root_allow_vector; 179 static int root_allow_size; 180 181 void 182 root_allow_add (arg) 183 char *arg; 184 { 185 char *p; 186 187 if (root_allow_size <= root_allow_count) 188 { 189 if (root_allow_size == 0) 190 { 191 root_allow_size = 1; 192 root_allow_vector = 193 (char **) malloc (root_allow_size * sizeof (char *)); 194 } 195 else 196 { 197 root_allow_size *= 2; 198 root_allow_vector = 199 (char **) realloc (root_allow_vector, 200 root_allow_size * sizeof (char *)); 201 } 202 203 if (root_allow_vector == NULL) 204 { 205 no_memory: 206 /* Strictly speaking, we're not supposed to output anything 207 now. But we're about to exit(), give it a try. */ 208 printf ("E Fatal server error, aborting.\n\ 209 error ENOMEM Virtual memory exhausted.\n"); 210 211 /* I'm doing this manually rather than via error_exit () 212 because I'm not sure whether we want to call server_cleanup. 213 Needs more investigation.... */ 214 215 #ifdef SYSTEM_CLEANUP 216 /* Hook for OS-specific behavior, for example socket 217 subsystems on NT and OS2 or dealing with windows 218 and arguments on Mac. */ 219 SYSTEM_CLEANUP (); 220 #endif 221 222 exit (EXIT_FAILURE); 223 } 224 } 225 p = malloc (strlen (arg) + 1); 226 if (p == NULL) 227 goto no_memory; 228 strcpy (p, arg); 229 root_allow_vector[root_allow_count++] = p; 230 } 231 232 void 233 root_allow_free () 234 { 235 if (root_allow_vector != NULL) 236 free_names (&root_allow_count, root_allow_vector); 237 root_allow_size = 0; 238 } 239 240 int 241 root_allow_ok (arg) 242 char *arg; 243 { 244 int i; 245 246 if (root_allow_count == 0) 247 { 248 /* Probably someone upgraded from CVS before 1.9.10 to 1.9.10 249 or later without reading the documentation about 250 --allow-root. Printing an error here doesn't disclose any 251 particularly useful information to an attacker because a 252 CVS server configured in this way won't let *anyone* in. */ 253 254 /* Note that we are called from a context where we can spit 255 back "error" rather than waiting for the next request which 256 expects responses. */ 257 printf ("\ 258 error 0 Server configuration missing --allow-root in inetd.conf\n"); 259 error_exit (); 260 } 261 262 for (i = 0; i < root_allow_count; ++i) 263 if (strcmp (root_allow_vector[i], arg) == 0) 264 return 1; 265 return 0; 266 } 267 268 269 270 /* This global variable holds the global -d option. It is NULL if -d 271 was not used, which means that we must get the CVSroot information 272 from the CVSROOT environment variable or from a CVS/Root file. */ 273 274 char *CVSroot_cmdline; 275 276 277 278 /* Parse a CVSROOT variable into its constituent parts -- method, 279 * username, hostname, directory. The prototypical CVSROOT variable 280 * looks like: 281 * 282 * :method:user@host:path 283 * 284 * Some methods may omit fields; local, for example, doesn't need user 285 * and host. 286 * 287 * Returns pointer to new cvsroot_t on success, NULL on failure. */ 288 289 cvsroot_t *current_parsed_root = NULL; 290 291 292 293 /* allocate and initialize a cvsroot_t 294 * 295 * We must initialize the strings to NULL so we know later what we should 296 * free 297 * 298 * Some of the other zeroes remain meaningful as, "never set, use default", 299 * or the like 300 */ 301 static cvsroot_t * 302 new_cvsroot_t () 303 { 304 cvsroot_t *newroot; 305 306 /* gotta store it somewhere */ 307 newroot = xmalloc(sizeof(cvsroot_t)); 308 309 newroot->original = NULL; 310 newroot->method = null_method; 311 newroot->username = NULL; 312 newroot->password = NULL; 313 newroot->hostname = NULL; 314 newroot->port = 0; 315 newroot->directory = NULL; 316 #ifdef CLIENT_SUPPORT 317 newroot->isremote = 0; 318 #endif /* CLIENT_SUPPORT */ 319 320 return newroot; 321 } 322 323 324 325 /* Dispose of a cvsroot_t and its component parts */ 326 void 327 free_cvsroot_t (root) 328 cvsroot_t *root; 329 { 330 if (root->original != NULL) 331 free (root->original); 332 if (root->username != NULL) 333 free (root->username); 334 if (root->password != NULL) 335 { 336 /* I like to be paranoid */ 337 memset (root->password, 0, strlen (root->password)); 338 free (root->password); 339 } 340 if (root->hostname != NULL) 341 free (root->hostname); 342 if (root->directory != NULL) 343 free (root->directory); 344 free (root); 345 } 346 347 348 349 /* 350 * parse a CVSROOT string to allocate and return a new cvsroot_t structure 351 */ 352 cvsroot_t * 353 parse_cvsroot (root_in) 354 char *root_in; 355 { 356 cvsroot_t *newroot; /* the new root to be returned */ 357 char *cvsroot_save; /* what we allocated so we can dispose 358 * it when finished */ 359 char *firstslash; /* save where the path spec starts 360 * while we parse 361 * [[user][:password]@]host[:[port]] 362 */ 363 char *cvsroot_copy, *p, *q; /* temporary pointers for parsing */ 364 char *new_hostname; 365 int check_hostname, no_port, no_password; 366 367 /* allocate some space */ 368 newroot = new_cvsroot_t(); 369 370 /* save the original string */ 371 newroot->original = xstrdup (root_in); 372 373 /* and another copy we can munge while parsing */ 374 cvsroot_save = cvsroot_copy = xstrdup (root_in); 375 376 if (*cvsroot_copy == ':') 377 { 378 char *method = ++cvsroot_copy; 379 380 /* Access method specified, as in 381 * "cvs -d :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path", 382 * "cvs -d [:(ext|server):][[user]@]host[:]/path", 383 * "cvs -d :local:e:\path", 384 * "cvs -d :fork:/path". 385 * We need to get past that part of CVSroot before parsing the 386 * rest of it. 387 */ 388 389 if (! (p = strchr (method, ':'))) 390 { 391 error (0, 0, "bad CVSroot: %s", root_in); 392 free (cvsroot_save); 393 goto error_exit; 394 } 395 *p = '\0'; 396 cvsroot_copy = ++p; 397 398 /* Now we have an access method -- see if it's valid. */ 399 400 if (strcmp (method, "local") == 0) 401 newroot->method = local_method; 402 else if (strcmp (method, "pserver") == 0) 403 newroot->method = pserver_method; 404 else if (strcmp (method, "kserver") == 0) 405 newroot->method = kserver_method; 406 else if (strcmp (method, "gserver") == 0) 407 newroot->method = gserver_method; 408 else if (strcmp (method, "server") == 0) 409 newroot->method = server_method; 410 else if (strcmp (method, "ext") == 0) 411 newroot->method = ext_method; 412 else if (strcmp (method, "fork") == 0) 413 newroot->method = fork_method; 414 else 415 { 416 error (0, 0, "unknown method in CVSroot: %s", root_in); 417 free (cvsroot_save); 418 goto error_exit; 419 } 420 } 421 else 422 { 423 /* If the method isn't specified, assume 424 SERVER_METHOD/EXT_METHOD if the string contains a colon or 425 LOCAL_METHOD otherwise. */ 426 427 newroot->method = ((*cvsroot_copy != '/' && strchr (cvsroot_copy, '/')) 428 /*#ifdef RSH_NOT_TRANSPARENT 429 ? server_method 430 #else*/ 431 ? ext_method 432 /*#endif*/ 433 : local_method); 434 } 435 436 #ifdef CLIENT_SUPPORT 437 newroot->isremote = (newroot->method != local_method); 438 #endif /* CLIENT_SUPPORT */ 439 440 441 if ((newroot->method != local_method) 442 && (newroot->method != fork_method)) 443 { 444 /* split the string into [[user][:password]@]host[:[port]] & /path 445 * 446 * this will allow some characters such as '@' & ':' to remain unquoted 447 * in the path portion of the spec 448 */ 449 if ((p = strchr (cvsroot_copy, '/')) == NULL) 450 { 451 error (0, 0, "CVSROOT (\"%s\")", root_in); 452 error (0, 0, "requires a path spec"); 453 error (0, 0, ":(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path"); 454 error (0, 0, "[:(ext|server):][[user]@]host[:]/path"); 455 free (cvsroot_save); 456 goto error_exit; 457 } 458 firstslash = p; /* == NULL if '/' not in string */ 459 *p = '\0'; 460 461 /* Check to see if there is a username[:password] in the string. */ 462 if ((p = strchr (cvsroot_copy, '@')) != NULL) 463 { 464 *p = '\0'; 465 /* check for a password */ 466 if ((q = strchr (cvsroot_copy, ':')) != NULL) 467 { 468 *q = '\0'; 469 newroot->password = xstrdup (++q); 470 /* Don't check for *newroot->password == '\0' since 471 * a user could conceivably wish to specify a blank password 472 * (newroot->password == NULL means to use the 473 * password from .cvspass) 474 */ 475 } 476 477 /* copy the username */ 478 if (*cvsroot_copy != '\0') 479 /* a blank username is impossible, so leave it NULL in that 480 * case so we know to use the default username 481 */ 482 newroot->username = xstrdup (cvsroot_copy); 483 484 cvsroot_copy = ++p; 485 } 486 487 new_hostname = NULL; 488 if (*cvsroot_copy == '[') 489 { 490 p = strchr(cvsroot_copy, ']'); 491 if (p != NULL) 492 { 493 *p = '\0'; 494 new_hostname = xstrdup (cvsroot_copy+1); 495 *p++ = ']'; 496 if (*p == ':') 497 cvsroot_copy = p; 498 } 499 } 500 501 /* now deal with host[:[port]] */ 502 503 /* the port */ 504 if ((p = strchr (cvsroot_copy, ':')) != NULL) 505 { 506 *p++ = '\0'; 507 if (strlen(p)) 508 { 509 q = p; 510 if (*q == '-') q++; 511 while (*q) 512 { 513 if (!isdigit(*q++)) 514 { 515 error(0, 0, "CVSROOT (\"%s\")", root_in); 516 error(0, 0, "may only specify a positive, non-zero, integer port (not \"%s\").", 517 p); 518 error(0, 0, "perhaps you entered a relative pathname?"); 519 free (cvsroot_save); 520 if (new_hostname != NULL) 521 free (new_hostname); 522 goto error_exit; 523 } 524 } 525 if ((newroot->port = atoi (p)) <= 0) 526 { 527 error (0, 0, "CVSROOT (\"%s\")", root_in); 528 error(0, 0, "may only specify a positive, non-zero, integer port (not \"%s\").", 529 p); 530 error(0, 0, "perhaps you entered a relative pathname?"); 531 if (new_hostname != NULL) 532 free (new_hostname); 533 free (cvsroot_save); 534 goto error_exit; 535 } 536 } 537 } 538 539 /* copy host */ 540 if (new_hostname != NULL) 541 newroot->hostname = new_hostname; 542 else if (*cvsroot_copy != '\0') 543 /* blank hostnames are invalid, but for now leave the field NULL 544 * and catch the error during the sanity checks later 545 */ 546 newroot->hostname = xstrdup (cvsroot_copy); 547 548 /* restore the '/' */ 549 cvsroot_copy = firstslash; 550 *cvsroot_copy = '/'; 551 } 552 553 /* parse the path for all methods */ 554 newroot->directory = xstrdup(cvsroot_copy); 555 free (cvsroot_save); 556 557 /* 558 * Do various sanity checks. 559 */ 560 561 #if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG) 562 if (newroot->method != local_method) 563 { 564 error (0, 0, "CVSROOT \"%s\"", root_in); 565 error (0, 0, "is set for a remote access method but your"); 566 error (0, 0, "CVS executable doesn't support it"); 567 goto error_exit; 568 } 569 #endif 570 571 #if ! defined (SERVER_SUPPORT) && ! defined (DEBUG) 572 if (newroot->method == fork_method) 573 { 574 error (0, 0, "CVSROOT \"%s\"", root_in); 575 error (0, 0, "is set to use the :fork: access method but your"); 576 error (0, 0, "CVS executable doesn't support it"); 577 goto error_exit; 578 } 579 #endif 580 581 if (newroot->username && ! newroot->hostname) 582 { 583 error (0, 0, "missing hostname in CVSROOT: \"%s\"", root_in); 584 goto error_exit; 585 } 586 587 check_hostname = 0; 588 no_password = 0; 589 no_port = 0; 590 switch (newroot->method) 591 { 592 case local_method: 593 if (newroot->username || newroot->hostname) 594 { 595 error (0, 0, "can't specify hostname and username in CVSROOT"); 596 error (0, 0, "(\"%s\")", root_in); 597 error (0, 0, "when using local access method"); 598 goto error_exit; 599 } 600 /* cvs.texinfo has always told people that CVSROOT must be an 601 absolute pathname. Furthermore, attempts to use a relative 602 pathname produced various errors (I couldn't get it to work), 603 so there would seem to be little risk in making this a fatal 604 error. */ 605 if (!isabsolute (newroot->directory)) 606 { 607 error (0, 0, "CVSROOT \"%s\" must be an absolute pathname", 608 newroot->directory); 609 goto error_exit; 610 } 611 no_port = 1; 612 no_password = 1; 613 break; 614 case fork_method: 615 /* We want :fork: to behave the same as other remote access 616 methods. Therefore, don't check to see that the repository 617 name is absolute -- let the server do it. */ 618 if (newroot->username || newroot->hostname) 619 { 620 error (0, 0, "can't specify hostname and username in CVSROOT"); 621 error (0, 0, "(\"%s\")", root_in); 622 error (0, 0, "when using fork access method"); 623 goto error_exit; 624 } 625 if (!isabsolute (newroot->directory)) 626 { 627 error (0, 0, "CVSROOT \"%s\" must be an absolute pathname", 628 newroot->directory); 629 goto error_exit; 630 } 631 no_port = 1; 632 no_password = 1; 633 break; 634 case kserver_method: 635 #ifndef HAVE_KERBEROS 636 error (0, 0, "CVSROOT \"%s\"", root_in); 637 error (0, 0, "is set for a kerberos access method but your"); 638 error (0, 0, "CVS executable doesn't support it"); 639 goto error_exit; 640 #else 641 check_hostname = 1; 642 break; 643 #endif 644 case gserver_method: 645 #ifndef HAVE_GSSAPI 646 error (0, 0, "CVSROOT \"%s\"", root_in); 647 error (0, 0, "is set for a GSSAPI access method but your"); 648 error (0, 0, "CVS executable doesn't support it"); 649 goto error_exit; 650 #else 651 check_hostname = 1; 652 break; 653 #endif 654 case server_method: 655 case ext_method: 656 no_port = 1; 657 no_password = 1; 658 check_hostname = 1; 659 break; 660 case pserver_method: 661 check_hostname = 1; 662 break; 663 } 664 665 if (no_password && newroot->password) 666 { 667 error (0, 0, "CVSROOT password specification is only valid for"); 668 error (0, 0, "pserver connection method."); 669 goto error_exit; 670 } 671 672 if (check_hostname && !newroot->hostname) 673 { 674 error (0, 0, "didn't specify hostname in CVSROOT: %s", root_in); 675 goto error_exit; 676 } 677 678 if (no_port && newroot->port) 679 { 680 error (0, 0, "CVSROOT port specification is only valid for gserver, kserver,"); 681 error (0, 0, "and pserver connection methods."); 682 goto error_exit; 683 } 684 685 if (*newroot->directory == '\0') 686 { 687 error (0, 0, "missing directory in CVSROOT: %s", root_in); 688 goto error_exit; 689 } 690 691 /* Hooray! We finally parsed it! */ 692 return newroot; 693 694 error_exit: 695 free_cvsroot_t (newroot); 696 return NULL; 697 } 698 699 700 701 #ifdef AUTH_CLIENT_SUPPORT 702 /* Use root->username, root->hostname, root->port, and root->directory 703 * to create a normalized CVSROOT fit for the .cvspass file 704 * 705 * username defaults to the result of getcaller() 706 * port defaults to the result of get_cvs_port_number() 707 * 708 * FIXME - we could cache the canonicalized version of a root inside the 709 * cvsroot_t, but we'd have to un'const the input here and stop expecting the 710 * caller to be responsible for our return value 711 */ 712 char * 713 normalize_cvsroot (root) 714 const cvsroot_t *root; 715 { 716 char *cvsroot_canonical; 717 char *p, *hostname, *username; 718 char port_s[64]; 719 720 /* get the appropriate port string */ 721 sprintf (port_s, "%d", get_cvs_port_number (root)); 722 723 /* use a lower case hostname since we know hostnames are case insensitive */ 724 /* Some logic says we should be tacking our domain name on too if it isn't 725 * there already, but for now this works. Reverse->Forward lookups are 726 * almost certainly too much since that would make CVS immune to some of 727 * the DNS trickery that makes life easier for sysadmins when they want to 728 * move a repository or the like 729 */ 730 p = hostname = xstrdup(root->hostname); 731 while (*p) 732 { 733 *p = tolower(*p); 734 p++; 735 } 736 737 /* get the username string */ 738 username = root->username ? root->username : getcaller(); 739 cvsroot_canonical = xmalloc ( strlen(username) 740 + strlen(hostname) + strlen(port_s) 741 + strlen(root->directory) + 12); 742 sprintf (cvsroot_canonical, ":pserver:%s@%s:%s%s", 743 username, hostname, port_s, root->directory); 744 745 free (hostname); 746 return cvsroot_canonical; 747 } 748 #endif /* AUTH_CLIENT_SUPPORT */ 749 750 751 752 /* allocate and return a cvsroot_t structure set up as if we're using the local 753 * repository DIR. */ 754 cvsroot_t * 755 local_cvsroot (dir) 756 char *dir; 757 { 758 cvsroot_t *newroot = new_cvsroot_t(); 759 760 newroot->original = xstrdup(dir); 761 newroot->method = local_method; 762 newroot->directory = xstrdup(dir); 763 764 return newroot; 765 } 766 767 768 769 #ifdef DEBUG 770 /* This is for testing the parsing function. Use 771 772 gcc -I. -I.. -I../lib -DDEBUG root.c -o root 773 774 to compile. */ 775 776 #include <stdio.h> 777 778 char *program_name = "testing"; 779 char *command_name = "parse_cvsroot"; /* XXX is this used??? */ 780 781 /* Toy versions of various functions when debugging under unix. Yes, 782 these make various bad assumptions, but they're pretty easy to 783 debug when something goes wrong. */ 784 785 void 786 error_exit PROTO ((void)) 787 { 788 exit (1); 789 } 790 791 int 792 isabsolute (dir) 793 const char *dir; 794 { 795 return (dir && (*dir == '/')); 796 } 797 798 void 799 main (argc, argv) 800 int argc; 801 char *argv[]; 802 { 803 program_name = argv[0]; 804 805 if (argc != 2) 806 { 807 fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name); 808 exit (2); 809 } 810 811 if ((current_parsed_root = parse_cvsroot (argv[1])) == NULL) 812 { 813 fprintf (stderr, "%s: Parsing failed.\n", program_name); 814 exit (1); 815 } 816 printf ("CVSroot: %s\n", argv[1]); 817 printf ("current_parsed_root->method: %s\n", method_names[current_parsed_root->method]); 818 printf ("current_parsed_root->username: %s\n", 819 current_parsed_root->username ? current_parsed_root->username : "NULL"); 820 printf ("current_parsed_root->hostname: %s\n", 821 current_parsed_root->hostname ? current_parsed_root->hostname : "NULL"); 822 printf ("current_parsed_root->directory: %s\n", current_parsed_root->directory); 823 824 exit (0); 825 /* NOTREACHED */ 826 } 827 #endif 828