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