1/* 2 * Copyright (C) 1986-2008 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 185int 186root_allow_used () 187{ 188 return root_allow_count; 189} 190 191void 192root_allow_add (arg) 193 char *arg; 194{ 195 char *p; 196 197 if (root_allow_size <= root_allow_count) 198 { 199 if (root_allow_size == 0) 200 { 201 root_allow_size = 1; 202 root_allow_vector = 203 (char **) xmalloc (root_allow_size * sizeof (char *)); 204 } 205 else 206 { 207 root_allow_size *= 2; 208 root_allow_vector = 209 (char **) xrealloc (root_allow_vector, 210 root_allow_size * sizeof (char *)); 211 } 212 213 if (root_allow_vector == NULL) 214 { 215 no_memory: 216 /* Strictly speaking, we're not supposed to output anything 217 now. But we're about to exit(), give it a try. */ 218 printf ("E Fatal server error, aborting.\n\ 219error ENOMEM Virtual memory exhausted.\n"); 220 221 error_exit (); 222 } 223 } 224 p = xmalloc (strlen (arg) + 1); 225 if (p == NULL) 226 goto no_memory; 227 strcpy (p, arg); 228 root_allow_vector[root_allow_count++] = p; 229} 230 231void 232root_allow_free () 233{ 234 if (root_allow_vector != NULL) 235 free_names (&root_allow_count, root_allow_vector); 236 root_allow_size = 0; 237} 238 239int 240root_allow_ok (arg) 241 char *arg; 242{ 243 int i; 244 245 if (root_allow_count == 0) 246 { 247 /* Probably someone upgraded from CVS before 1.9.10 to 1.9.10 248 or later without reading the documentation about 249 --allow-root. Printing an error here doesn't disclose any 250 particularly useful information to an attacker because a 251 CVS server configured in this way won't let *anyone* in. */ 252 253 /* Note that we are called from a context where we can spit 254 back "error" rather than waiting for the next request which 255 expects responses. */ 256 printf ("\ 257error 0 Server configuration missing --allow-root in inetd.conf\n"); 258 error_exit (); 259 } 260 261 for (i = 0; i < root_allow_count; ++i) 262 if (strcmp (root_allow_vector[i], arg) == 0) 263 return 1; 264 return 0; 265} 266 267 268 269/* This global variable holds the global -d option. It is NULL if -d 270 was not used, which means that we must get the CVSroot information 271 from the CVSROOT environment variable or from a CVS/Root file. */ 272char *CVSroot_cmdline; 273 274 275 276/* FIXME - Deglobalize this. */ 277cvsroot_t *current_parsed_root = NULL; 278 279 280 281/* allocate and initialize a cvsroot_t 282 * 283 * We must initialize the strings to NULL so we know later what we should 284 * free 285 * 286 * Some of the other zeroes remain meaningful as, "never set, use default", 287 * or the like 288 */ 289static cvsroot_t * 290new_cvsroot_t () 291{ 292 cvsroot_t *newroot; 293 294 /* gotta store it somewhere */ 295 newroot = xmalloc(sizeof(cvsroot_t)); 296 297 newroot->original = NULL; 298 newroot->method = null_method; 299 newroot->isremote = 0; 300#ifdef CLIENT_SUPPORT 301 newroot->username = NULL; 302 newroot->password = NULL; 303 newroot->hostname = NULL; 304 newroot->port = 0; 305 newroot->directory = NULL; 306 newroot->proxy_hostname = NULL; 307 newroot->proxy_port = 0; 308#endif /* CLIENT_SUPPORT */ 309 310 return newroot; 311} 312 313 314 315/* Dispose of a cvsroot_t and its component parts */ 316void 317free_cvsroot_t (root) 318 cvsroot_t *root; 319{ 320 if (root->original != NULL) 321 free (root->original); 322 if (root->directory != NULL) 323 free (root->directory); 324#ifdef CLIENT_SUPPORT 325 if (root->username != NULL) 326 free (root->username); 327 if (root->password != NULL) 328 { 329 /* I like to be paranoid */ 330 memset (root->password, 0, strlen (root->password)); 331 free (root->password); 332 } 333 if (root->hostname != NULL) 334 free (root->hostname); 335 if (root->proxy_hostname != NULL) 336 free (root->proxy_hostname); 337#endif /* CLIENT_SUPPORT */ 338 free (root); 339} 340 341 342 343/* 344 * Parse a CVSROOT string to allocate and return a new cvsroot_t structure. 345 * Valid specifications are: 346 * 347 * :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path 348 * [:(ext|server):][[user]@]host[:]/path 349 * [:local:[e:]]/path 350 * :fork:/path 351 * 352 * INPUTS 353 * root_in C String containing the CVSROOT to be parsed. 354 * 355 * RETURNS 356 * A pointer to a newly allocated cvsroot_t structure upon success and 357 * NULL upon failure. The caller is responsible for disposing of 358 * new structures with a call to free_cvsroot_t(). 359 * 360 * NOTES 361 * This would have been a lot easier to write in Perl. 362 * 363 * SEE ALSO 364 * free_cvsroot_t() 365 */ 366cvsroot_t * 367parse_cvsroot (root_in) 368 const char *root_in; 369{ 370 cvsroot_t *newroot; /* the new root to be returned */ 371 char *cvsroot_save; /* what we allocated so we can dispose 372 * it when finished */ 373 char *firstslash; /* save where the path spec starts 374 * while we parse 375 * [[user][:password]@]host[:[port]] 376 */ 377 char *cvsroot_copy, *p, *q; /* temporary pointers for parsing */ 378#ifdef CLIENT_SUPPORT 379 int check_hostname, no_port, no_password; 380#endif /* CLIENT_SUPPORT */ 381 382 assert (root_in); 383 384 /* allocate some space */ 385 newroot = new_cvsroot_t(); 386 387 /* save the original string */ 388 newroot->original = xstrdup (root_in); 389 390 /* and another copy we can munge while parsing */ 391 cvsroot_save = cvsroot_copy = xstrdup (root_in); 392 393 if (*cvsroot_copy == ':') 394 { 395 char *method = ++cvsroot_copy; 396 397 /* Access method specified, as in 398 * "cvs -d :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path", 399 * "cvs -d [:(ext|server):][[user]@]host[:]/path", 400 * "cvs -d :local:e:\path", 401 * "cvs -d :fork:/path". 402 * We need to get past that part of CVSroot before parsing the 403 * rest of it. 404 */ 405 406 if (! (p = strchr (method, ':'))) 407 { 408 error (0, 0, "No closing `:' on method in CVSROOT."); 409 goto error_exit; 410 } 411 *p = '\0'; 412 cvsroot_copy = ++p; 413 414#ifdef CLIENT_SUPPORT 415 /* Look for method options, for instance, proxy, proxyport. 416 * We don't handle these, but we like to try and warn the user that 417 * they are being ignored. 418 */ 419 if ((p = strchr (method, ';')) != NULL) 420 { 421 *p++ = '\0'; 422 if (!really_quiet) 423 { 424 error (0, 0, 425"WARNING: Ignoring method options found in CVSROOT: `%s'.", 426 p); 427 error (0, 0, 428"Use CVS version 1.12.7 or later to handle method options."); 429 } 430 } 431#endif /* CLIENT_SUPPORT */ 432 433 /* Now we have an access method -- see if it's valid. */ 434 435 if (strcmp (method, "local") == 0) 436 newroot->method = local_method; 437 else if (strcmp (method, "pserver") == 0) 438 newroot->method = pserver_method; 439 else if (strcmp (method, "kserver") == 0) 440 newroot->method = kserver_method; 441 else if (strcmp (method, "gserver") == 0) 442 newroot->method = gserver_method; 443 else if (strcmp (method, "server") == 0) 444 newroot->method = server_method; 445 else if (strcmp (method, "ext") == 0) 446 newroot->method = ext_method; 447 else if (strcmp (method, "extssh") == 0) 448 newroot->method = extssh_method; 449 else if (strcmp (method, "fork") == 0) 450 newroot->method = fork_method; 451 else 452 { 453 error (0, 0, "Unknown method (`%s') in CVSROOT.", method); 454 goto error_exit; 455 } 456 } 457 else 458 { 459 /* If the method isn't specified, assume EXT_METHOD if the string looks 460 like a relative path and LOCAL_METHOD otherwise. */ 461 462 newroot->method = ((*cvsroot_copy != '/' && strchr (cvsroot_copy, '/')) 463 ? ext_method 464 : local_method); 465 } 466 467 newroot->isremote = (newroot->method != local_method); 468 469 if ((newroot->method != local_method) 470 && (newroot->method != fork_method)) 471 { 472 /* split the string into [[user][:password]@]host[:[port]] & /path 473 * 474 * this will allow some characters such as '@' & ':' to remain unquoted 475 * in the path portion of the spec 476 */ 477 if ((p = strchr (cvsroot_copy, '/')) == NULL) 478 { 479 error (0, 0, "CVSROOT requires a path spec:"); 480 error (0, 0, 481":(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path"); 482 error (0, 0, "[:(ext|server):][[user]@]host[:]/path"); 483 goto error_exit; 484 } 485 firstslash = p; /* == NULL if '/' not in string */ 486 *p = '\0'; 487 488 /* Don't parse username, password, hostname, or port without client 489 * support. 490 */ 491#ifdef CLIENT_SUPPORT 492 /* Check to see if there is a username[:password] in the string. */ 493 if ((p = strchr (cvsroot_copy, '@')) != NULL) 494 { 495 *p = '\0'; 496 /* check for a password */ 497 if ((q = strchr (cvsroot_copy, ':')) != NULL) 498 { 499 *q = '\0'; 500 newroot->password = xstrdup (++q); 501 /* Don't check for *newroot->password == '\0' since 502 * a user could conceivably wish to specify a blank password 503 * 504 * (newroot->password == NULL means to use the 505 * password from .cvspass) 506 */ 507 } 508 509 /* copy the username */ 510 if (*cvsroot_copy != '\0') 511 /* a blank username is impossible, so leave it NULL in that 512 * case so we know to use the default username 513 */ 514 newroot->username = xstrdup (cvsroot_copy); 515 516 cvsroot_copy = ++p; 517 } 518 519 /* now deal with host[:[port]] */ 520 521 /* the port */ 522 if ((p = strchr (cvsroot_copy, ':')) != NULL) 523 { 524 *p++ = '\0'; 525 if (strlen(p)) 526 { 527 q = p; 528 if (*q == '-') q++; 529 while (*q) 530 { 531 if (!isdigit(*q++)) 532 { 533 error (0, 0, 534"CVSROOT may only specify a positive, non-zero, integer port (not `%s').", 535 p); 536 error (0, 0, 537 "Perhaps you entered a relative pathname?"); 538 goto error_exit; 539 } 540 } 541 if ((newroot->port = atoi (p)) <= 0) 542 { 543 error (0, 0, 544"CVSROOT may only specify a positive, non-zero, integer port (not `%s').", 545 p); 546 error (0, 0, "Perhaps you entered a relative pathname?"); 547 goto error_exit; 548 } 549 } 550 } 551 552 /* copy host */ 553 if (*cvsroot_copy != '\0') 554 /* blank hostnames are invalid, but for now leave the field NULL 555 * and catch the error during the sanity checks later 556 */ 557 newroot->hostname = xstrdup (cvsroot_copy); 558 559 /* restore the '/' */ 560 cvsroot_copy = firstslash; 561 *cvsroot_copy = '/'; 562#endif /* CLIENT_SUPPORT */ 563 } 564 565 /* 566 * Parse the path for all methods. 567 */ 568 /* Here & local_cvsroot() should be the only places this needs to be 569 * called on a CVSROOT now. cvsroot->original is saved for error messages 570 * and, otherwise, we want no trailing slashes. 571 */ 572 Sanitize_Repository_Name( cvsroot_copy ); 573 newroot->directory = xstrdup(cvsroot_copy); 574 575 /* 576 * Do various sanity checks. 577 */ 578 579#if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG) 580 if (newroot->method != local_method) 581 { 582 error (0, 0, "CVSROOT is set for a remote access method but your"); 583 error (0, 0, "CVS executable doesn't support it."); 584 goto error_exit; 585 } 586#endif 587 588#if ! defined (SERVER_SUPPORT) && ! defined (DEBUG) 589 if (newroot->method == fork_method) 590 { 591 error (0, 0, "CVSROOT is set to use the :fork: access method but your"); 592 error (0, 0, "CVS executable doesn't support it."); 593 goto error_exit; 594 } 595#endif 596 597#ifdef CLIENT_SUPPORT 598 if (newroot->username && ! newroot->hostname) 599 { 600 error (0, 0, "Missing hostname in CVSROOT."); 601 goto error_exit; 602 } 603 604 check_hostname = 0; 605 no_password = 1; 606 no_port = 0; 607#endif /* CLIENT_SUPPORT */ 608 switch (newroot->method) 609 { 610 case local_method: 611#ifdef CLIENT_SUPPORT 612 if (newroot->username || newroot->hostname) 613 { 614 error (0, 0, "Can't specify hostname and username in CVSROOT"); 615 error (0, 0, "when using local access method."); 616 goto error_exit; 617 } 618 no_port = 1; 619 /* no_password already set */ 620#endif /* CLIENT_SUPPORT */ 621 /* cvs.texinfo has always told people that CVSROOT must be an 622 absolute pathname. Furthermore, attempts to use a relative 623 pathname produced various errors (I couldn't get it to work), 624 so there would seem to be little risk in making this a fatal 625 error. */ 626 if (!isabsolute (newroot->directory)) 627 { 628 error (0, 0, "CVSROOT must be an absolute pathname (not `%s')", 629 newroot->directory); 630 error (0, 0, "when using local access method."); 631 goto error_exit; 632 } 633 break; 634#ifdef CLIENT_SUPPORT 635 case fork_method: 636 /* We want :fork: to behave the same as other remote access 637 methods. Therefore, don't check to see that the repository 638 name is absolute -- let the server do it. */ 639 if (newroot->username || newroot->hostname) 640 { 641 error (0, 0, "Can't specify hostname and username in CVSROOT"); 642 error (0, 0, "when using fork access method."); 643 goto error_exit; 644 } 645 newroot->hostname = xstrdup("server"); /* for error messages */ 646 if (!isabsolute (newroot->directory)) 647 { 648 error (0, 0, "CVSROOT must be an absolute pathname (not `%s')", 649 newroot->directory); 650 error (0, 0, "when using fork access method."); 651 goto error_exit; 652 } 653 no_port = 1; 654 /* no_password already set */ 655 break; 656 case kserver_method: 657# ifndef HAVE_KERBEROS 658 error (0, 0, "CVSROOT is set for a kerberos access method but your"); 659 error (0, 0, "CVS executable doesn't support it."); 660 goto error_exit; 661# else 662 check_hostname = 1; 663 /* no_password already set */ 664 break; 665# endif 666 case gserver_method: 667# ifndef HAVE_GSSAPI 668 error (0, 0, "CVSROOT is set for a GSSAPI access method but your"); 669 error (0, 0, "CVS executable doesn't support it."); 670 goto error_exit; 671# else 672 check_hostname = 1; 673 /* no_password already set */ 674 break; 675# endif 676 case server_method: 677 case ext_method: 678 case extssh_method: 679 no_port = 1; 680 /* no_password already set */ 681 check_hostname = 1; 682 break; 683 case pserver_method: 684 no_password = 0; 685 check_hostname = 1; 686 break; 687#endif /* CLIENT_SUPPORT */ 688 default: 689 error (1, 0, "Invalid method found in parse_cvsroot"); 690 } 691 692#ifdef CLIENT_SUPPORT 693 if (no_password && newroot->password) 694 { 695 error (0, 0, "CVSROOT password specification is only valid for"); 696 error (0, 0, "pserver connection method."); 697 goto error_exit; 698 } 699 700 if (check_hostname && !newroot->hostname) 701 { 702 error (0, 0, "Didn't specify hostname in CVSROOT."); 703 goto error_exit; 704 } 705 706 if (no_port && newroot->port) 707 { 708 error (0, 0, "CVSROOT port specification is only valid for gserver, kserver,"); 709 error (0, 0, "and pserver connection methods."); 710 goto error_exit; 711 } 712#endif /* CLIENT_SUPPORT */ 713 714 if (*newroot->directory == '\0') 715 { 716 error (0, 0, "Missing directory in CVSROOT."); 717 goto error_exit; 718 } 719 720 /* Hooray! We finally parsed it! */ 721 free (cvsroot_save); 722 return newroot; 723 724error_exit: 725 free (cvsroot_save); 726 free_cvsroot_t (newroot); 727 return NULL; 728} 729 730 731 732#ifdef AUTH_CLIENT_SUPPORT 733/* Use root->username, root->hostname, root->port, and root->directory 734 * to create a normalized CVSROOT fit for the .cvspass file 735 * 736 * username defaults to the result of getcaller() 737 * port defaults to the result of get_cvs_port_number() 738 * 739 * FIXME - we could cache the canonicalized version of a root inside the 740 * cvsroot_t, but we'd have to un'const the input here and stop expecting the 741 * caller to be responsible for our return value 742 */ 743char * 744normalize_cvsroot (root) 745 const cvsroot_t *root; 746{ 747 char *cvsroot_canonical; 748 char *p, *hostname, *username; 749 char port_s[64]; 750 751 assert (root && root->hostname && root->directory); 752 753 /* get the appropriate port string */ 754 sprintf (port_s, "%d", get_cvs_port_number (root)); 755 756 /* use a lower case hostname since we know hostnames are case insensitive */ 757 /* Some logic says we should be tacking our domain name on too if it isn't 758 * there already, but for now this works. Reverse->Forward lookups are 759 * almost certainly too much since that would make CVS immune to some of 760 * the DNS trickery that makes life easier for sysadmins when they want to 761 * move a repository or the like 762 */ 763 p = hostname = xstrdup(root->hostname); 764 while (*p) 765 { 766 *p = tolower(*p); 767 p++; 768 } 769 770 /* get the username string */ 771 username = root->username ? root->username : getcaller(); 772 cvsroot_canonical = xmalloc ( strlen(username) 773 + strlen(hostname) + strlen(port_s) 774 + strlen(root->directory) + 12); 775 sprintf (cvsroot_canonical, ":pserver:%s@%s:%s%s", 776 username, hostname, port_s, root->directory); 777 778 free (hostname); 779 return cvsroot_canonical; 780} 781#endif /* AUTH_CLIENT_SUPPORT */ 782 783 784 785/* allocate and return a cvsroot_t structure set up as if we're using the local 786 * repository DIR. */ 787cvsroot_t * 788local_cvsroot (dir) 789 const char *dir; 790{ 791 cvsroot_t *newroot = new_cvsroot_t(); 792 793 newroot->original = xstrdup(dir); 794 newroot->method = local_method; 795 newroot->directory = xstrdup(dir); 796 /* Here and parse_cvsroot() should be the only places this needs to be 797 * called on a CVSROOT now. cvsroot->original is saved for error messages 798 * and, otherwise, we want no trailing slashes. 799 */ 800 Sanitize_Repository_Name( newroot->directory ); 801 return newroot; 802} 803 804 805 806#ifdef DEBUG 807/* This is for testing the parsing function. Use 808 809 gcc -I. -I.. -I../lib -DDEBUG root.c -o root 810 811 to compile. */ 812 813#include <stdio.h> 814 815char *program_name = "testing"; 816char *cvs_cmd_name = "parse_cvsroot"; /* XXX is this used??? */ 817 818/* Toy versions of various functions when debugging under unix. Yes, 819 these make various bad assumptions, but they're pretty easy to 820 debug when something goes wrong. */ 821 822void 823error_exit PROTO ((void)) 824{ 825 exit (1); 826} 827 828int 829isabsolute (dir) 830 const char *dir; 831{ 832 return (dir && (*dir == '/')); 833} 834 835void 836main (argc, argv) 837 int argc; 838 char *argv[]; 839{ 840 program_name = argv[0]; 841 842 if (argc != 2) 843 { 844 fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name); 845 exit (2); 846 } 847 848 if ((current_parsed_root = parse_cvsroot (argv[1])) == NULL) 849 { 850 fprintf (stderr, "%s: Parsing failed.\n", program_name); 851 exit (1); 852 } 853 printf ("CVSroot: %s\n", argv[1]); 854 printf ("current_parsed_root->method: %s\n", method_names[current_parsed_root->method]); 855 printf ("current_parsed_root->username: %s\n", 856 current_parsed_root->username ? current_parsed_root->username : "NULL"); 857 printf ("current_parsed_root->hostname: %s\n", 858 current_parsed_root->hostname ? current_parsed_root->hostname : "NULL"); 859 printf ("current_parsed_root->directory: %s\n", current_parsed_root->directory); 860 861 exit (0); 862 /* NOTREACHED */ 863} 864#endif 865