main.c revision 25865
1/* 2 * Copyright (c) 1992, Brian Berliner and Jeff Polk 3 * Copyright (c) 1989-1992, Brian Berliner 4 * 5 * You may distribute under the terms of the GNU General Public License 6 * as specified in the README file that comes with the CVS 1.4 kit. 7 * 8 * This is the main C driver for the CVS system. 9 * 10 * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing 11 * the shell-script CVS system that this is based on. 12 * 13 */ 14 15#include "cvs.h" 16 17#ifdef HAVE_WINSOCK_H 18#include <winsock.h> 19#else 20extern int gethostname (); 21#endif 22 23char *program_name; 24char *program_path; 25char *command_name; 26 27/* I'd dynamically allocate this, but it seems like gethostname 28 requires a fixed size array. If I'm remembering the RFCs right, 29 256 should be enough. */ 30#ifndef MAXHOSTNAMELEN 31#define MAXHOSTNAMELEN 256 32#endif 33 34char hostname[MAXHOSTNAMELEN]; 35 36int use_editor = TRUE; 37int use_cvsrc = TRUE; 38int cvswrite = !CVSREAD_DFLT; 39int really_quiet = FALSE; 40int quiet = FALSE; 41int trace = FALSE; 42int noexec = FALSE; 43int logoff = FALSE; 44mode_t cvsumask = UMASK_DFLT; 45 46char *CurDir; 47 48/* 49 * Defaults, for the environment variables that are not set 50 */ 51char *Rcsbin = RCSBIN_DFLT; 52char *Tmpdir = TMPDIR_DFLT; 53char *Editor = EDITOR_DFLT; 54 55static const struct cmd 56{ 57 char *fullname; /* Full name of the function (e.g. "commit") */ 58 59 /* Synonyms for the command, nick1 and nick2. We supply them 60 mostly for two reasons: (1) CVS has always supported them, and 61 we need to maintain compatibility, (2) if there is a need for a 62 version which is shorter than the fullname, for ease in typing. 63 Synonyms have the disadvantage that people will see "new" and 64 then have to think about it, or look it up, to realize that is 65 the operation they know as "add". Also, this means that one 66 cannot create a command "cvs new" with a different meaning. So 67 new synonyms are probably best used sparingly, and where used 68 should be abbreviations of the fullname (preferably consisting 69 of the first 2 or 3 or so letters). 70 71 One thing that some systems do is to recognize any unique 72 abbreviation, for example "annotat" "annota", etc., for 73 "annotate". The problem with this is that scripts and user 74 habits will expect a certain abbreviation to be unique, and in 75 a future release of CVS it may not be. So it is better to 76 accept only an explicit list of abbreviations and plan on 77 supporting them in the future as well as now. */ 78 79 char *nick1; 80 char *nick2; 81 82 int (*func) (); /* Function takes (argc, argv) arguments. */ 83} cmds[] = 84 85{ 86 { "add", "ad", "new", add }, 87 { "admin", "adm", "rcs", admin }, 88 { "annotate", "ann", NULL, annotate }, 89 { "checkout", "co", "get", checkout }, 90 { "commit", "ci", "com", commit }, 91 { "diff", "di", "dif", diff }, 92 { "edit", NULL, NULL, edit }, 93 { "editors", NULL, NULL, editors }, 94 { "export", "exp", "ex", checkout }, 95 { "history", "hi", "his", history }, 96 { "import", "im", "imp", import }, 97 { "init", NULL, NULL, init }, 98#ifdef SERVER_SUPPORT 99 { "kserver", NULL, NULL, server }, /* placeholder */ 100#endif 101 { "log", "lo", "rlog", cvslog }, 102#ifdef AUTH_CLIENT_SUPPORT 103 { "login", "logon", "lgn", login }, 104 { "logout", NULL, NULL, logout }, 105#ifdef SERVER_SUPPORT 106 { "pserver", NULL, NULL, server }, /* placeholder */ 107#endif 108#endif /* AUTH_CLIENT_SUPPORT */ 109 { "rdiff", "patch", "pa", patch }, 110 { "release", "re", "rel", release }, 111 { "remove", "rm", "delete", cvsremove }, 112 { "status", "st", "stat", status }, 113 { "rtag", "rt", "rfreeze", rtag }, 114 { "tag", "ta", "freeze", cvstag }, 115 { "unedit", NULL, NULL, unedit }, 116 { "update", "up", "upd", update }, 117 { "watch", NULL, NULL, watch }, 118 { "watchers", NULL, NULL, watchers }, 119#ifdef SERVER_SUPPORT 120 { "server", NULL, NULL, server }, 121#endif 122 { NULL, NULL, NULL, NULL }, 123}; 124 125static const char *const usg[] = 126{ 127 "Usage: %s [cvs-options] command [command-options] [files...]\n", 128 " Where 'cvs-options' are:\n", 129 " -H Displays Usage information for command\n", 130 " -Q Cause CVS to be really quiet.\n", 131 " -q Cause CVS to be somewhat quiet.\n", 132 " -r Make checked-out files read-only\n", 133 " -w Make checked-out files read-write (default)\n", 134 " -l Turn History logging off\n", 135 " -n Do not execute anything that will change the disk\n", 136 " -t Show trace of program execution -- Try with -n\n", 137 " -v CVS version and copyright\n", 138 " -b bindir Find RCS programs in 'bindir'\n", 139 " -T tmpdir Use 'tmpdir' for temporary files\n", 140 " -e editor Use 'editor' for editing log information\n", 141 " -d CVS_root Overrides $CVSROOT as the root of the CVS tree\n", 142 " -f Do not use the ~/.cvsrc file\n", 143#ifdef CLIENT_SUPPORT 144 " -z # Use compression level '#' for net traffic.\n", 145#ifdef ENCRYPTION 146 " -x Encrypt all net traffic.\n", 147#endif 148#endif 149 " -s VAR=VAL Set CVS user variable.\n", 150 "\n", 151 " and where 'command' is: add, admin, etc. (use the --help-commands\n", 152 " option for a list of commands)\n", 153 NULL, 154}; 155 156static const char *const cmd_usage[] = 157{ 158 "CVS commands are:\n", 159 " add Add a new file/directory to the repository\n", 160 " admin Administration front end for rcs\n", 161 " annotate Show last revision where each line was modified\n", 162 " checkout Checkout sources for editing\n", 163 " commit Check files into the repository\n", 164 " diff Show differences between revisions\n", 165 " edit Get ready to edit a watched file\n", 166 " editors See who is editing a watched file\n", 167 " export Export sources from CVS, similar to checkout\n", 168 " history Show repository access history\n", 169 " import Import sources into CVS, using vendor branches\n", 170 " init Create a CVS repository if it doesn't exist\n", 171 " log Print out history information for files\n", 172#ifdef AUTH_CLIENT_SUPPORT 173 " login Prompt for password for authenticating server.\n", 174 " logout Removes entry in .cvspass for remote repository.\n", 175#endif /* AUTH_CLIENT_SUPPORT */ 176 " rdiff Create 'patch' format diffs between releases\n", 177 " release Indicate that a Module is no longer in use\n", 178 " remove Remove an entry from the repository\n", 179 " rtag Add a symbolic tag to a module\n", 180 " status Display status information on checked out files\n", 181 " tag Add a symbolic tag to checked out version of files\n", 182 " unedit Undo an edit command\n", 183 " update Bring work tree in sync with repository\n", 184 " watch Set watches\n", 185 " watchers See who is watching a file\n", 186 "(Use the --help-synonyms option for a list of alternate command names)\n", 187 NULL, 188}; 189 190static const char * const* 191cmd_synonyms () 192{ 193 char ** synonyms; 194 char ** line; 195 const struct cmd *c = &cmds[0]; 196 int numcmds = 2; /* two more for title and end */ 197 198 while (c->fullname != NULL) 199 { 200 numcmds++; 201 c++; 202 } 203 204 synonyms = (char **) xmalloc(numcmds * sizeof(char *)); 205 line = synonyms; 206 *line++ = "CVS command synonyms are:\n"; 207 for (c = &cmds[0]; c->fullname != NULL; c++) 208 { 209 if (c->nick1 || c->nick2) 210 { 211 *line = xmalloc (strlen (c->fullname) 212 + (c->nick1 != NULL ? strlen (c->nick1) : 0) 213 + (c->nick2 != NULL ? strlen (c->nick2) : 0) 214 + 40); 215 sprintf(*line, " %-12s %s %s\n", c->fullname, 216 c->nick1 ? c->nick1 : "", 217 c->nick2 ? c->nick2 : ""); 218 line++; 219 } 220 } 221 *line = NULL; 222 223 return (const char * const*) synonyms; /* will never be freed */ 224} 225 226 227unsigned long int 228lookup_command_attribute (cmd_name) 229 char *cmd_name; 230{ 231 unsigned long int ret = 0; 232 233 if (strcmp (cmd_name, "import") != 0) 234 { 235 ret |= CVS_CMD_IGNORE_ADMROOT; 236 } 237 238 239 if ((strcmp (cmd_name, "checkout") != 0) && 240 (strcmp (cmd_name, "init") != 0) && 241 (strcmp (cmd_name, "login") != 0) && 242 (strcmp (cmd_name, "logout") != 0) && 243 (strcmp (cmd_name, "rdiff") != 0) && 244 (strcmp (cmd_name, "release") != 0) && 245 (strcmp (cmd_name, "rtag") != 0)) 246 { 247 ret |= CVS_CMD_USES_WORK_DIR; 248 } 249 250 251 /* The following commands do not modify the repository; we 252 conservatively assume that everything else does. Feel free to 253 add to this list if you are _certain_ something is safe. */ 254 if ((strcmp (cmd_name, "checkout") != 0) && 255 (strcmp (cmd_name, "diff") != 0) && 256 (strcmp (cmd_name, "update") != 0) && 257 (strcmp (cmd_name, "history") != 0) && 258 (strcmp (cmd_name, "editors") != 0) && 259 (strcmp (cmd_name, "export") != 0) && 260 (strcmp (cmd_name, "history") != 0) && 261 (strcmp (cmd_name, "log") != 0) && 262 (strcmp (cmd_name, "noop") != 0) && 263 (strcmp (cmd_name, "watchers") != 0) && 264 (strcmp (cmd_name, "status") != 0)) 265 { 266 ret |= CVS_CMD_MODIFIES_REPOSITORY; 267 } 268 269 return ret; 270} 271 272 273static RETSIGTYPE 274main_cleanup (sig) 275 int sig; 276{ 277#ifndef DONT_USE_SIGNALS 278 const char *name; 279 char temp[10]; 280 281 switch (sig) 282 { 283#ifdef SIGHUP 284 case SIGHUP: 285 name = "hangup"; 286 break; 287#endif 288#ifdef SIGINT 289 case SIGINT: 290 name = "interrupt"; 291 break; 292#endif 293#ifdef SIGQUIT 294 case SIGQUIT: 295 name = "quit"; 296 break; 297#endif 298#ifdef SIGPIPE 299 case SIGPIPE: 300 name = "broken pipe"; 301 break; 302#endif 303#ifdef SIGTERM 304 case SIGTERM: 305 name = "termination"; 306 break; 307#endif 308 default: 309 /* This case should never be reached, because we list above all 310 the signals for which we actually establish a signal handler. */ 311 sprintf (temp, "%d", sig); 312 name = temp; 313 break; 314 } 315 316 error (1, 0, "received %s signal", name); 317#endif /* !DONT_USE_SIGNALS */ 318} 319 320int 321main (argc, argv) 322 int argc; 323 char **argv; 324{ 325 char *CVSroot = CVSROOT_DFLT; 326 extern char *version_string; 327 extern char *config_string; 328 char *cp, *end; 329 const struct cmd *cm; 330 int c, err = 0; 331 int rcsbin_update_env, tmpdir_update_env, cvs_update_env; 332 int help = 0; /* Has the user asked for help? This 333 lets us support the `cvs -H cmd' 334 convention to give help for cmd. */ 335 static struct option long_options[] = 336 { 337 {"help", 0, NULL, 'H'}, 338 {"version", 0, NULL, 'v'}, 339 {"help-commands", 0, NULL, 1}, 340 {"help-synonyms", 0, NULL, 2}, 341 {0, 0, 0, 0} 342 }; 343 /* `getopt_long' stores the option index here, but right now we 344 don't use it. */ 345 int option_index = 0; 346 int need_to_create_root = 0; 347 348#ifdef SYSTEM_INITIALIZE 349 /* Hook for OS-specific behavior, for example socket subsystems on 350 NT and OS2 or dealing with windows and arguments on Mac. */ 351 SYSTEM_INITIALIZE (&argc, &argv); 352#endif 353 354#ifdef HAVE_TZSET 355 /* On systems that have tzset (which is almost all the ones I know 356 of), it's a good idea to call it. */ 357 tzset (); 358#endif 359 360 /* 361 * Just save the last component of the path for error messages 362 */ 363 program_path = xstrdup (argv[0]); 364#ifdef ARGV0_NOT_PROGRAM_NAME 365 /* On some systems, e.g. VMS, argv[0] is not the name of the command 366 which the user types to invoke the program. */ 367 program_name = "cvs"; 368#else 369 program_name = last_component (argv[0]); 370#endif 371 372 /* 373 * Query the environment variables up-front, so that 374 * they can be overridden by command line arguments 375 */ 376 cvs_update_env = 0; 377 rcsbin_update_env = *Rcsbin; /* RCSBIN_DFLT must be set */ 378 if ((cp = getenv (RCSBIN_ENV)) != NULL) 379 { 380 Rcsbin = cp; 381 rcsbin_update_env = 0; /* it's already there */ 382 } 383 tmpdir_update_env = *Tmpdir; /* TMPDIR_DFLT must be set */ 384 if ((cp = getenv (TMPDIR_ENV)) != NULL) 385 { 386 Tmpdir = cp; 387 tmpdir_update_env = 0; /* it's already there */ 388 } 389 if ((cp = getenv (EDITOR1_ENV)) != NULL) 390 Editor = cp; 391 else if ((cp = getenv (EDITOR2_ENV)) != NULL) 392 Editor = cp; 393 else if ((cp = getenv (EDITOR3_ENV)) != NULL) 394 Editor = cp; 395 if ((cp = getenv (CVSROOT_ENV)) != NULL) 396 { 397 CVSroot = cp; 398 cvs_update_env = 0; /* it's already there */ 399 } 400 if (getenv (CVSREAD_ENV) != NULL) 401 cvswrite = FALSE; 402 403 /* I'm not sure whether this needs to be 1 instead of 0 anymore. Using 404 1 used to accomplish what passing "+" as the first character to 405 the option string does, but that reason doesn't exist anymore. */ 406 optind = 1; 407 408 409 /* We have to parse the options twice because else there is no 410 chance to avoid reading the global options from ".cvsrc". Set 411 opterr to 0 for avoiding error messages about invalid options. 412 */ 413 opterr = 0; 414 415 while ((c = getopt_long 416 (argc, argv, "+f", NULL, NULL)) 417 != EOF) 418 { 419 if (c == 'f') 420 use_cvsrc = FALSE; 421 } 422 423 /* 424 * Scan cvsrc file for global options. 425 */ 426 if (use_cvsrc) 427 read_cvsrc (&argc, &argv, "cvs"); 428 429 optind = 1; 430 opterr = 1; 431 432 while ((c = getopt_long 433 (argc, argv, "+Qqrwtnlvb:T:e:d:Hfz:s:x", long_options, &option_index)) 434 != EOF) 435 { 436 switch (c) 437 { 438 case 1: 439 /* --help-commands */ 440 usage (cmd_usage); 441 break; 442 case 2: 443 /* --help-synonyms */ 444 usage (cmd_synonyms()); 445 break; 446 case 'Q': 447 really_quiet = TRUE; 448 /* FALL THROUGH */ 449 case 'q': 450 quiet = TRUE; 451 break; 452 case 'r': 453 cvswrite = FALSE; 454 break; 455 case 'w': 456 cvswrite = TRUE; 457 break; 458 case 't': 459 trace = TRUE; 460 break; 461 case 'n': 462 noexec = TRUE; 463 case 'l': /* Fall through */ 464 logoff = TRUE; 465 break; 466 case 'v': 467 (void) fputs (version_string, stdout); 468 (void) fputs (config_string, stdout); 469 (void) fputs ("\n", stdout); 470 (void) fputs ("Copyright (c) 1993-1994 Brian Berliner\n", stdout); 471 (void) fputs ("Copyright (c) 1993-1994 david d `zoo' zuhn\n", stdout); 472 (void) fputs ("Copyright (c) 1992, Brian Berliner and Jeff Polk\n", stdout); 473 (void) fputs ("Copyright (c) 1989-1992, Brian Berliner\n", stdout); 474 (void) fputs ("\n", stdout); 475 (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout); 476 (void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout); 477 exit (0); 478 break; 479 case 'b': 480 Rcsbin = optarg; 481 rcsbin_update_env = 1; /* need to update environment */ 482 break; 483 case 'T': 484 Tmpdir = optarg; 485 tmpdir_update_env = 1; /* need to update environment */ 486 break; 487 case 'e': 488 Editor = optarg; 489 break; 490 case 'd': 491 CVSroot = optarg; 492 cvs_update_env = 1; /* need to update environment */ 493 break; 494 case 'H': 495 help = 1; 496 break; 497 case 'f': 498 use_cvsrc = FALSE; /* unnecessary, since we've done it above */ 499 break; 500 case 'z': 501#ifdef CLIENT_SUPPORT 502 gzip_level = atoi (optarg); 503 if (gzip_level <= 0 || gzip_level > 9) 504 error (1, 0, 505 "gzip compression level must be between 1 and 9"); 506#endif 507 /* If no CLIENT_SUPPORT, we just silently ignore the gzip 508 level, so that users can have it in their .cvsrc and not 509 cause any trouble. */ 510 break; 511 case 's': 512 variable_set (optarg); 513 break; 514 case 'x': 515#ifdef CLIENT_SUPPORT 516 cvsencrypt = 1; 517#endif /* CLIENT_SUPPORT */ 518 /* If no CLIENT_SUPPORT, ignore -x, so that users can 519 have it in their .cvsrc and not cause any trouble. 520 If no ENCRYPTION, we still accept -x, but issue an 521 error if we are being run as a client. */ 522 break; 523 case '?': 524 default: 525 usage (usg); 526 } 527 } 528 529 argc -= optind; 530 argv += optind; 531 if (argc < 1) 532 usage (usg); 533 534 535 /* Look up the command name. */ 536 537 command_name = argv[0]; 538 for (cm = cmds; cm->fullname; cm++) 539 { 540 if (cm->nick1 && !strcmp (command_name, cm->nick1)) 541 break; 542 if (cm->nick2 && !strcmp (command_name, cm->nick2)) 543 break; 544 if (!strcmp (command_name, cm->fullname)) 545 break; 546 } 547 548 if (!cm->fullname) 549 usage (cmd_usage); /* no match */ 550 else 551 command_name = cm->fullname; /* Global pointer for later use */ 552 553 if (strcmp (argv[0], "rlog") == 0) 554 { 555 error (0, 0, "warning: the rlog command is deprecated"); 556 error (0, 0, "use the synonymous log command instead"); 557 } 558 559 if (help) 560 argc = -1; /* some functions only check for this */ 561 else 562 { 563 /* The user didn't ask for help, so go ahead and authenticate, 564 set up CVSROOT, and the rest of it. */ 565 566 /* The UMASK environment variable isn't handled with the 567 others above, since we don't want to signal errors if the 568 user has asked for help. This won't work if somebody adds 569 a command-line flag to set the umask, since we'll have to 570 parse it before we get here. */ 571 572 if ((cp = getenv (CVSUMASK_ENV)) != NULL) 573 { 574 /* FIXME: Should be accepting symbolic as well as numeric mask. */ 575 cvsumask = strtol (cp, &end, 8) & 0777; 576 if (*end != '\0') 577 error (1, errno, "invalid umask value in %s (%s)", 578 CVSUMASK_ENV, cp); 579 } 580 581#if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT) 582 /* If we are invoked with a single argument "kserver", then we are 583 running as Kerberos server as root. Do the authentication as 584 the very first thing, to minimize the amount of time we are 585 running as root. */ 586 if (strcmp (command_name, "kserver") == 0) 587 { 588 kserver_authenticate_connection (); 589 590 /* Pretend we were invoked as a plain server. */ 591 command_name = "server"; 592 } 593#endif /* HAVE_KERBEROS */ 594 595 596#if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT) 597 if (strcmp (command_name, "pserver") == 0) 598 { 599 /* Gets username and password from client, authenticates, then 600 switches to run as that user and sends an ACK back to the 601 client. */ 602 pserver_authenticate_connection (); 603 604 /* Pretend we were invoked as a plain server. */ 605 command_name = "server"; 606 } 607#endif /* AUTH_SERVER_SUPPORT && SERVER_SUPPORT */ 608 609 610 /* Fiddling with CVSROOT doesn't make sense if we're running 611 in server mode, since the client will send the repository 612 directory after the connection is made. */ 613 614#ifdef SERVER_SUPPORT 615 if (strcmp (command_name, "server") != 0) 616#endif 617 { 618 char *CVSADM_Root; 619 620 /* See if we are able to find a 'better' value for CVSroot 621 in the CVSADM_ROOT directory. */ 622 623 CVSADM_Root = NULL; 624 625 /* "cvs import" shouldn't check CVS/Root; in general it 626 ignores CVS directories and CVS/Root is likely to 627 specify a different repository than the one we are 628 importing to. */ 629 630 if (lookup_command_attribute (command_name) 631 & CVS_CMD_IGNORE_ADMROOT) 632 { 633 CVSADM_Root = Name_Root((char *) NULL, (char *) NULL); 634 } 635 636 if (CVSADM_Root != NULL) 637 { 638 if (CVSroot == NULL || !cvs_update_env) 639 { 640 CVSroot = CVSADM_Root; 641 cvs_update_env = 1; /* need to update environment */ 642 } 643 /* Let -d override CVS/Root file. The user might want 644 to change the access method, use a different server 645 (if there are two server machines which share the 646 repository using a networked file system), etc. */ 647 else if ( 648#ifdef CLIENT_SUPPORT 649 !getenv ("CVS_IGNORE_REMOTE_ROOT") && 650#endif 651 strcmp (CVSroot, CVSADM_Root) != 0) 652 { 653 /* Once we have verified that this root is usable, 654 we will want to write it into CVS/Root. 655 656 Don't do it for the "login" command, however. 657 Consider: if the user executes "cvs login" with 658 the working directory inside an already checked 659 out module, we'd incorrectly change the 660 CVS/Root file to reflect the CVSROOT of the 661 "cvs login" command. Ahh, the things one 662 discovers. */ 663 664 if (lookup_command_attribute (command_name) 665 & CVS_CMD_USES_WORK_DIR) 666 { 667 need_to_create_root = 1; 668 } 669 670 } 671 } 672 673 /* Now we've reconciled CVSROOT from the command line, the 674 CVS/Root file, and the environment variable. Do the 675 last sanity checks on the variable. */ 676 677 if (! CVSroot) 678 { 679 error (0, 0, 680 "No CVSROOT specified! Please use the `-d' option"); 681 error (1, 0, 682 "or set the %s environment variable.", CVSROOT_ENV); 683 } 684 685 if (! *CVSroot) 686 { 687 error (0, 0, 688 "CVSROOT is set but empty! Make sure that the"); 689 error (0, 0, 690 "specification of CVSROOT is legal, either via the"); 691 error (0, 0, 692 "`-d' option, the %s environment variable, or the", 693 CVSROOT_ENV); 694 error (1, 0, 695 "CVS/Root file (if any)."); 696 } 697 698 /* Now we're 100% sure that we have a valid CVSROOT 699 variable. Parse it to see if we're supposed to do 700 remote accesses or use a special access method. */ 701 702 if (parse_cvsroot (CVSroot)) 703 error (1, 0, "Bad CVSROOT."); 704 705 /* 706 * Check to see if we can write into the history file. If not, 707 * we assume that we can't work in the repository. 708 * BUT, only if the history file exists. 709 */ 710 711 if (!client_active) 712 { 713 char *path; 714 int save_errno; 715 716 path = xmalloc (strlen (CVSroot_directory) 717 + sizeof (CVSROOTADM) 718 + 20 719 + sizeof (CVSROOTADM_HISTORY)); 720 (void) sprintf (path, "%s/%s", CVSroot_directory, CVSROOTADM); 721 if (!isaccessible (path, R_OK | X_OK)) 722 { 723 save_errno = errno; 724 /* If this is "cvs init", the root need not exist yet. */ 725 if (strcmp (command_name, "init") != 0) 726 { 727 error (1, save_errno, "%s", path); 728 } 729 } 730 (void) strcat (path, "/"); 731 (void) strcat (path, CVSROOTADM_HISTORY); 732 if (isfile (path) && !isaccessible (path, R_OK | W_OK)) 733 { 734 save_errno = errno; 735 error (0, 0, "Sorry, you don't have read/write access to the history file"); 736 error (1, save_errno, "%s", path); 737 } 738 free (path); 739 parseopts(CVSroot_directory); 740 } 741 742#ifdef HAVE_PUTENV 743 /* Update the CVSROOT environment variable if necessary. */ 744 745 if (cvs_update_env) 746 { 747 char *env; 748 env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot) 749 + 1 + 1); 750 (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot); 751 (void) putenv (env); 752 /* do not free env, as putenv has control of it */ 753 } 754#endif 755 } 756 757 /* This is only used for writing into the history file. For 758 remote connections, it might be nice to have hostname 759 and/or remote path, on the other hand I'm not sure whether 760 it is worth the trouble. */ 761 762#ifdef SERVER_SUPPORT 763 if (strcmp (command_name, "server") == 0) 764 CurDir = xstrdup ("<remote>"); 765 else 766#endif 767 { 768 CurDir = xgetwd (); 769 if (CurDir == NULL) 770 error (1, errno, "cannot get working directory"); 771 } 772 773 if (Tmpdir == NULL || Tmpdir[0] == '\0') 774 Tmpdir = "/tmp"; 775 776#ifdef HAVE_PUTENV 777 /* Now, see if we should update the environment with the 778 Rcsbin value */ 779 if (rcsbin_update_env) 780 { 781 char *env; 782 env = xmalloc (strlen (RCSBIN_ENV) + strlen (Rcsbin) + 1 + 1); 783 (void) sprintf (env, "%s=%s", RCSBIN_ENV, Rcsbin); 784 (void) putenv (env); 785 /* do not free env, as putenv has control of it */ 786 } 787 if (tmpdir_update_env) 788 { 789 char *env; 790 env = xmalloc (strlen (TMPDIR_ENV) + strlen (Tmpdir) + 1 + 1); 791 (void) sprintf (env, "%s=%s", TMPDIR_ENV, Tmpdir); 792 (void) putenv (env); 793 /* do not free env, as putenv has control of it */ 794 } 795 { 796 char *env; 797 env = xmalloc (sizeof "CVS_PID=" + 32); /* XXX pid < 10^32 */ 798 (void) sprintf (env, "CVS_PID=%ld", (long) getpid ()); 799 (void) putenv (env); 800 } 801#endif 802 803 /* 804 * If Rcsbin is set to something, make sure it is terminated with 805 * a slash character. If not, add one. 806 */ 807 if (*Rcsbin) 808 { 809 int len = strlen (Rcsbin); 810 char *rcsbin; 811 812 if (Rcsbin[len - 1] != '/') 813 { 814 rcsbin = Rcsbin; 815 Rcsbin = xmalloc (len + 2); /* one for '/', one for NULL */ 816 (void) strcpy (Rcsbin, rcsbin); 817 (void) strcat (Rcsbin, "/"); 818 } 819 } 820 821#ifndef DONT_USE_SIGNALS 822 /* make sure we clean up on error */ 823#ifdef SIGHUP 824 (void) SIG_register (SIGHUP, main_cleanup); 825 (void) SIG_register (SIGHUP, Lock_Cleanup); 826#endif 827#ifdef SIGINT 828 (void) SIG_register (SIGINT, main_cleanup); 829 (void) SIG_register (SIGINT, Lock_Cleanup); 830#endif 831#ifdef SIGQUIT 832 (void) SIG_register (SIGQUIT, main_cleanup); 833 (void) SIG_register (SIGQUIT, Lock_Cleanup); 834#endif 835#ifdef SIGPIPE 836 (void) SIG_register (SIGPIPE, main_cleanup); 837 (void) SIG_register (SIGPIPE, Lock_Cleanup); 838#endif 839#ifdef SIGTERM 840 (void) SIG_register (SIGTERM, main_cleanup); 841 (void) SIG_register (SIGTERM, Lock_Cleanup); 842#endif 843#endif /* !DONT_USE_SIGNALS */ 844 845 gethostname(hostname, sizeof (hostname)); 846 847#ifdef KLUDGE_FOR_WNT_TESTSUITE 848 /* Probably the need for this will go away at some point once 849 we call fflush enough places (e.g. fflush (stdout) in 850 cvs_outerr). */ 851 (void) setvbuf (stdout, (char *) NULL, _IONBF, 0); 852 (void) setvbuf (stderr, (char *) NULL, _IONBF, 0); 853#endif /* KLUDGE_FOR_WNT_TESTSUITE */ 854 855 if (use_cvsrc) 856 read_cvsrc (&argc, &argv, command_name); 857 858 } /* end of stuff that gets done if the user DOESN'T ask for help */ 859 860 err = (*(cm->func)) (argc, argv); 861 862 if (need_to_create_root) 863 { 864 /* Update the CVS/Root file. We might want to do this in 865 all directories that we recurse into, but currently we 866 don't. */ 867 Create_Root (NULL, CVSroot); 868 } 869 870 Lock_Cleanup (); 871 872#ifdef SYSTEM_CLEANUP 873 /* Hook for OS-specific behavior, for example socket subsystems on 874 NT and OS2 or dealing with windows and arguments on Mac. */ 875 SYSTEM_CLEANUP (); 876#endif 877 878 /* This is exit rather than return because apparently that keeps 879 some tools which check for memory leaks happier. */ 880 exit (err ? EXIT_FAILURE : 0); 881} 882 883char * 884Make_Date (rawdate) 885 char *rawdate; 886{ 887 struct tm *ftm; 888 time_t unixtime; 889 char date[MAXDATELEN]; 890 char *ret; 891 892 unixtime = get_date (rawdate, (struct timeb *) NULL); 893 if (unixtime == (time_t) - 1) 894 error (1, 0, "Can't parse date/time: %s", rawdate); 895#ifdef HAVE_RCS5 896 ftm = gmtime (&unixtime); 897#else 898 ftm = localtime (&unixtime); 899#endif 900 (void) sprintf (date, DATEFORM, 901 ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900), 902 ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour, 903 ftm->tm_min, ftm->tm_sec); 904 ret = xstrdup (date); 905 return (ret); 906} 907 908void 909usage (cpp) 910 register const char *const *cpp; 911{ 912 (void) fprintf (stderr, *cpp++, program_name, command_name); 913 for (; *cpp; cpp++) 914 (void) fprintf (stderr, *cpp); 915 error_exit (); 916} 917 918void 919parseopts(root) 920 const char *root; 921{ 922 char path[PATH_MAX]; 923 int save_errno; 924 char buf[1024]; 925 const char *p; 926 char *q; 927 FILE *fp; 928 929 if (root == NULL) { 930 printf("no CVSROOT in parseopts\n"); 931 return; 932 } 933 p = strchr (root, ':'); 934 if (p) 935 p++; 936 else 937 p = root; 938 if (p == NULL) { 939 printf("mangled CVSROOT in parseopts\n"); 940 return; 941 } 942 (void) sprintf (path, "%s/%s/%s", p, CVSROOTADM, CVSROOTADM_OPTIONS); 943 if ((fp = fopen(path, "r")) != NULL) { 944 while (fgets(buf, sizeof buf, fp) != NULL) { 945 if (buf[0] == '#') 946 continue; 947 q = strrchr(buf, '\n'); 948 if (q) 949 *q = '\0'; 950 951 if (!strncmp(buf, "tag=", 4)) { 952 char *what; 953 char *rcs_localid; 954 955 rcs_localid = buf + 4; 956 RCS_setlocalid(rcs_localid); 957 what = malloc(sizeof("RCSLOCALID") + 2 + strlen(rcs_localid)); 958 if (what == NULL) { 959 printf("no memory for local tag\n"); 960 return; 961 } 962 sprintf(what, "RCSLOCALID=%s", rcs_localid); 963 putenv(what); 964 } 965 if (!strncmp(buf, "tagexpand=", 10)) { 966 char *what; 967 char *rcs_incexc; 968 969 rcs_incexc = buf + 10; 970 RCS_setincexc(rcs_incexc); 971 what = malloc(sizeof("RCSINCEXC") + 2 + strlen(rcs_incexc)); 972 if (what == NULL) { 973 printf("no memory for tag expand mode\n"); 974 return; 975 } 976 sprintf(what, "RCSINCEXC=%s", rcs_incexc); 977 putenv(what); 978 } 979 /* 980 * OpenBSD has a "umask=" and "dlimit=" command, we silently 981 * ignore them here since they are not much use to us. cvsumask 982 * defaults to 002 already, and the dlimit (data size limit) 983 * should really be handled elsewhere. 984 */ 985 } 986 fclose(fp); 987 } 988} 989