mkmodules.c revision 25843
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 16static int checkout_file PROTO((char *file, char *temp)); 17static char *make_tempfile PROTO((void)); 18static void rename_rcsfile PROTO((char *temp, char *real)); 19 20#ifndef MY_NDBM 21static void rename_dbmfile PROTO((char *temp)); 22static void write_dbmfile PROTO((char *temp)); 23#endif /* !MY_NDBM */ 24 25/* Structure which describes an administrative file. */ 26struct 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 49static 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 79static 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 96static 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 121static 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 146static 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 165static 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 189static 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 206static const char *const cvswrappers_contents[] = { 207 "# This file describes wrappers and other binary files to CVS.\n", 208 "#\n", 209 "# Wrappers are the concept where directories of files are to be\n", 210 "# treated as a single file. The intended use is to wrap up a wrapper\n", 211 "# into a single tar such that the tar archive can be treated as a\n", 212 "# single binary file in CVS.\n", 213 "#\n", 214 "# To solve the problem effectively, it was also necessary to be able to\n", 215 "# prevent rcsmerge from merging these files.\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 "#\n", 226 "# and value is a single-quote delimited value.\n", 227 "#\n", 228 "# For example:\n", 229 NULL 230}; 231 232static const char *const notify_contents[] = { 233 "# The \"notify\" file controls where notifications from watches set by\n", 234 "# \"cvs watch add\" or \"cvs edit\" are sent. The first entry on a line is\n", 235 "# a regular expression which is tested against the directory that the\n", 236 "# change is being made to, relative to the $CVSROOT. If it matches,\n", 237 "# then the remainder of the line is a filter program that should contain\n", 238 "# one occurrence of %s for the user to notify, and information on its\n", 239 "# standard input.\n", 240 "#\n", 241 "# \"ALL\" or \"DEFAULT\" can be used in place of the regular expression.\n", 242 "#\n", 243 "# For example:\n", 244 "#ALL mail %s -s \"CVS notification\"\n", 245 NULL 246}; 247 248static const char *const modules_contents[] = { 249 "# Three different line formats are valid:\n", 250 "# key -a aliases...\n", 251 "# key [options] directory\n", 252 "# key [options] directory files...\n", 253 "#\n", 254 "# Where \"options\" are composed of:\n", 255 "# -i prog Run \"prog\" on \"cvs commit\" from top-level of module.\n", 256 "# -o prog Run \"prog\" on \"cvs checkout\" of module.\n", 257 "# -e prog Run \"prog\" on \"cvs export\" of module.\n", 258 "# -t prog Run \"prog\" on \"cvs rtag\" of module.\n", 259 "# -u prog Run \"prog\" on \"cvs update\" of module.\n", 260 "# -d dir Place module in directory \"dir\" instead of module name.\n", 261 "# -l Top-level directory only -- do not recurse.\n", 262 "#\n", 263 "# NOTE: If you change any of the \"Run\" options above, you'll have to\n", 264 "# release and re-checkout any working directories of these modules.\n", 265 "#\n", 266 "# And \"directory\" is a path to a directory relative to $CVSROOT.\n", 267 "#\n", 268 "# The \"-a\" option specifies an alias. An alias is interpreted as if\n", 269 "# everything on the right of the \"-a\" had been typed on the command line.\n", 270 "#\n", 271 "# You can encode a module within a module by using the special '&'\n", 272 "# character to interpose another module into the current module. This\n", 273 "# can be useful for creating a module that consists of many directories\n", 274 "# spread out over the entire source repository.\n", 275 NULL 276}; 277 278static const struct admin_file filelist[] = { 279 {CVSROOTADM_LOGINFO, 280 "no logging of 'cvs commit' messages is done without a %s file", 281 &loginfo_contents[0]}, 282 {CVSROOTADM_RCSINFO, 283 "a %s file can be used to configure 'cvs commit' templates", 284 rcsinfo_contents}, 285 {CVSROOTADM_EDITINFO, 286 "a %s file can be used to validate log messages", 287 editinfo_contents}, 288 {CVSROOTADM_VERIFYMSG, 289 "a %s file can be used to validate log messages", 290 verifymsg_contents}, 291 {CVSROOTADM_COMMITINFO, 292 "a %s file can be used to configure 'cvs commit' checking", 293 commitinfo_contents}, 294 {CVSROOTADM_TAGINFO, 295 "a %s file can be used to configure 'cvs tag' checking", 296 taginfo_contents}, 297 {CVSROOTADM_IGNORE, 298 "a %s file can be used to specify files to ignore", 299 NULL}, 300 {CVSROOTADM_CHECKOUTLIST, 301 "a %s file can specify extra CVSROOT files to auto-checkout", 302 checkoutlist_contents}, 303 {CVSROOTADM_WRAPPER, 304 "a %s file can be used to specify files to treat as wrappers", 305 cvswrappers_contents}, 306 {CVSROOTADM_NOTIFY, 307 "a %s file can be used to specify where notifications go", 308 notify_contents}, 309 {CVSROOTADM_MODULES, 310 /* modules is special-cased in mkmodules. */ 311 NULL, 312 modules_contents}, 313 {CVSROOTADM_READERS, 314 "a %s file specifies read-only users", 315 NULL}, 316 {CVSROOTADM_WRITERS, 317 "a %s file specifies read/write users", 318 NULL}, 319 /* Some have suggested listing CVSROOTADM_PASSWD here too. The 320 security implications of transmitting hashed passwords over the 321 net are no worse than transmitting cleartext passwords which pserver 322 does, so this isn't a problem. But I'm worried about the implications 323 of storing old passwords--if someone used a password in the past 324 they might be using it elsewhere, using a similar password, etc, 325 and so it doesn't seem to me like we should be saving old passwords, 326 even hashed. */ 327 {NULL, NULL} 328}; 329 330/* Rebuild the checked out administrative files in directory DIR. */ 331int 332mkmodules (dir) 333 char *dir; 334{ 335 struct saved_cwd cwd; 336 char *temp; 337 char *cp, *last, *fname; 338#ifdef MY_NDBM 339 DBM *db; 340#endif 341 FILE *fp; 342 char *line = NULL; 343 size_t line_allocated = 0; 344 const struct admin_file *fileptr; 345 346 if (save_cwd (&cwd)) 347 error_exit (); 348 349 if ( CVS_CHDIR (dir) < 0) 350 error (1, errno, "cannot chdir to %s", dir); 351 352 /* 353 * First, do the work necessary to update the "modules" database. 354 */ 355 temp = make_tempfile (); 356 switch (checkout_file (CVSROOTADM_MODULES, temp)) 357 { 358 359 case 0: /* everything ok */ 360#ifdef MY_NDBM 361 /* open it, to generate any duplicate errors */ 362 if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL) 363 dbm_close (db); 364#else 365 write_dbmfile (temp); 366 rename_dbmfile (temp); 367#endif 368 rename_rcsfile (temp, CVSROOTADM_MODULES); 369 break; 370 371 case -1: /* fork failed */ 372 (void) unlink_file (temp); 373 error (1, errno, "cannot check out %s", CVSROOTADM_MODULES); 374 /* NOTREACHED */ 375 376 default: 377 error (0, 0, 378 "'cvs checkout' is less functional without a %s file", 379 CVSROOTADM_MODULES); 380 break; 381 } /* switch on checkout_file() */ 382 383 (void) unlink_file (temp); 384 free (temp); 385 386 /* Checkout the files that need it in CVSROOT dir */ 387 for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) { 388 if (fileptr->errormsg == NULL) 389 continue; 390 temp = make_tempfile (); 391 if (checkout_file (fileptr->filename, temp) == 0) 392 rename_rcsfile (temp, fileptr->filename); 393#if 0 394 /* 395 * If there was some problem other than the file not existing, 396 * checkout_file already printed a real error message. If the 397 * file does not exist, it is harmless--it probably just means 398 * that the repository was created with an old version of CVS 399 * which didn't have so many files in CVSROOT. 400 */ 401 else if (fileptr->errormsg) 402 error (0, 0, fileptr->errormsg, fileptr->filename); 403#endif 404 (void) unlink_file (temp); 405 free (temp); 406 } 407 408 fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r"); 409 if (fp) 410 { 411 /* 412 * File format: 413 * [<whitespace>]<filename><whitespace><error message><end-of-line> 414 * 415 * comment lines begin with '#' 416 */ 417 while (getline (&line, &line_allocated, fp) >= 0) 418 { 419 /* skip lines starting with # */ 420 if (line[0] == '#') 421 continue; 422 423 if ((last = strrchr (line, '\n')) != NULL) 424 *last = '\0'; /* strip the newline */ 425 426 /* Skip leading white space. */ 427 for (fname = line; *fname && isspace(*fname); fname++) 428 ; 429 430 /* Find end of filename. */ 431 for (cp = fname; *cp && !isspace(*cp); cp++) 432 ; 433 *cp = '\0'; 434 435 temp = make_tempfile (); 436 if (checkout_file (fname, temp) == 0) 437 { 438 rename_rcsfile (temp, fname); 439 } 440 else 441 { 442 for (cp++; cp < last && *last && isspace(*last); cp++) 443 ; 444 if (cp < last && *cp) 445 error (0, 0, cp, fname); 446 } 447 free (temp); 448 } 449 if (line) 450 free (line); 451 if (ferror (fp)) 452 error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST); 453 if (fclose (fp) < 0) 454 error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST); 455 } 456 else 457 { 458 /* Error from CVS_FOPEN. */ 459 if (!existence_error (errno)) 460 error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST); 461 } 462 463 if (restore_cwd (&cwd, NULL)) 464 error_exit (); 465 free_cwd (&cwd); 466 467 return (0); 468} 469 470/* 471 * Yeah, I know, there are NFS race conditions here. 472 */ 473static char * 474make_tempfile () 475{ 476 static int seed = 0; 477 int fd; 478 char *temp; 479 480 if (seed == 0) 481 seed = getpid (); 482 temp = xmalloc (sizeof (BAKPREFIX) + 40); 483 while (1) 484 { 485 (void) sprintf (temp, "%s%d", BAKPREFIX, seed++); 486 if ((fd = CVS_OPEN (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1) 487 break; 488 if (errno != EEXIST) 489 error (1, errno, "cannot create temporary file %s", temp); 490 } 491 if (close(fd) < 0) 492 error(1, errno, "cannot close temporary file %s", temp); 493 return temp; 494} 495 496static int 497checkout_file (file, temp) 498 char *file; 499 char *temp; 500{ 501 char *rcs; 502 RCSNode *rcsnode; 503 int retcode = 0; 504 505 if (noexec) 506 return 0; 507 508 rcs = xmalloc (strlen (file) + 5); 509 strcpy (rcs, file); 510 strcat (rcs, RCSEXT); 511 if (!isfile (rcs)) 512 { 513 free (rcs); 514 return (1); 515 } 516 rcsnode = RCS_parsercsfile (rcs); 517 retcode = RCS_checkout (rcsnode, NULL, NULL, NULL, NULL, temp, 518 (RCSCHECKOUTPROC) NULL, (void *) NULL); 519 if (retcode != 0) 520 { 521 error (0, retcode == -1 ? errno : 0, "failed to check out %s file", 522 file); 523 } 524 freercsnode (&rcsnode); 525 free (rcs); 526 return (retcode); 527} 528 529#ifndef MY_NDBM 530 531static void 532write_dbmfile (temp) 533 char *temp; 534{ 535 char line[DBLKSIZ], value[DBLKSIZ]; 536 FILE *fp; 537 DBM *db; 538 char *cp, *vp; 539 datum key, val; 540 int len, cont, err = 0; 541 542 fp = open_file (temp, "r"); 543 if ((db = dbm_open (temp, O_RDWR | O_CREAT | O_TRUNC, 0666)) == NULL) 544 error (1, errno, "cannot open dbm file %s for creation", temp); 545 for (cont = 0; fgets (line, sizeof (line), fp) != NULL;) 546 { 547 if ((cp = strrchr (line, '\n')) != NULL) 548 *cp = '\0'; /* strip the newline */ 549 550 /* 551 * Add the line to the value, at the end if this is a continuation 552 * line; otherwise at the beginning, but only after any trailing 553 * backslash is removed. 554 */ 555 vp = value; 556 if (cont) 557 vp += strlen (value); 558 559 /* 560 * See if the line we read is a continuation line, and strip the 561 * backslash if so. 562 */ 563 len = strlen (line); 564 if (len > 0) 565 cp = &line[len - 1]; 566 else 567 cp = line; 568 if (*cp == '\\') 569 { 570 cont = 1; 571 *cp = '\0'; 572 } 573 else 574 { 575 cont = 0; 576 } 577 (void) strcpy (vp, line); 578 if (value[0] == '#') 579 continue; /* comment line */ 580 vp = value; 581 while (*vp && isspace (*vp)) 582 vp++; 583 if (*vp == '\0') 584 continue; /* empty line */ 585 586 /* 587 * If this was not a continuation line, add the entry to the database 588 */ 589 if (!cont) 590 { 591 key.dptr = vp; 592 while (*vp && !isspace (*vp)) 593 vp++; 594 key.dsize = vp - key.dptr; 595 *vp++ = '\0'; /* NULL terminate the key */ 596 while (*vp && isspace (*vp)) 597 vp++; /* skip whitespace to value */ 598 if (*vp == '\0') 599 { 600 error (0, 0, "warning: NULL value for key `%s'", key.dptr); 601 continue; 602 } 603 val.dptr = vp; 604 val.dsize = strlen (vp); 605 if (dbm_store (db, key, val, DBM_INSERT) == 1) 606 { 607 error (0, 0, "duplicate key found for `%s'", key.dptr); 608 err++; 609 } 610 } 611 } 612 dbm_close (db); 613 (void) fclose (fp); 614 if (err) 615 { 616 char dotdir[50], dotpag[50], dotdb[50]; 617 618 (void) sprintf (dotdir, "%s.dir", temp); 619 (void) sprintf (dotpag, "%s.pag", temp); 620 (void) sprintf (dotdb, "%s.db", temp); 621 (void) unlink_file (dotdir); 622 (void) unlink_file (dotpag); 623 (void) unlink_file (dotdb); 624 error (1, 0, "DBM creation failed; correct above errors"); 625 } 626} 627 628static void 629rename_dbmfile (temp) 630 char *temp; 631{ 632 char newdir[50], newpag[50], newdb[50]; 633 char dotdir[50], dotpag[50], dotdb[50]; 634 char bakdir[50], bakpag[50], bakdb[50]; 635 636 (void) sprintf (dotdir, "%s.dir", CVSROOTADM_MODULES); 637 (void) sprintf (dotpag, "%s.pag", CVSROOTADM_MODULES); 638 (void) sprintf (dotdb, "%s.db", CVSROOTADM_MODULES); 639 (void) sprintf (bakdir, "%s%s.dir", BAKPREFIX, CVSROOTADM_MODULES); 640 (void) sprintf (bakpag, "%s%s.pag", BAKPREFIX, CVSROOTADM_MODULES); 641 (void) sprintf (bakdb, "%s%s.db", BAKPREFIX, CVSROOTADM_MODULES); 642 (void) sprintf (newdir, "%s.dir", temp); 643 (void) sprintf (newpag, "%s.pag", temp); 644 (void) sprintf (newdb, "%s.db", temp); 645 646 (void) chmod (newdir, 0666); 647 (void) chmod (newpag, 0666); 648 (void) chmod (newdb, 0666); 649 650 /* don't mess with me */ 651 SIG_beginCrSect (); 652 653 (void) unlink_file (bakdir); /* rm .#modules.dir .#modules.pag */ 654 (void) unlink_file (bakpag); 655 (void) unlink_file (bakdb); 656 (void) CVS_RENAME (dotdir, bakdir); /* mv modules.dir .#modules.dir */ 657 (void) CVS_RENAME (dotpag, bakpag); /* mv modules.pag .#modules.pag */ 658 (void) CVS_RENAME (dotdb, bakdb); /* mv modules.db .#modules.db */ 659 (void) CVS_RENAME (newdir, dotdir); /* mv "temp".dir modules.dir */ 660 (void) CVS_RENAME (newpag, dotpag); /* mv "temp".pag modules.pag */ 661 (void) CVS_RENAME (newdb, dotdb); /* mv "temp".db modules.db */ 662 663 /* OK -- make my day */ 664 SIG_endCrSect (); 665} 666 667#endif /* !MY_NDBM */ 668 669static void 670rename_rcsfile (temp, real) 671 char *temp; 672 char *real; 673{ 674 char *bak; 675 struct stat statbuf; 676 char *rcs; 677 678 /* Set "x" bits if set in original. */ 679 rcs = xmalloc (strlen (real) + sizeof (RCSEXT) + 10); 680 (void) sprintf (rcs, "%s%s", real, RCSEXT); 681 statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */ 682 (void) CVS_STAT (rcs, &statbuf); 683 free (rcs); 684 685 if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0) 686 error (0, errno, "warning: cannot chmod %s", temp); 687 bak = xmalloc (strlen (real) + sizeof (BAKPREFIX) + 10); 688 (void) sprintf (bak, "%s%s", BAKPREFIX, real); 689 (void) unlink_file (bak); /* rm .#loginfo */ 690 (void) CVS_RENAME (real, bak); /* mv loginfo .#loginfo */ 691 (void) CVS_RENAME (temp, real); /* mv "temp" loginfo */ 692 free (bak); 693} 694 695const char *const init_usage[] = { 696 "Usage: %s %s\n", 697 NULL 698}; 699 700int 701init (argc, argv) 702 int argc; 703 char **argv; 704{ 705 /* Name of CVSROOT directory. */ 706 char *adm; 707 /* Name of this administrative file. */ 708 char *info; 709 /* Name of ,v file for this administrative file. */ 710 char *info_v; 711 712 const struct admin_file *fileptr; 713 714 umask (cvsumask); 715 716 if (argc == -1 || argc > 1) 717 usage (init_usage); 718 719#ifdef CLIENT_SUPPORT 720 if (client_active) 721 { 722 start_server (); 723 724 ign_setup (); 725 send_init_command (); 726 return get_responses_and_close (); 727 } 728#endif /* CLIENT_SUPPORT */ 729 730 /* Note: we do *not* create parent directories as needed like the 731 old cvsinit.sh script did. Few utilities do that, and a 732 non-existent parent directory is as likely to be a typo as something 733 which needs to be created. */ 734 mkdir_if_needed (CVSroot_directory); 735 736 adm = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + 10); 737 strcpy (adm, CVSroot_directory); 738 strcat (adm, "/"); 739 strcat (adm, CVSROOTADM); 740 mkdir_if_needed (adm); 741 742 /* This is needed by the call to "ci" below. */ 743 if ( CVS_CHDIR (adm) < 0) 744 error (1, errno, "cannot change to directory %s", adm); 745 746 /* 80 is long enough for all the administrative file names, plus 747 "/" and so on. */ 748 info = xmalloc (strlen (adm) + 80); 749 info_v = xmalloc (strlen (adm) + 80); 750 for (fileptr = filelist; fileptr && fileptr->filename; ++fileptr) 751 { 752 if (fileptr->contents == NULL) 753 continue; 754 strcpy (info, adm); 755 strcat (info, "/"); 756 strcat (info, fileptr->filename); 757 strcpy (info_v, info); 758 strcat (info_v, RCSEXT); 759 if (isfile (info_v)) 760 /* We will check out this file in the mkmodules step. 761 Nothing else is required. */ 762 ; 763 else 764 { 765 int retcode; 766 767 if (!isfile (info)) 768 { 769 FILE *fp; 770 const char * const *p; 771 772 fp = open_file (info, "w"); 773 for (p = fileptr->contents; *p != NULL; ++p) 774 if (fputs (*p, fp) < 0) 775 error (1, errno, "cannot write %s", info); 776 if (fclose (fp) < 0) 777 error (1, errno, "cannot close %s", info); 778 } 779 /* Now check the file in. FIXME: we could be using 780 add_rcs_file from import.c which is faster (if it were 781 tweaked slightly). */ 782 run_setup ("%s%s -x,v/ -q -u -t-", Rcsbin, RCS_CI); 783 run_args ("-minitial checkin of %s", fileptr->filename); 784 run_arg (fileptr->filename); 785 retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); 786 if (retcode != 0) 787 error (1, retcode == -1 ? errno : 0, 788 "failed to check in %s", info); 789 } 790 } 791 792 /* Turn on history logging by default. The user can remove the file 793 to disable it. */ 794 strcpy (info, adm); 795 strcat (info, "/"); 796 strcat (info, CVSROOTADM_HISTORY); 797 if (!isfile (info)) 798 { 799 FILE *fp; 800 801 fp = open_file (info, "w"); 802 if (fclose (fp) < 0) 803 error (1, errno, "cannot close %s", info); 804 } 805 806 free (info); 807 free (info_v); 808 809 mkmodules (adm); 810 811 free (adm); 812 return 0; 813} 814