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