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