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 as 6 * specified in the README file that comes with the CVS kit. */ 7 8 #include "cvs.h" 9 #include "savecwd.h" 10 #include "getline.h" 11 12 #ifndef DBLKSIZ 13 #define DBLKSIZ 4096 /* since GNU ndbm doesn't define it */ 14 #endif 15 16 static int checkout_file PROTO((char *file, char *temp)); 17 static char *make_tempfile PROTO((void)); 18 static void rename_rcsfile PROTO((char *temp, char *real)); 19 20 #ifndef MY_NDBM 21 static void rename_dbmfile PROTO((char *temp)); 22 static void write_dbmfile PROTO((char *temp)); 23 #endif /* !MY_NDBM */ 24 25 /* Structure which describes an administrative file. */ 26 struct admin_file { 27 /* Name of the file, within the CVSROOT directory. */ 28 char *filename; 29 30 /* This is a one line description of what the file is for. It is not 31 currently used, although one wonders whether it should be, somehow. 32 If NULL, then don't process this file in mkmodules (FIXME?: a bit of 33 a kludge; probably should replace this with a flags field). */ 34 char *errormsg; 35 36 /* Contents which the file should have in a new repository. To avoid 37 problems with brain-dead compilers which choke on long string constants, 38 this is a pointer to an array of char * terminated by NULL--each of 39 the strings is concatenated. 40 41 If this field is NULL, the file is not created in a new 42 repository, but it can be added with "cvs add" (just as if one 43 had created the repository with a version of CVS which didn't 44 know about the file) and the checked-out copy will be updated 45 without having to add it to checkoutlist. */ 46 const char * const *contents; 47 }; 48 49 static const char *const loginfo_contents[] = { 50 "# The \"loginfo\" file controls where \"cvs commit\" log information\n", 51 "# is sent. The first entry on a line is a regular expression which must match\n", 52 "# the directory that the change is being made to, relative to the\n", 53 "# $CVSROOT. If a match is found, then the remainder of the line is a filter\n", 54 "# program that should expect log information on its standard input.\n", 55 "#\n", 56 "# If the repository name does not match any of the regular expressions in this\n", 57 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 58 "#\n", 59 "# If the name ALL appears as a regular expression it is always used\n", 60 "# in addition to the first matching regex or DEFAULT.\n", 61 "#\n", 62 "# You may specify a format string as part of the\n", 63 "# filter. The string is composed of a `%' followed\n", 64 "# by a single format character, or followed by a set of format\n", 65 "# characters surrounded by `{' and `}' as separators. The format\n", 66 "# characters are:\n", 67 "#\n", 68 "# s = file name\n", 69 "# V = old version number (pre-checkin)\n", 70 "# v = new version number (post-checkin)\n", 71 "#\n", 72 "# For example:\n", 73 "#DEFAULT (echo \"\"; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", 74 "# or\n", 75 "#DEFAULT (echo \"\"; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", 76 NULL 77 }; 78 79 static const char *const rcsinfo_contents[] = { 80 "# The \"rcsinfo\" file is used to control templates with which the editor\n", 81 "# is invoked on commit and import.\n", 82 "#\n", 83 "# The first entry on a line is a regular expression which is tested\n", 84 "# against the directory that the change is being made to, relative to the\n", 85 "# $CVSROOT. For the first match that is found, then the remainder of the\n", 86 "# line is the name of the file that contains the template.\n", 87 "#\n", 88 "# If the repository name does not match any of the regular expressions in this\n", 89 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 90 "#\n", 91 "# If the name \"ALL\" appears as a regular expression it is always used\n", 92 "# in addition to the first matching regex or \"DEFAULT\".\n", 93 NULL 94 }; 95 96 static const char *const editinfo_contents[] = { 97 "# The \"editinfo\" file is used to allow verification of logging\n", 98 "# information. It works best when a template (as specified in the\n", 99 "# rcsinfo file) is provided for the logging procedure. Given a\n", 100 "# template with locations for, a bug-id number, a list of people who\n", 101 "# reviewed the code before it can be checked in, and an external\n", 102 "# process to catalog the differences that were code reviewed, the\n", 103 "# following test can be applied to the code:\n", 104 "#\n", 105 "# Making sure that the entered bug-id number is correct.\n", 106 "# Validating that the code that was reviewed is indeed the code being\n", 107 "# checked in (using the bug-id number or a seperate review\n", 108 "# number to identify this particular code set.).\n", 109 "#\n", 110 "# If any of the above test failed, then the commit would be aborted.\n", 111 "#\n", 112 "# Actions such as mailing a copy of the report to each reviewer are\n", 113 "# better handled by an entry in the loginfo file.\n", 114 "#\n", 115 "# One thing that should be noted is the the ALL keyword is not\n", 116 "# supported. There can be only one entry that matches a given\n", 117 "# repository.\n", 118 NULL 119 }; 120 121 static const char *const verifymsg_contents[] = { 122 "# The \"verifymsg\" file is used to allow verification of logging\n", 123 "# information. It works best when a template (as specified in the\n", 124 "# rcsinfo file) is provided for the logging procedure. Given a\n", 125 "# template with locations for, a bug-id number, a list of people who\n", 126 "# reviewed the code before it can be checked in, and an external\n", 127 "# process to catalog the differences that were code reviewed, the\n", 128 "# following test can be applied to the code:\n", 129 "#\n", 130 "# Making sure that the entered bug-id number is correct.\n", 131 "# Validating that the code that was reviewed is indeed the code being\n", 132 "# checked in (using the bug-id number or a seperate review\n", 133 "# number to identify this particular code set.).\n", 134 "#\n", 135 "# If any of the above test failed, then the commit would be aborted.\n", 136 "#\n", 137 "# Actions such as mailing a copy of the report to each reviewer are\n", 138 "# better handled by an entry in the loginfo file.\n", 139 "#\n", 140 "# One thing that should be noted is the the ALL keyword is not\n", 141 "# supported. There can be only one entry that matches a given\n", 142 "# repository.\n", 143 NULL 144 }; 145 146 static const char *const commitinfo_contents[] = { 147 "# The \"commitinfo\" file is used to control pre-commit checks.\n", 148 "# The filter on the right is invoked with the repository and a list \n", 149 "# of files to check. A non-zero exit of the filter program will \n", 150 "# cause the commit to be aborted.\n", 151 "#\n", 152 "# The first entry on a line is a regular expression which is tested\n", 153 "# against the directory that the change is being committed to, relative\n", 154 "# to the $CVSROOT. For the first match that is found, then the remainder\n", 155 "# of the line is the name of the filter to run.\n", 156 "#\n", 157 "# If the repository name does not match any of the regular expressions in this\n", 158 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 159 "#\n", 160 "# If the name \"ALL\" appears as a regular expression it is always used\n", 161 "# in addition to the first matching regex or \"DEFAULT\".\n", 162 NULL 163 }; 164 165 static const char *const taginfo_contents[] = { 166 "# The \"taginfo\" file is used to control pre-tag checks.\n", 167 "# The filter on the right is invoked with the following arguments:\n", 168 "#\n", 169 "# $1 -- tagname\n", 170 "# $2 -- operation \"add\" for tag, \"mov\" for tag -F, and \"del\" for tag -d\n", 171 "# $3 -- repository\n", 172 "# $4-> file revision [file revision ...]\n", 173 "#\n", 174 "# A non-zero exit of the filter program will cause the tag to be aborted.\n", 175 "#\n", 176 "# The first entry on a line is a regular expression which is tested\n", 177 "# against the directory that the change is being committed to, relative\n", 178 "# to the $CVSROOT. For the first match that is found, then the remainder\n", 179 "# of the line is the name of the filter to run.\n", 180 "#\n", 181 "# If the repository name does not match any of the regular expressions in this\n", 182 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 183 "#\n", 184 "# If the name \"ALL\" appears as a regular expression it is always used\n", 185 "# in addition to the first matching regex or \"DEFAULT\".\n", 186 NULL 187 }; 188 189 static const char *const checkoutlist_contents[] = { 190 "# The \"checkoutlist\" file is used to support additional version controlled\n", 191 "# administrative files in $CVSROOT/CVSROOT, such as template files.\n", 192 "#\n", 193 "# The first entry on a line is a filename which will be checked out from\n", 194 "# the corresponding RCS file in the $CVSROOT/CVSROOT directory.\n", 195 "# The remainder of the line is an error message to use if the file cannot\n", 196 "# be checked out.\n", 197 "#\n", 198 "# File format:\n", 199 "#\n", 200 "# [<whitespace>]<filename><whitespace><error message><end-of-line>\n", 201 "#\n", 202 "# comment lines begin with '#'\n", 203 NULL 204 }; 205 206 static const char *const cvswrappers_contents[] = { 207 "# This file affects handling of files based on their names.\n", 208 "#\n", 209 "# The -t/-f options allow one to treat directories of files\n", 210 "# as a single file, or to transform a file in other ways on\n", 211 "# its way in and out of CVS.\n", 212 "#\n", 213 "# The -m option specifies whether CVS attempts to merge files.\n", 214 "#\n", 215 "# The -k option specifies keyword expansion (e.g. -kb for binary).\n", 216 "#\n", 217 "# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)\n", 218 "#\n", 219 "# wildcard [option value][option value]...\n", 220 "#\n", 221 "# where option is one of\n", 222 "# -f from cvs filter value: path to filter\n", 223 "# -t to cvs filter value: path to filter\n", 224 "# -m update methodology value: MERGE or COPY\n", 225 "# -k expansion mode value: b, o, kkv, &c\n", 226 "#\n", 227 "# and value is a single-quote delimited value.\n", 228 "# For example:\n", 229 "#*.gif -k 'b'\n", 230 NULL 231 }; 232 233 static const char *const notify_contents[] = { 234 "# The \"notify\" file controls where notifications from watches set by\n", 235 "# \"cvs watch add\" or \"cvs edit\" are sent. The first entry on a line is\n", 236 "# a regular expression which is tested against the directory that the\n", 237 "# change is being made to, relative to the $CVSROOT. If it matches,\n", 238 "# then the remainder of the line is a filter program that should contain\n", 239 "# one occurrence of %s for the user to notify, and information on its\n", 240 "# standard input.\n", 241 "#\n", 242 "# \"ALL\" or \"DEFAULT\" can be used in place of the regular expression.\n", 243 "#\n", 244 "# For example:\n", 245 "#ALL mail %s -s \"CVS notification\"\n", 246 NULL 247 }; 248 249 static const char *const modules_contents[] = { 250 "# Three different line formats are valid:\n", 251 "# key -a aliases...\n", 252 "# key [options] directory\n", 253 "# key [options] directory files...\n", 254 "#\n", 255 "# Where \"options\" are composed of:\n", 256 "# -i prog Run \"prog\" on \"cvs commit\" from top-level of module.\n", 257 "# -o prog Run \"prog\" on \"cvs checkout\" of module.\n", 258 "# -e prog Run \"prog\" on \"cvs export\" of module.\n", 259 "# -t prog Run \"prog\" on \"cvs rtag\" of module.\n", 260 "# -u prog Run \"prog\" on \"cvs update\" of module.\n", 261 "# -d dir Place module in directory \"dir\" instead of module name.\n", 262 "# -l Top-level directory only -- do not recurse.\n", 263 "#\n", 264 "# NOTE: If you change any of the \"Run\" options above, you'll have to\n", 265 "# release and re-checkout any working directories of these modules.\n", 266 "#\n", 267 "# And \"directory\" is a path to a directory relative to $CVSROOT.\n", 268 "#\n", 269 "# The \"-a\" option specifies an alias. An alias is interpreted as if\n", 270 "# everything on the right of the \"-a\" had been typed on the command line.\n", 271 "#\n", 272 "# You can encode a module within a module by using the special '&'\n", 273 "# character to interpose another module into the current module. This\n", 274 "# can be useful for creating a module that consists of many directories\n", 275 "# spread out over the entire source repository.\n", 276 NULL 277 }; 278 279 static const char *const config_contents[] = { 280 "# Set this to \"no\" if pserver shouldn't check system users/passwords\n", 281 "#SystemAuth=no\n", 282 "\n", 283 "# Put CVS lock files in this directory rather than directly in the repository.\n", 284 "#LockDir=/var/lock/cvs\n", 285 "\n", 286 #ifdef PRESERVE_PERMISSIONS_SUPPORT 287 "# Set `PreservePermissions' to `yes' to save file status information\n", 288 "# in the repository.\n", 289 "#PreservePermissions=no\n", 290 "\n", 291 #endif 292 "# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top\n", 293 "# level of the new working directory when using the `cvs checkout'\n", 294 "# command.\n", 295 "#TopLevelAdmin=no\n", 296 "\n", 297 "# Set this to the name of a local tag to use in addition to Id\n", 298 "#tag=OurTag\n", 299 "\n", 300 "# Set this to the default umask to use when creating files and directories\n", 301 "#umask=002\n", 302 "\n", 303 "# Set this to the default data resource limit to use\n", 304 "#dlimit=65536\n", 305 "\n", 306 "# Set `LogHistory' to `all' or `TOFEWGCMAR' to log all transactions to the\n", 307 "# history file, or a subset as needed (ie `TMAR' logs all write operations)\n", 308 "#LogHistory=TOFEWGCMAR\n", 309 NULL 310 }; 311 312 static const struct admin_file filelist[] = { 313 {CVSROOTADM_LOGINFO, 314 "no logging of 'cvs commit' messages is done without a %s file", 315 &loginfo_contents[0]}, 316 {CVSROOTADM_RCSINFO, 317 "a %s file can be used to configure 'cvs commit' templates", 318 rcsinfo_contents}, 319 {CVSROOTADM_EDITINFO, 320 "a %s file can be used to validate log messages", 321 editinfo_contents}, 322 {CVSROOTADM_VERIFYMSG, 323 "a %s file can be used to validate log messages", 324 verifymsg_contents}, 325 {CVSROOTADM_COMMITINFO, 326 "a %s file can be used to configure 'cvs commit' checking", 327 commitinfo_contents}, 328 {CVSROOTADM_TAGINFO, 329 "a %s file can be used to configure 'cvs tag' checking", 330 taginfo_contents}, 331 {CVSROOTADM_IGNORE, 332 "a %s file can be used to specify files to ignore", 333 NULL}, 334 {CVSROOTADM_CHECKOUTLIST, 335 "a %s file can specify extra CVSROOT files to auto-checkout", 336 checkoutlist_contents}, 337 {CVSROOTADM_WRAPPER, 338 "a %s file can be used to specify files to treat as wrappers", 339 cvswrappers_contents}, 340 {CVSROOTADM_NOTIFY, 341 "a %s file can be used to specify where notifications go", 342 notify_contents}, 343 {CVSROOTADM_MODULES, 344 /* modules is special-cased in mkmodules. */ 345 NULL, 346 modules_contents}, 347 {CVSROOTADM_READERS, 348 "a %s file specifies read-only users", 349 NULL}, 350 {CVSROOTADM_WRITERS, 351 "a %s file specifies read/write users", 352 NULL}, 353 354 /* Some have suggested listing CVSROOTADM_PASSWD here too. This 355 would mean that CVS commands which operate on the 356 CVSROOTADM_PASSWD file would transmit hashed passwords over the 357 net. This might seem to be no big deal, as pserver normally 358 transmits cleartext passwords, but the difference is that 359 CVSROOTADM_PASSWD contains *all* passwords, not just the ones 360 currently being used. For example, it could be too easy to 361 accidentally give someone readonly access to CVSROOTADM_PASSWD 362 (e.g. via anonymous CVS or cvsweb), and then if there are any 363 guessable passwords for read/write access (usually there will be) 364 they get read/write access. 365 366 Another worry is the implications of storing old passwords--if 367 someone used a password in the past they might be using it 368 elsewhere, using a similar password, etc, and so saving old 369 passwords, even hashed, is probably not a good idea. */ 370 371 {CVSROOTADM_CONFIG, 372 "a %s file configures various behaviors", 373 config_contents}, 374 {NULL, NULL, NULL} 375 }; 376 377 /* Rebuild the checked out administrative files in directory DIR. */ 378 int 379 mkmodules (dir) 380 char *dir; 381 { 382 struct saved_cwd cwd; 383 char *temp; 384 char *cp, *last, *fname; 385 #ifdef MY_NDBM 386 DBM *db; 387 #endif 388 FILE *fp; 389 char *line = NULL; 390 size_t line_allocated = 0; 391 const struct admin_file *fileptr; 392 393 if (noexec) 394 return 0; 395 396 if (save_cwd (&cwd)) 397 error_exit (); 398 399 if ( CVS_CHDIR (dir) < 0) 400 error (1, errno, "cannot chdir to %s", dir); 401 402 /* 403 * First, do the work necessary to update the "modules" database. 404 */ 405 temp = make_tempfile (); 406 switch (checkout_file (CVSROOTADM_MODULES, temp)) 407 { 408 409 case 0: /* everything ok */ 410 #ifdef MY_NDBM 411 /* open it, to generate any duplicate errors */ 412 if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL) 413 dbm_close (db); 414 #else 415 write_dbmfile (temp); 416 rename_dbmfile (temp); 417 #endif 418 rename_rcsfile (temp, CVSROOTADM_MODULES); 419 break; 420 421 default: 422 error (0, 0, 423 "'cvs checkout' is less functional without a %s file", 424 CVSROOTADM_MODULES); 425 break; 426 } /* switch on checkout_file() */ 427 428 if (unlink_file (temp) < 0 429 && !existence_error (errno)) 430 error (0, errno, "cannot remove %s", temp); 431 free (temp); 432 433 /* Checkout the files that need it in CVSROOT dir */ 434 for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) { 435 if (fileptr->errormsg == NULL) 436 continue; 437 temp = make_tempfile (); 438 if (checkout_file (fileptr->filename, temp) == 0) 439 rename_rcsfile (temp, fileptr->filename); 440 #if 0 441 /* 442 * If there was some problem other than the file not existing, 443 * checkout_file already printed a real error message. If the 444 * file does not exist, it is harmless--it probably just means 445 * that the repository was created with an old version of CVS 446 * which didn't have so many files in CVSROOT. 447 */ 448 else if (fileptr->errormsg) 449 error (0, 0, fileptr->errormsg, fileptr->filename); 450 #endif 451 if (unlink_file (temp) < 0 452 && !existence_error (errno)) 453 error (0, errno, "cannot remove %s", temp); 454 free (temp); 455 } 456 457 fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r"); 458 if (fp) 459 { 460 /* 461 * File format: 462 * [<whitespace>]<filename><whitespace><error message><end-of-line> 463 * 464 * comment lines begin with '#' 465 */ 466 while (getline (&line, &line_allocated, fp) >= 0) 467 { 468 /* skip lines starting with # */ 469 if (line[0] == '#') 470 continue; 471 472 if ((last = strrchr (line, '\n')) != NULL) 473 *last = '\0'; /* strip the newline */ 474 475 /* Skip leading white space. */ 476 for (fname = line; 477 *fname && isspace ((unsigned char) *fname); 478 fname++) 479 ; 480 481 /* Find end of filename. */ 482 for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++) 483 ; 484 *cp = '\0'; 485 486 temp = make_tempfile (); 487 if (checkout_file (fname, temp) == 0) 488 { 489 rename_rcsfile (temp, fname); 490 } 491 else 492 { 493 for (cp++; 494 cp < last && *last && isspace ((unsigned char) *last); 495 cp++) 496 ; 497 if (cp < last && *cp) 498 error (0, 0, cp, fname); 499 } 500 if (unlink_file (temp) < 0 501 && !existence_error (errno)) 502 error (0, errno, "cannot remove %s", temp); 503 free (temp); 504 } 505 if (line) 506 free (line); 507 if (ferror (fp)) 508 error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST); 509 if (fclose (fp) < 0) 510 error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST); 511 } 512 else 513 { 514 /* Error from CVS_FOPEN. */ 515 if (!existence_error (errno)) 516 error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST); 517 } 518 519 if (restore_cwd (&cwd, NULL)) 520 error_exit (); 521 free_cwd (&cwd); 522 523 return (0); 524 } 525 526 /* 527 * Yeah, I know, there are NFS race conditions here. 528 */ 529 static char * 530 make_tempfile () 531 { 532 static int seed = 0; 533 int fd; 534 char *temp; 535 536 if (seed == 0) 537 seed = getpid (); 538 temp = xmalloc (sizeof (BAKPREFIX) + 40); 539 while (1) 540 { 541 (void) sprintf (temp, "%s%d", BAKPREFIX, seed++); 542 if ((fd = CVS_OPEN (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1) 543 break; 544 if (errno != EEXIST) 545 error (1, errno, "cannot create temporary file %s", temp); 546 } 547 if (close(fd) < 0) 548 error(1, errno, "cannot close temporary file %s", temp); 549 return temp; 550 } 551 552 /* Get a file. If the file does not exist, return 1 silently. If 553 there is an error, print a message and return 1 (FIXME: probably 554 not a very clean convention). On success, return 0. */ 555 556 static int 557 checkout_file (file, temp) 558 char *file; 559 char *temp; 560 { 561 char *rcs; 562 RCSNode *rcsnode; 563 int retcode = 0; 564 565 if (noexec) 566 return 0; 567 568 rcs = xmalloc (strlen (file) + 5); 569 strcpy (rcs, file); 570 strcat (rcs, RCSEXT); 571 if (!isfile (rcs)) 572 { 573 free (rcs); 574 return (1); 575 } 576 rcsnode = RCS_parsercsfile (rcs); 577 retcode = RCS_checkout (rcsnode, NULL, NULL, NULL, NULL, temp, 578 (RCSCHECKOUTPROC) NULL, (void *) NULL); 579 if (retcode != 0) 580 { 581 /* Probably not necessary (?); RCS_checkout already printed a 582 message. */ 583 error (0, 0, "failed to check out %s file", 584 file); 585 } 586 freercsnode (&rcsnode); 587 free (rcs); 588 return (retcode); 589 } 590 591 #ifndef MY_NDBM 592 593 static void 594 write_dbmfile (temp) 595 char *temp; 596 { 597 char line[DBLKSIZ], value[DBLKSIZ]; 598 FILE *fp; 599 DBM *db; 600 char *cp, *vp; 601 datum key, val; 602 int len, cont, err = 0; 603 604 fp = open_file (temp, "r"); 605 if ((db = dbm_open (temp, O_RDWR | O_CREAT | O_TRUNC, 0666)) == NULL) 606 error (1, errno, "cannot open dbm file %s for creation", temp); 607 for (cont = 0; fgets (line, sizeof (line), fp) != NULL;) 608 { 609 if ((cp = strrchr (line, '\n')) != NULL) 610 *cp = '\0'; /* strip the newline */ 611 612 /* 613 * Add the line to the value, at the end if this is a continuation 614 * line; otherwise at the beginning, but only after any trailing 615 * backslash is removed. 616 */ 617 vp = value; 618 if (cont) 619 vp += strlen (value); 620 621 /* 622 * See if the line we read is a continuation line, and strip the 623 * backslash if so. 624 */ 625 len = strlen (line); 626 if (len > 0) 627 cp = &line[len - 1]; 628 else 629 cp = line; 630 if (*cp == '\\') 631 { 632 cont = 1; 633 *cp = '\0'; 634 } 635 else 636 { 637 cont = 0; 638 } 639 (void) strcpy (vp, line); 640 if (value[0] == '#') 641 continue; /* comment line */ 642 vp = value; 643 while (*vp && isspace ((unsigned char) *vp)) 644 vp++; 645 if (*vp == '\0') 646 continue; /* empty line */ 647 648 /* 649 * If this was not a continuation line, add the entry to the database 650 */ 651 if (!cont) 652 { 653 key.dptr = vp; 654 while (*vp && !isspace ((unsigned char) *vp)) 655 vp++; 656 key.dsize = vp - key.dptr; 657 *vp++ = '\0'; /* NULL terminate the key */ 658 while (*vp && isspace ((unsigned char) *vp)) 659 vp++; /* skip whitespace to value */ 660 if (*vp == '\0') 661 { 662 error (0, 0, "warning: NULL value for key `%s'", key.dptr); 663 continue; 664 } 665 val.dptr = vp; 666 val.dsize = strlen (vp); 667 if (dbm_store (db, key, val, DBM_INSERT) == 1) 668 { 669 error (0, 0, "duplicate key found for `%s'", key.dptr); 670 err++; 671 } 672 } 673 } 674 dbm_close (db); 675 if (fclose (fp) < 0) 676 error (0, errno, "cannot close %s", temp); 677 if (err) 678 { 679 /* I think that the size of the buffer needed here is 680 just determined by sizeof (CVSROOTADM_MODULES), the 681 filenames created by make_tempfile, and other things that won't 682 overflow. */ 683 char dotdir[50], dotpag[50], dotdb[50]; 684 685 (void) sprintf (dotdir, "%s.dir", temp); 686 (void) sprintf (dotpag, "%s.pag", temp); 687 (void) sprintf (dotdb, "%s.db", temp); 688 if (unlink_file (dotdir) < 0 689 && !existence_error (errno)) 690 error (0, errno, "cannot remove %s", dotdir); 691 if (unlink_file (dotpag) < 0 692 && !existence_error (errno)) 693 error (0, errno, "cannot remove %s", dotpag); 694 if (unlink_file (dotdb) < 0 695 && !existence_error (errno)) 696 error (0, errno, "cannot remove %s", dotdb); 697 error (1, 0, "DBM creation failed; correct above errors"); 698 } 699 } 700 701 static void 702 rename_dbmfile (temp) 703 char *temp; 704 { 705 /* I think that the size of the buffer needed here is 706 just determined by sizeof (CVSROOTADM_MODULES), the 707 filenames created by make_tempfile, and other things that won't 708 overflow. */ 709 char newdir[50], newpag[50], newdb[50]; 710 char dotdir[50], dotpag[50], dotdb[50]; 711 char bakdir[50], bakpag[50], bakdb[50]; 712 713 int dir1_errno = 0, pag1_errno = 0, db1_errno = 0; 714 int dir2_errno = 0, pag2_errno = 0, db2_errno = 0; 715 int dir3_errno = 0, pag3_errno = 0, db3_errno = 0; 716 717 (void) sprintf (dotdir, "%s.dir", CVSROOTADM_MODULES); 718 (void) sprintf (dotpag, "%s.pag", CVSROOTADM_MODULES); 719 (void) sprintf (dotdb, "%s.db", CVSROOTADM_MODULES); 720 (void) sprintf (bakdir, "%s%s.dir", BAKPREFIX, CVSROOTADM_MODULES); 721 (void) sprintf (bakpag, "%s%s.pag", BAKPREFIX, CVSROOTADM_MODULES); 722 (void) sprintf (bakdb, "%s%s.db", BAKPREFIX, CVSROOTADM_MODULES); 723 (void) sprintf (newdir, "%s.dir", temp); 724 (void) sprintf (newpag, "%s.pag", temp); 725 (void) sprintf (newdb, "%s.db", temp); 726 727 (void) chmod (newdir, 0666); 728 (void) chmod (newpag, 0666); 729 (void) chmod (newdb, 0666); 730 731 /* don't mess with me */ 732 SIG_beginCrSect (); 733 734 /* rm .#modules.dir .#modules.pag */ 735 if (unlink_file (bakdir) < 0) 736 dir1_errno = errno; 737 if (unlink_file (bakpag) < 0) 738 pag1_errno = errno; 739 if (unlink_file (bakdb) < 0) 740 db1_errno = errno; 741 742 /* mv modules.dir .#modules.dir */ 743 if (CVS_RENAME (dotdir, bakdir) < 0) 744 dir2_errno = errno; 745 /* mv modules.pag .#modules.pag */ 746 if (CVS_RENAME (dotpag, bakpag) < 0) 747 pag2_errno = errno; 748 /* mv modules.db .#modules.db */ 749 if (CVS_RENAME (dotdb, bakdb) < 0) 750 db2_errno = errno; 751 752 /* mv "temp".dir modules.dir */ 753 if (CVS_RENAME (newdir, dotdir) < 0) 754 dir3_errno = errno; 755 /* mv "temp".pag modules.pag */ 756 if (CVS_RENAME (newpag, dotpag) < 0) 757 pag3_errno = errno; 758 /* mv "temp".db modules.db */ 759 if (CVS_RENAME (newdb, dotdb) < 0) 760 db3_errno = errno; 761 762 /* OK -- make my day */ 763 SIG_endCrSect (); 764 765 /* I didn't want to call error() when we had signals blocked 766 (unnecessary?), but do it now. */ 767 if (dir1_errno && !existence_error (dir1_errno)) 768 error (0, dir1_errno, "cannot remove %s", bakdir); 769 if (pag1_errno && !existence_error (pag1_errno)) 770 error (0, pag1_errno, "cannot remove %s", bakpag); 771 if (db1_errno && !existence_error (db1_errno)) 772 error (0, db1_errno, "cannot remove %s", bakdb); 773 774 if (dir2_errno && !existence_error (dir2_errno)) 775 error (0, dir2_errno, "cannot remove %s", bakdir); 776 if (pag2_errno && !existence_error (pag2_errno)) 777 error (0, pag2_errno, "cannot remove %s", bakpag); 778 if (db2_errno && !existence_error (db2_errno)) 779 error (0, db2_errno, "cannot remove %s", bakdb); 780 781 if (dir3_errno && !existence_error (dir3_errno)) 782 error (0, dir3_errno, "cannot remove %s", bakdir); 783 if (pag3_errno && !existence_error (pag3_errno)) 784 error (0, pag3_errno, "cannot remove %s", bakpag); 785 if (db3_errno && !existence_error (db3_errno)) 786 error (0, db3_errno, "cannot remove %s", bakdb); 787 } 788 789 #endif /* !MY_NDBM */ 790 791 static void 792 rename_rcsfile (temp, real) 793 char *temp; 794 char *real; 795 { 796 char *bak; 797 struct stat statbuf; 798 char *rcs; 799 800 /* Set "x" bits if set in original. */ 801 rcs = xmalloc (strlen (real) + sizeof (RCSEXT) + 10); 802 (void) sprintf (rcs, "%s%s", real, RCSEXT); 803 statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */ 804 if (CVS_STAT (rcs, &statbuf) < 0 805 && !existence_error (errno)) 806 error (0, errno, "cannot stat %s", rcs); 807 free (rcs); 808 809 if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0) 810 error (0, errno, "warning: cannot chmod %s", temp); 811 bak = xmalloc (strlen (real) + sizeof (BAKPREFIX) + 10); 812 (void) sprintf (bak, "%s%s", BAKPREFIX, real); 813 814 /* rm .#loginfo */ 815 if (unlink_file (bak) < 0 816 && !existence_error (errno)) 817 error (0, errno, "cannot remove %s", bak); 818 819 /* mv loginfo .#loginfo */ 820 if (CVS_RENAME (real, bak) < 0 821 && !existence_error (errno)) 822 error (0, errno, "cannot rename %s to %s", real, bak); 823 824 /* mv "temp" loginfo */ 825 if (CVS_RENAME (temp, real) < 0 826 && !existence_error (errno)) 827 error (0, errno, "cannot rename %s to %s", temp, real); 828 829 free (bak); 830 } 831 832 const char *const init_usage[] = { 833 "Usage: %s %s\n", 834 "(Specify the --help global option for a list of other help options)\n", 835 NULL 836 }; 837 838 int 839 init (argc, argv) 840 int argc; 841 char **argv; 842 { 843 /* Name of CVSROOT directory. */ 844 char *adm; 845 /* Name of this administrative file. */ 846 char *info; 847 /* Name of ,v file for this administrative file. */ 848 char *info_v; 849 /* Exit status. */ 850 int err; 851 852 const struct admin_file *fileptr; 853 854 umask (cvsumask); 855 856 if (argc == -1 || argc > 1) 857 usage (init_usage); 858 859 #ifdef CLIENT_SUPPORT 860 if (client_active) 861 { 862 start_server (); 863 864 ign_setup (); 865 send_init_command (); 866 return get_responses_and_close (); 867 } 868 #endif /* CLIENT_SUPPORT */ 869 870 /* Note: we do *not* create parent directories as needed like the 871 old cvsinit.sh script did. Few utilities do that, and a 872 non-existent parent directory is as likely to be a typo as something 873 which needs to be created. */ 874 mkdir_if_needed (CVSroot_directory); 875 876 adm = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + 10); 877 strcpy (adm, CVSroot_directory); 878 strcat (adm, "/"); 879 strcat (adm, CVSROOTADM); 880 mkdir_if_needed (adm); 881 882 /* This is needed because we pass "fileptr->filename" not "info" 883 to add_rcs_file below. I think this would be easy to change, 884 thus nuking the need for CVS_CHDIR here, but I haven't looked 885 closely (e.g. see wrappers calls within add_rcs_file). */ 886 if ( CVS_CHDIR (adm) < 0) 887 error (1, errno, "cannot change to directory %s", adm); 888 889 /* Make Emptydir so it's there if we need it */ 890 mkdir_if_needed (CVSNULLREPOS); 891 892 /* 80 is long enough for all the administrative file names, plus 893 "/" and so on. */ 894 info = xmalloc (strlen (adm) + 80); 895 info_v = xmalloc (strlen (adm) + 80); 896 for (fileptr = filelist; fileptr && fileptr->filename; ++fileptr) 897 { 898 if (fileptr->contents == NULL) 899 continue; 900 strcpy (info, adm); 901 strcat (info, "/"); 902 strcat (info, fileptr->filename); 903 strcpy (info_v, info); 904 strcat (info_v, RCSEXT); 905 if (isfile (info_v)) 906 /* We will check out this file in the mkmodules step. 907 Nothing else is required. */ 908 ; 909 else 910 { 911 int retcode; 912 913 if (!isfile (info)) 914 { 915 FILE *fp; 916 const char * const *p; 917 918 fp = open_file (info, "w"); 919 for (p = fileptr->contents; *p != NULL; ++p) 920 if (fputs (*p, fp) < 0) 921 error (1, errno, "cannot write %s", info); 922 if (fclose (fp) < 0) 923 error (1, errno, "cannot close %s", info); 924 } 925 /* The message used to say " of " and fileptr->filename after 926 "initial checkin" but I fail to see the point as we know what 927 file it is from the name. */ 928 retcode = add_rcs_file ("initial checkin", info_v, 929 fileptr->filename, "1.1", NULL, 930 931 /* No vendor branch. */ 932 NULL, NULL, 0, NULL, 933 934 NULL, 0, NULL); 935 if (retcode != 0) 936 /* add_rcs_file already printed an error message. */ 937 err = 1; 938 } 939 } 940 941 /* Turn on history logging by default. The user can remove the file 942 to disable it. */ 943 strcpy (info, adm); 944 strcat (info, "/"); 945 strcat (info, CVSROOTADM_HISTORY); 946 if (!isfile (info)) 947 { 948 FILE *fp; 949 950 fp = open_file (info, "w"); 951 if (fclose (fp) < 0) 952 error (1, errno, "cannot close %s", info); 953 954 /* Make the new history file world-writeable, since every CVS 955 user will need to be able to write to it. We use chmod() 956 because xchmod() is too shy. */ 957 chmod (info, 0666); 958 } 959 960 /* Make an empty val-tags file to prevent problems creating it later. */ 961 strcpy (info, adm); 962 strcat (info, "/"); 963 strcat (info, CVSROOTADM_VALTAGS); 964 if (!isfile (info)) 965 { 966 FILE *fp; 967 968 fp = open_file (info, "w"); 969 if (fclose (fp) < 0) 970 error (1, errno, "cannot close %s", info); 971 972 /* Make the new val-tags file world-writeable, since every CVS 973 user will need to be able to write to it. We use chmod() 974 because xchmod() is too shy. */ 975 chmod (info, 0666); 976 } 977 978 free (info); 979 free (info_v); 980 981 mkmodules (adm); 982 983 free (adm); 984 return 0; 985 } 986