117721Speter/*
2177404Sobrien * Copyright (C) 1986-2008 The Free Software Foundation, Inc.
3175282Sobrien *
4175282Sobrien * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5175282Sobrien *                                  and others.
6175282Sobrien *
7175282Sobrien * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8175282Sobrien * Portions Copyright (C) 1989-1992, Brian Berliner
917721Speter *
1017721Speter * You may distribute under the terms of the GNU General Public License as
11177405Sobrien * specified in the README file that comes with the CVS kit.  */
1217721Speter
13177404Sobrien#include <assert.h>
1417721Speter#include "cvs.h"
15128269Speter#include "getline.h"
16128269Speter#include "history.h"
1717721Speter#include "savecwd.h"
1817721Speter
1917721Speter#ifndef DBLKSIZ
2017721Speter#define	DBLKSIZ	4096			/* since GNU ndbm doesn't define it */
2117721Speter#endif
2217721Speter
2317721Speterstatic int checkout_file PROTO((char *file, char *temp));
2425843Speterstatic char *make_tempfile PROTO((void));
2517721Speterstatic void rename_rcsfile PROTO((char *temp, char *real));
2617721Speter
2717721Speter#ifndef MY_NDBM
2817721Speterstatic void rename_dbmfile PROTO((char *temp));
2917721Speterstatic void write_dbmfile PROTO((char *temp));
3017721Speter#endif				/* !MY_NDBM */
3117721Speter
3217721Speter/* Structure which describes an administrative file.  */
3317721Speterstruct admin_file {
3417721Speter   /* Name of the file, within the CVSROOT directory.  */
3517721Speter   char *filename;
3617721Speter
3717721Speter   /* This is a one line description of what the file is for.  It is not
3817721Speter      currently used, although one wonders whether it should be, somehow.
3925843Speter      If NULL, then don't process this file in mkmodules (FIXME?: a bit of
4017721Speter      a kludge; probably should replace this with a flags field).  */
4117721Speter   char *errormsg;
4217721Speter
4317721Speter   /* Contents which the file should have in a new repository.  To avoid
4417721Speter      problems with brain-dead compilers which choke on long string constants,
4517721Speter      this is a pointer to an array of char * terminated by NULL--each of
4625843Speter      the strings is concatenated.
4725843Speter
4825843Speter      If this field is NULL, the file is not created in a new
4925843Speter      repository, but it can be added with "cvs add" (just as if one
5025843Speter      had created the repository with a version of CVS which didn't
5125843Speter      know about the file) and the checked-out copy will be updated
5225843Speter      without having to add it to checkoutlist.  */
5317721Speter   const char * const *contents;
5417721Speter};
5517721Speter
5617721Speterstatic const char *const loginfo_contents[] = {
5725843Speter    "# The \"loginfo\" file controls where \"cvs commit\" log information\n",
5825843Speter    "# is sent.  The first entry on a line is a regular expression which must match\n",
5925843Speter    "# the directory that the change is being made to, relative to the\n",
6025843Speter    "# $CVSROOT.  If a match is found, then the remainder of the line is a filter\n",
6125843Speter    "# program that should expect log information on its standard input.\n",
6217721Speter    "#\n",
6325843Speter    "# If the repository name does not match any of the regular expressions in this\n",
6425843Speter    "# file, the \"DEFAULT\" line is used, if it is specified.\n",
6517721Speter    "#\n",
6625843Speter    "# If the name ALL appears as a regular expression it is always used\n",
6725843Speter    "# in addition to the first matching regex or DEFAULT.\n",
6817721Speter    "#\n",
6925843Speter    "# You may specify a format string as part of the\n",
7025843Speter    "# filter.  The string is composed of a `%' followed\n",
7125843Speter    "# by a single format character, or followed by a set of format\n",
7225843Speter    "# characters surrounded by `{' and `}' as separators.  The format\n",
7325843Speter    "# characters are:\n",
7417721Speter    "#\n",
7525843Speter    "#   s = file name\n",
7625843Speter    "#   V = old version number (pre-checkin)\n",
7725843Speter    "#   v = new version number (post-checkin)\n",
7825843Speter    "#\n",
7917721Speter    "# For example:\n",
8025843Speter    "#DEFAULT (echo \"\"; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog\n",
8125843Speter    "# or\n",
8225843Speter    "#DEFAULT (echo \"\"; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog\n",
8317721Speter    NULL
8417721Speter};
8517721Speter
8617721Speterstatic const char *const rcsinfo_contents[] = {
8717721Speter    "# The \"rcsinfo\" file is used to control templates with which the editor\n",
8817721Speter    "# is invoked on commit and import.\n",
8917721Speter    "#\n",
9017721Speter    "# The first entry on a line is a regular expression which is tested\n",
9117721Speter    "# against the directory that the change is being made to, relative to the\n",
9217721Speter    "# $CVSROOT.  For the first match that is found, then the remainder of the\n",
9317721Speter    "# line is the name of the file that contains the template.\n",
9417721Speter    "#\n",
9517721Speter    "# If the repository name does not match any of the regular expressions in this\n",
9617721Speter    "# file, the \"DEFAULT\" line is used, if it is specified.\n",
9717721Speter    "#\n",
9817721Speter    "# If the name \"ALL\" appears as a regular expression it is always used\n",
9917721Speter    "# in addition to the first matching regex or \"DEFAULT\".\n",
10017721Speter    NULL
10117721Speter};
10217721Speter
10317721Speterstatic const char *const editinfo_contents[] = {
10417721Speter    "# The \"editinfo\" file is used to allow verification of logging\n",
10517721Speter    "# information.  It works best when a template (as specified in the\n",
10617721Speter    "# rcsinfo file) is provided for the logging procedure.  Given a\n",
10717721Speter    "# template with locations for, a bug-id number, a list of people who\n",
10817721Speter    "# reviewed the code before it can be checked in, and an external\n",
10917721Speter    "# process to catalog the differences that were code reviewed, the\n",
11017721Speter    "# following test can be applied to the code:\n",
11117721Speter    "#\n",
11217721Speter    "#   Making sure that the entered bug-id number is correct.\n",
11317721Speter    "#   Validating that the code that was reviewed is indeed the code being\n",
11417721Speter    "#       checked in (using the bug-id number or a seperate review\n",
11517721Speter    "#       number to identify this particular code set.).\n",
11617721Speter    "#\n",
11717721Speter    "# If any of the above test failed, then the commit would be aborted.\n",
11817721Speter    "#\n",
11917721Speter    "# Actions such as mailing a copy of the report to each reviewer are\n",
12017721Speter    "# better handled by an entry in the loginfo file.\n",
12117721Speter    "#\n",
12225843Speter    "# One thing that should be noted is the the ALL keyword is not\n",
12325843Speter    "# supported.  There can be only one entry that matches a given\n",
12417721Speter    "# repository.\n",
12517721Speter    NULL
12617721Speter};
12717721Speter
12825843Speterstatic const char *const verifymsg_contents[] = {
12925843Speter    "# The \"verifymsg\" file is used to allow verification of logging\n",
13025843Speter    "# information.  It works best when a template (as specified in the\n",
13125843Speter    "# rcsinfo file) is provided for the logging procedure.  Given a\n",
13225843Speter    "# template with locations for, a bug-id number, a list of people who\n",
13325843Speter    "# reviewed the code before it can be checked in, and an external\n",
13425843Speter    "# process to catalog the differences that were code reviewed, the\n",
13525843Speter    "# following test can be applied to the code:\n",
13625843Speter    "#\n",
13725843Speter    "#   Making sure that the entered bug-id number is correct.\n",
13825843Speter    "#   Validating that the code that was reviewed is indeed the code being\n",
13925843Speter    "#       checked in (using the bug-id number or a seperate review\n",
14025843Speter    "#       number to identify this particular code set.).\n",
14125843Speter    "#\n",
14225843Speter    "# If any of the above test failed, then the commit would be aborted.\n",
14325843Speter    "#\n",
14425843Speter    "# Actions such as mailing a copy of the report to each reviewer are\n",
14525843Speter    "# better handled by an entry in the loginfo file.\n",
14625843Speter    "#\n",
14725843Speter    "# One thing that should be noted is the the ALL keyword is not\n",
14825843Speter    "# supported.  There can be only one entry that matches a given\n",
14925843Speter    "# repository.\n",
15025843Speter    NULL
15125843Speter};
15225843Speter
15317721Speterstatic const char *const commitinfo_contents[] = {
15417721Speter    "# The \"commitinfo\" file is used to control pre-commit checks.\n",
15517721Speter    "# The filter on the right is invoked with the repository and a list \n",
15617721Speter    "# of files to check.  A non-zero exit of the filter program will \n",
15717721Speter    "# cause the commit to be aborted.\n",
15817721Speter    "#\n",
15917721Speter    "# The first entry on a line is a regular expression which is tested\n",
16017721Speter    "# against the directory that the change is being committed to, relative\n",
16117721Speter    "# to the $CVSROOT.  For the first match that is found, then the remainder\n",
16217721Speter    "# of the line is the name of the filter to run.\n",
16317721Speter    "#\n",
16417721Speter    "# If the repository name does not match any of the regular expressions in this\n",
16517721Speter    "# file, the \"DEFAULT\" line is used, if it is specified.\n",
16617721Speter    "#\n",
16717721Speter    "# If the name \"ALL\" appears as a regular expression it is always used\n",
16817721Speter    "# in addition to the first matching regex or \"DEFAULT\".\n",
16917721Speter    NULL
17017721Speter};
17117721Speter
17217721Speterstatic const char *const taginfo_contents[] = {
17317721Speter    "# The \"taginfo\" file is used to control pre-tag checks.\n",
17417721Speter    "# The filter on the right is invoked with the following arguments:\n",
17517721Speter    "#\n",
17617721Speter    "# $1 -- tagname\n",
17717721Speter    "# $2 -- operation \"add\" for tag, \"mov\" for tag -F, and \"del\" for tag -d\n",
17817721Speter    "# $3 -- repository\n",
17917721Speter    "# $4->  file revision [file revision ...]\n",
18017721Speter    "#\n",
18117721Speter    "# A non-zero exit of the filter program will cause the tag to be aborted.\n",
18217721Speter    "#\n",
18317721Speter    "# The first entry on a line is a regular expression which is tested\n",
18417721Speter    "# against the directory that the change is being committed to, relative\n",
18517721Speter    "# to the $CVSROOT.  For the first match that is found, then the remainder\n",
18617721Speter    "# of the line is the name of the filter to run.\n",
18717721Speter    "#\n",
18817721Speter    "# If the repository name does not match any of the regular expressions in this\n",
18917721Speter    "# file, the \"DEFAULT\" line is used, if it is specified.\n",
19017721Speter    "#\n",
19117721Speter    "# If the name \"ALL\" appears as a regular expression it is always used\n",
19217721Speter    "# in addition to the first matching regex or \"DEFAULT\".\n",
19317721Speter    NULL
19417721Speter};
19517721Speter
19617721Speterstatic const char *const checkoutlist_contents[] = {
19717721Speter    "# The \"checkoutlist\" file is used to support additional version controlled\n",
19817721Speter    "# administrative files in $CVSROOT/CVSROOT, such as template files.\n",
19917721Speter    "#\n",
20017721Speter    "# The first entry on a line is a filename which will be checked out from\n",
20117721Speter    "# the corresponding RCS file in the $CVSROOT/CVSROOT directory.\n",
20217721Speter    "# The remainder of the line is an error message to use if the file cannot\n",
20317721Speter    "# be checked out.\n",
20417721Speter    "#\n",
20517721Speter    "# File format:\n",
20617721Speter    "#\n",
207128269Speter    "#	[<whitespace>]<filename>[<whitespace><error message>]<end-of-line>\n",
20817721Speter    "#\n",
20917721Speter    "# comment lines begin with '#'\n",
21017721Speter    NULL
21117721Speter};
21217721Speter
21317721Speterstatic const char *const cvswrappers_contents[] = {
21432899Speter    "# This file affects handling of files based on their names.\n",
21517721Speter    "#\n",
216102843Speter#if 0    /* see comments in wrap_add in wrapper.c */
21732899Speter    "# The -t/-f options allow one to treat directories of files\n",
21832899Speter    "# as a single file, or to transform a file in other ways on\n",
21932899Speter    "# its way in and out of CVS.\n",
22017721Speter    "#\n",
221102843Speter#endif
22232899Speter    "# The -m option specifies whether CVS attempts to merge files.\n",
22317721Speter    "#\n",
22432899Speter    "# The -k option specifies keyword expansion (e.g. -kb for binary).\n",
22532899Speter    "#\n",
22617721Speter    "# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)\n",
22717721Speter    "#\n",
22817721Speter    "#  wildcard	[option value][option value]...\n",
22917721Speter    "#\n",
23017721Speter    "#  where option is one of\n",
23117721Speter    "#  -f		from cvs filter		value: path to filter\n",
23217721Speter    "#  -t		to cvs filter		value: path to filter\n",
23317721Speter    "#  -m		update methodology	value: MERGE or COPY\n",
23432899Speter    "#  -k		expansion mode		value: b, o, kkv, &c\n",
23517721Speter    "#\n",
23617721Speter    "#  and value is a single-quote delimited value.\n",
23717721Speter    "# For example:\n",
23832899Speter    "#*.gif -k 'b'\n",
23917721Speter    NULL
24017721Speter};
24117721Speter
24217721Speterstatic const char *const notify_contents[] = {
24317721Speter    "# The \"notify\" file controls where notifications from watches set by\n",
24417721Speter    "# \"cvs watch add\" or \"cvs edit\" are sent.  The first entry on a line is\n",
24517721Speter    "# a regular expression which is tested against the directory that the\n",
24617721Speter    "# change is being made to, relative to the $CVSROOT.  If it matches,\n",
24717721Speter    "# then the remainder of the line is a filter program that should contain\n",
24817721Speter    "# one occurrence of %s for the user to notify, and information on its\n",
24917721Speter    "# standard input.\n",
25017721Speter    "#\n",
25117721Speter    "# \"ALL\" or \"DEFAULT\" can be used in place of the regular expression.\n",
25217721Speter    "#\n",
25317721Speter    "# For example:\n",
254102843Speter    "#ALL mail -s \"CVS notification\" %s\n",
25517721Speter    NULL
25617721Speter};
25717721Speter
25817721Speterstatic const char *const modules_contents[] = {
25917721Speter    "# Three different line formats are valid:\n",
26017721Speter    "#	key	-a    aliases...\n",
26117721Speter    "#	key [options] directory\n",
26217721Speter    "#	key [options] directory files...\n",
26317721Speter    "#\n",
26417721Speter    "# Where \"options\" are composed of:\n",
26517721Speter    "#	-o prog		Run \"prog\" on \"cvs checkout\" of module.\n",
26617721Speter    "#	-e prog		Run \"prog\" on \"cvs export\" of module.\n",
26717721Speter    "#	-t prog		Run \"prog\" on \"cvs rtag\" of module.\n",
26817721Speter    "#	-u prog		Run \"prog\" on \"cvs update\" of module.\n",
26917721Speter    "#	-d dir		Place module in directory \"dir\" instead of module name.\n",
27017721Speter    "#	-l		Top-level directory only -- do not recurse.\n",
27117721Speter    "#\n",
27225843Speter    "# NOTE:  If you change any of the \"Run\" options above, you'll have to\n",
27325843Speter    "# release and re-checkout any working directories of these modules.\n",
27425843Speter    "#\n",
27517721Speter    "# And \"directory\" is a path to a directory relative to $CVSROOT.\n",
27617721Speter    "#\n",
27717721Speter    "# The \"-a\" option specifies an alias.  An alias is interpreted as if\n",
27817721Speter    "# everything on the right of the \"-a\" had been typed on the command line.\n",
27917721Speter    "#\n",
28017721Speter    "# You can encode a module within a module by using the special '&'\n",
28117721Speter    "# character to interpose another module into the current module.  This\n",
28217721Speter    "# can be useful for creating a module that consists of many directories\n",
28317721Speter    "# spread out over the entire source repository.\n",
28417721Speter    NULL
28517721Speter};
28617721Speter
28732788Speterstatic const char *const config_contents[] = {
28832788Speter    "# Set this to \"no\" if pserver shouldn't check system users/passwords\n",
289175282Sobrien    "#SystemAuth=yes\n",
29034467Speter    "\n",
291177404Sobrien    "# Set `IgnoreUnknownConfigKeys' to `yes' to ignore unknown config\n",
292177404Sobrien    "# keys which are supported in a future version of CVS.\n",
293177404Sobrien    "# This option is intended to be useful as a transition for read-only\n",
294177404Sobrien    "# mirror sites when sites may need to be updated later than the\n",
295177404Sobrien    "# primary CVS repository.\n",
296177404Sobrien    "#IgnoreUnknownConfigKeys=no\n",
297177404Sobrien    "\n",
29866528Speter    "# Put CVS lock files in this directory rather than directly in the repository.\n",
29966528Speter    "#LockDir=/var/lock/cvs\n",
30066528Speter    "\n",
30166528Speter#ifdef PRESERVE_PERMISSIONS_SUPPORT
30234467Speter    "# Set `PreservePermissions' to `yes' to save file status information\n",
30334467Speter    "# in the repository.\n",
30434467Speter    "#PreservePermissions=no\n",
30544856Speter    "\n",
30666528Speter#endif
30744856Speter    "# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top\n",
30844856Speter    "# level of the new working directory when using the `cvs checkout'\n",
30944856Speter    "# command.\n",
31044856Speter    "#TopLevelAdmin=no\n",
31166528Speter    "\n",
312128269Speter    "# Set `LogHistory' to `all' or `" ALL_HISTORY_REC_TYPES "' to log all transactions to the\n",
31366528Speter    "# history file, or a subset as needed (ie `TMAR' logs all write operations)\n",
314128269Speter    "#LogHistory=" ALL_HISTORY_REC_TYPES "\n",
315102843Speter    "\n",
316102843Speter    "# Set `RereadLogAfterVerify' to `always' (the default) to allow the verifymsg\n",
317175282Sobrien    "# script to change the log message.  Set it to `stat' to force CVS to verify\n",
318102843Speter    "# that the file has changed before reading it (this can take up to an extra\n",
319102843Speter    "# second per directory being committed, so it is not recommended for large\n",
320102843Speter    "# repositories.  Set it to `never' (the previous CVS behavior) to prevent\n",
321102843Speter    "# verifymsg scripts from changing the log message.\n",
322102843Speter    "#RereadLogAfterVerify=always\n",
32332788Speter    NULL
32432788Speter};
32532788Speter
32617721Speterstatic const struct admin_file filelist[] = {
32717721Speter    {CVSROOTADM_LOGINFO,
32817721Speter	"no logging of 'cvs commit' messages is done without a %s file",
32917721Speter	&loginfo_contents[0]},
33017721Speter    {CVSROOTADM_RCSINFO,
33117721Speter	"a %s file can be used to configure 'cvs commit' templates",
33217721Speter	rcsinfo_contents},
33317721Speter    {CVSROOTADM_EDITINFO,
33417721Speter	"a %s file can be used to validate log messages",
33517721Speter	editinfo_contents},
33625843Speter    {CVSROOTADM_VERIFYMSG,
33725843Speter	"a %s file can be used to validate log messages",
33825843Speter	verifymsg_contents},
33917721Speter    {CVSROOTADM_COMMITINFO,
34017721Speter	"a %s file can be used to configure 'cvs commit' checking",
34117721Speter	commitinfo_contents},
34217721Speter    {CVSROOTADM_TAGINFO,
34317721Speter	"a %s file can be used to configure 'cvs tag' checking",
34417721Speter	taginfo_contents},
34517721Speter    {CVSROOTADM_IGNORE,
34617721Speter	"a %s file can be used to specify files to ignore",
34717721Speter	NULL},
34817721Speter    {CVSROOTADM_CHECKOUTLIST,
34917721Speter	"a %s file can specify extra CVSROOT files to auto-checkout",
35017721Speter	checkoutlist_contents},
35117721Speter    {CVSROOTADM_WRAPPER,
35217721Speter	"a %s file can be used to specify files to treat as wrappers",
35317721Speter	cvswrappers_contents},
35417721Speter    {CVSROOTADM_NOTIFY,
35517721Speter	"a %s file can be used to specify where notifications go",
35617721Speter	notify_contents},
35717721Speter    {CVSROOTADM_MODULES,
35817721Speter	/* modules is special-cased in mkmodules.  */
35917721Speter	NULL,
36017721Speter	modules_contents},
36125843Speter    {CVSROOTADM_READERS,
36225843Speter	"a %s file specifies read-only users",
36325843Speter	NULL},
36425843Speter    {CVSROOTADM_WRITERS,
36525843Speter	"a %s file specifies read/write users",
36625843Speter	NULL},
36732788Speter
36832788Speter    /* Some have suggested listing CVSROOTADM_PASSWD here too.  This
36932788Speter       would mean that CVS commands which operate on the
37032788Speter       CVSROOTADM_PASSWD file would transmit hashed passwords over the
37132788Speter       net.  This might seem to be no big deal, as pserver normally
37232788Speter       transmits cleartext passwords, but the difference is that
37332788Speter       CVSROOTADM_PASSWD contains *all* passwords, not just the ones
37432788Speter       currently being used.  For example, it could be too easy to
37532788Speter       accidentally give someone readonly access to CVSROOTADM_PASSWD
37632788Speter       (e.g. via anonymous CVS or cvsweb), and then if there are any
37732788Speter       guessable passwords for read/write access (usually there will be)
37832788Speter       they get read/write access.
37932788Speter
38032788Speter       Another worry is the implications of storing old passwords--if
38132788Speter       someone used a password in the past they might be using it
38232788Speter       elsewhere, using a similar password, etc, and so saving old
38332788Speter       passwords, even hashed, is probably not a good idea.  */
38432788Speter
38532788Speter    {CVSROOTADM_CONFIG,
38632788Speter	 "a %s file configures various behaviors",
38732788Speter	 config_contents},
38854431Speter    {NULL, NULL, NULL}
38917721Speter};
39017721Speter
39117721Speter/* Rebuild the checked out administrative files in directory DIR.  */
39217721Speterint
39317721Spetermkmodules (dir)
39417721Speter    char *dir;
39517721Speter{
39617721Speter    struct saved_cwd cwd;
39725843Speter    char *temp;
39817721Speter    char *cp, *last, *fname;
39917721Speter#ifdef MY_NDBM
40017721Speter    DBM *db;
40117721Speter#endif
40217721Speter    FILE *fp;
40325843Speter    char *line = NULL;
40425843Speter    size_t line_allocated = 0;
40517721Speter    const struct admin_file *fileptr;
40617721Speter
40766528Speter    if (noexec)
40866528Speter	return 0;
40966528Speter
41017721Speter    if (save_cwd (&cwd))
41125843Speter	error_exit ();
41217721Speter
41325843Speter    if ( CVS_CHDIR (dir) < 0)
41417721Speter	error (1, errno, "cannot chdir to %s", dir);
41517721Speter
41617721Speter    /*
41717721Speter     * First, do the work necessary to update the "modules" database.
41817721Speter     */
41925843Speter    temp = make_tempfile ();
42017721Speter    switch (checkout_file (CVSROOTADM_MODULES, temp))
42117721Speter    {
42217721Speter
42317721Speter	case 0:			/* everything ok */
42417721Speter#ifdef MY_NDBM
42517721Speter	    /* open it, to generate any duplicate errors */
42617721Speter	    if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL)
42717721Speter		dbm_close (db);
42817721Speter#else
42917721Speter	    write_dbmfile (temp);
43017721Speter	    rename_dbmfile (temp);
43117721Speter#endif
43217721Speter	    rename_rcsfile (temp, CVSROOTADM_MODULES);
43317721Speter	    break;
43417721Speter
43517721Speter	default:
43617721Speter	    error (0, 0,
43717721Speter		"'cvs checkout' is less functional without a %s file",
43817721Speter		CVSROOTADM_MODULES);
43917721Speter	    break;
44017721Speter    }					/* switch on checkout_file() */
44117721Speter
44254431Speter    if (unlink_file (temp) < 0
44354431Speter	&& !existence_error (errno))
44454431Speter	error (0, errno, "cannot remove %s", temp);
44525843Speter    free (temp);
44617721Speter
44717721Speter    /* Checkout the files that need it in CVSROOT dir */
44817721Speter    for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) {
44917721Speter	if (fileptr->errormsg == NULL)
45017721Speter	    continue;
45125843Speter	temp = make_tempfile ();
45217721Speter	if (checkout_file (fileptr->filename, temp) == 0)
45317721Speter	    rename_rcsfile (temp, fileptr->filename);
45417721Speter#if 0
45517721Speter	/*
45617721Speter	 * If there was some problem other than the file not existing,
45717721Speter	 * checkout_file already printed a real error message.  If the
45817721Speter	 * file does not exist, it is harmless--it probably just means
45917721Speter	 * that the repository was created with an old version of CVS
46017721Speter	 * which didn't have so many files in CVSROOT.
46117721Speter	 */
46217721Speter	else if (fileptr->errormsg)
46317721Speter	    error (0, 0, fileptr->errormsg, fileptr->filename);
46417721Speter#endif
46554431Speter	if (unlink_file (temp) < 0
46654431Speter	    && !existence_error (errno))
46754431Speter	    error (0, errno, "cannot remove %s", temp);
46825843Speter	free (temp);
46917721Speter    }
47017721Speter
47125843Speter    fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r");
47217721Speter    if (fp)
47317721Speter    {
47417721Speter	/*
47517721Speter	 * File format:
476128269Speter	 *  [<whitespace>]<filename>[<whitespace><error message>]<end-of-line>
47717721Speter	 *
47817721Speter	 * comment lines begin with '#'
47917721Speter	 */
48025843Speter	while (getline (&line, &line_allocated, fp) >= 0)
48117721Speter	{
48217721Speter	    /* skip lines starting with # */
48317721Speter	    if (line[0] == '#')
48417721Speter		continue;
48517721Speter
48617721Speter	    if ((last = strrchr (line, '\n')) != NULL)
48717721Speter		*last = '\0';			/* strip the newline */
48817721Speter
48917721Speter	    /* Skip leading white space. */
49054431Speter	    for (fname = line;
49154431Speter		 *fname && isspace ((unsigned char) *fname);
49254431Speter		 fname++)
49317721Speter		;
49417721Speter
49517721Speter	    /* Find end of filename. */
49654431Speter	    for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++)
49717721Speter		;
49817721Speter	    *cp = '\0';
49917721Speter
50025843Speter	    temp = make_tempfile ();
50117721Speter	    if (checkout_file (fname, temp) == 0)
50217721Speter	    {
50317721Speter		rename_rcsfile (temp, fname);
50417721Speter	    }
50517721Speter	    else
50617721Speter	    {
507128269Speter		/* Skip leading white space before the error message.  */
50854431Speter		for (cp++;
509128269Speter		     cp < last && *cp && isspace ((unsigned char) *cp);
51054431Speter		     cp++)
51117721Speter		    ;
51217721Speter		if (cp < last && *cp)
513128269Speter		    error (0, 0, "%s", cp);
51417721Speter	    }
51554431Speter	    if (unlink_file (temp) < 0
51654431Speter		&& !existence_error (errno))
51754431Speter		error (0, errno, "cannot remove %s", temp);
51825843Speter	    free (temp);
51917721Speter	}
52025843Speter	if (line)
52125843Speter	    free (line);
52225843Speter	if (ferror (fp))
52325843Speter	    error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST);
52425843Speter	if (fclose (fp) < 0)
52525843Speter	    error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST);
52617721Speter    }
52725843Speter    else
52825843Speter    {
52925843Speter	/* Error from CVS_FOPEN.  */
53025843Speter	if (!existence_error (errno))
53125843Speter	    error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST);
53225843Speter    }
53317721Speter
53417721Speter    if (restore_cwd (&cwd, NULL))
53525843Speter	error_exit ();
53617721Speter    free_cwd (&cwd);
53717721Speter
53817721Speter    return (0);
53917721Speter}
54017721Speter
54117721Speter/*
54217721Speter * Yeah, I know, there are NFS race conditions here.
54317721Speter */
54425843Speterstatic char *
54525843Spetermake_tempfile ()
54617721Speter{
54717721Speter    static int seed = 0;
54817721Speter    int fd;
54925843Speter    char *temp;
55017721Speter
55117721Speter    if (seed == 0)
55217721Speter	seed = getpid ();
55325843Speter    temp = xmalloc (sizeof (BAKPREFIX) + 40);
55417721Speter    while (1)
55517721Speter    {
55617721Speter	(void) sprintf (temp, "%s%d", BAKPREFIX, seed++);
55725843Speter	if ((fd = CVS_OPEN (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1)
55817721Speter	    break;
55917721Speter	if (errno != EEXIST)
56017721Speter	    error (1, errno, "cannot create temporary file %s", temp);
56117721Speter    }
56217721Speter    if (close(fd) < 0)
56317721Speter	error(1, errno, "cannot close temporary file %s", temp);
56425843Speter    return temp;
56517721Speter}
56617721Speter
56754431Speter/* Get a file.  If the file does not exist, return 1 silently.  If
56854431Speter   there is an error, print a message and return 1 (FIXME: probably
56954431Speter   not a very clean convention).  On success, return 0.  */
57054431Speter
57117721Speterstatic int
57217721Spetercheckout_file (file, temp)
57317721Speter    char *file;
57417721Speter    char *temp;
57517721Speter{
57625843Speter    char *rcs;
57725843Speter    RCSNode *rcsnode;
57817721Speter    int retcode = 0;
57917721Speter
58025843Speter    if (noexec)
58125843Speter	return 0;
58225843Speter
58325843Speter    rcs = xmalloc (strlen (file) + 5);
58425843Speter    strcpy (rcs, file);
58525843Speter    strcat (rcs, RCSEXT);
58617721Speter    if (!isfile (rcs))
58725843Speter    {
58825843Speter	free (rcs);
58917721Speter	return (1);
59025843Speter    }
591175282Sobrien
59225843Speter    rcsnode = RCS_parsercsfile (rcs);
593175282Sobrien    if (!rcsnode)
594175282Sobrien    {
595175282Sobrien	/* Probably not necessary (?); RCS_parsercsfile already printed a
596175282Sobrien	   message.  */
597175282Sobrien	error (0, 0, "Failed to parse `%s'.", rcs);
598175282Sobrien	free (rcs);
599175282Sobrien	return 1;
600175282Sobrien    }
601175282Sobrien
60225843Speter    retcode = RCS_checkout (rcsnode, NULL, NULL, NULL, NULL, temp,
60325843Speter			    (RCSCHECKOUTPROC) NULL, (void *) NULL);
60425843Speter    if (retcode != 0)
60517721Speter    {
60654431Speter	/* Probably not necessary (?); RCS_checkout already printed a
60754431Speter	   message.  */
60834467Speter	error (0, 0, "failed to check out %s file",
60925843Speter	       file);
61017721Speter    }
61125843Speter    freercsnode (&rcsnode);
61225843Speter    free (rcs);
61317721Speter    return (retcode);
61417721Speter}
61517721Speter
61617721Speter#ifndef MY_NDBM
61717721Speter
61817721Speterstatic void
61917721Speterwrite_dbmfile (temp)
62017721Speter    char *temp;
62117721Speter{
62217721Speter    char line[DBLKSIZ], value[DBLKSIZ];
62317721Speter    FILE *fp;
62417721Speter    DBM *db;
62517721Speter    char *cp, *vp;
62617721Speter    datum key, val;
62717721Speter    int len, cont, err = 0;
62817721Speter
62917721Speter    fp = open_file (temp, "r");
63017721Speter    if ((db = dbm_open (temp, O_RDWR | O_CREAT | O_TRUNC, 0666)) == NULL)
63117721Speter	error (1, errno, "cannot open dbm file %s for creation", temp);
63217721Speter    for (cont = 0; fgets (line, sizeof (line), fp) != NULL;)
63317721Speter    {
63417721Speter	if ((cp = strrchr (line, '\n')) != NULL)
63517721Speter	    *cp = '\0';			/* strip the newline */
63617721Speter
63717721Speter	/*
63817721Speter	 * Add the line to the value, at the end if this is a continuation
63917721Speter	 * line; otherwise at the beginning, but only after any trailing
64017721Speter	 * backslash is removed.
64117721Speter	 */
64217721Speter	vp = value;
64317721Speter	if (cont)
64417721Speter	    vp += strlen (value);
64517721Speter
64617721Speter	/*
64717721Speter	 * See if the line we read is a continuation line, and strip the
64817721Speter	 * backslash if so.
64917721Speter	 */
65017721Speter	len = strlen (line);
65117721Speter	if (len > 0)
65217721Speter	    cp = &line[len - 1];
65317721Speter	else
65417721Speter	    cp = line;
65517721Speter	if (*cp == '\\')
65617721Speter	{
65717721Speter	    cont = 1;
65817721Speter	    *cp = '\0';
65917721Speter	}
66017721Speter	else
66117721Speter	{
66217721Speter	    cont = 0;
66317721Speter	}
66417721Speter	(void) strcpy (vp, line);
66517721Speter	if (value[0] == '#')
66617721Speter	    continue;			/* comment line */
66717721Speter	vp = value;
66854431Speter	while (*vp && isspace ((unsigned char) *vp))
66917721Speter	    vp++;
67017721Speter	if (*vp == '\0')
67117721Speter	    continue;			/* empty line */
67217721Speter
67317721Speter	/*
67417721Speter	 * If this was not a continuation line, add the entry to the database
67517721Speter	 */
67617721Speter	if (!cont)
67717721Speter	{
67817721Speter	    key.dptr = vp;
67954431Speter	    while (*vp && !isspace ((unsigned char) *vp))
68017721Speter		vp++;
68117721Speter	    key.dsize = vp - key.dptr;
68217721Speter	    *vp++ = '\0';		/* NULL terminate the key */
68354431Speter	    while (*vp && isspace ((unsigned char) *vp))
68417721Speter		vp++;			/* skip whitespace to value */
68517721Speter	    if (*vp == '\0')
68617721Speter	    {
68717721Speter		error (0, 0, "warning: NULL value for key `%s'", key.dptr);
68817721Speter		continue;
68917721Speter	    }
69017721Speter	    val.dptr = vp;
69117721Speter	    val.dsize = strlen (vp);
69217721Speter	    if (dbm_store (db, key, val, DBM_INSERT) == 1)
69317721Speter	    {
69417721Speter		error (0, 0, "duplicate key found for `%s'", key.dptr);
69517721Speter		err++;
69617721Speter	    }
69717721Speter	}
69817721Speter    }
69917721Speter    dbm_close (db);
70054431Speter    if (fclose (fp) < 0)
70154431Speter	error (0, errno, "cannot close %s", temp);
70217721Speter    if (err)
70317721Speter    {
70454431Speter	/* I think that the size of the buffer needed here is
70554431Speter	   just determined by sizeof (CVSROOTADM_MODULES), the
70654431Speter	   filenames created by make_tempfile, and other things that won't
70754431Speter	   overflow.  */
70817721Speter	char dotdir[50], dotpag[50], dotdb[50];
70917721Speter
71017721Speter	(void) sprintf (dotdir, "%s.dir", temp);
71117721Speter	(void) sprintf (dotpag, "%s.pag", temp);
71217721Speter	(void) sprintf (dotdb, "%s.db", temp);
71354431Speter	if (unlink_file (dotdir) < 0
71454431Speter	    && !existence_error (errno))
71554431Speter	    error (0, errno, "cannot remove %s", dotdir);
71654431Speter	if (unlink_file (dotpag) < 0
71754431Speter	    && !existence_error (errno))
71854431Speter	    error (0, errno, "cannot remove %s", dotpag);
71954431Speter	if (unlink_file (dotdb) < 0
72054431Speter	    && !existence_error (errno))
72154431Speter	    error (0, errno, "cannot remove %s", dotdb);
72217721Speter	error (1, 0, "DBM creation failed; correct above errors");
72317721Speter    }
72417721Speter}
72517721Speter
72617721Speterstatic void
72717721Speterrename_dbmfile (temp)
72817721Speter    char *temp;
72917721Speter{
73054431Speter    /* I think that the size of the buffer needed here is
73154431Speter       just determined by sizeof (CVSROOTADM_MODULES), the
73254431Speter       filenames created by make_tempfile, and other things that won't
73354431Speter       overflow.  */
73417721Speter    char newdir[50], newpag[50], newdb[50];
73517721Speter    char dotdir[50], dotpag[50], dotdb[50];
73617721Speter    char bakdir[50], bakpag[50], bakdb[50];
73717721Speter
73854431Speter    int dir1_errno = 0, pag1_errno = 0, db1_errno = 0;
73954431Speter    int dir2_errno = 0, pag2_errno = 0, db2_errno = 0;
74054431Speter    int dir3_errno = 0, pag3_errno = 0, db3_errno = 0;
74154431Speter
74217721Speter    (void) sprintf (dotdir, "%s.dir", CVSROOTADM_MODULES);
74317721Speter    (void) sprintf (dotpag, "%s.pag", CVSROOTADM_MODULES);
74417721Speter    (void) sprintf (dotdb, "%s.db", CVSROOTADM_MODULES);
74517721Speter    (void) sprintf (bakdir, "%s%s.dir", BAKPREFIX, CVSROOTADM_MODULES);
74617721Speter    (void) sprintf (bakpag, "%s%s.pag", BAKPREFIX, CVSROOTADM_MODULES);
74717721Speter    (void) sprintf (bakdb, "%s%s.db", BAKPREFIX, CVSROOTADM_MODULES);
74817721Speter    (void) sprintf (newdir, "%s.dir", temp);
74917721Speter    (void) sprintf (newpag, "%s.pag", temp);
75017721Speter    (void) sprintf (newdb, "%s.db", temp);
75117721Speter
75217721Speter    (void) chmod (newdir, 0666);
75317721Speter    (void) chmod (newpag, 0666);
75417721Speter    (void) chmod (newdb, 0666);
75517721Speter
75617721Speter    /* don't mess with me */
75717721Speter    SIG_beginCrSect ();
75817721Speter
75954431Speter    /* rm .#modules.dir .#modules.pag */
76054431Speter    if (unlink_file (bakdir) < 0)
76154431Speter	dir1_errno = errno;
76254431Speter    if (unlink_file (bakpag) < 0)
76354431Speter	pag1_errno = errno;
76454431Speter    if (unlink_file (bakdb) < 0)
76554431Speter	db1_errno = errno;
76617721Speter
76754431Speter    /* mv modules.dir .#modules.dir */
76854431Speter    if (CVS_RENAME (dotdir, bakdir) < 0)
76954431Speter	dir2_errno = errno;
77054431Speter    /* mv modules.pag .#modules.pag */
77154431Speter    if (CVS_RENAME (dotpag, bakpag) < 0)
77254431Speter	pag2_errno = errno;
77354431Speter    /* mv modules.db .#modules.db */
77454431Speter    if (CVS_RENAME (dotdb, bakdb) < 0)
77554431Speter	db2_errno = errno;
77654431Speter
77754431Speter    /* mv "temp".dir modules.dir */
77854431Speter    if (CVS_RENAME (newdir, dotdir) < 0)
77954431Speter	dir3_errno = errno;
78054431Speter    /* mv "temp".pag modules.pag */
78154431Speter    if (CVS_RENAME (newpag, dotpag) < 0)
78254431Speter	pag3_errno = errno;
78354431Speter    /* mv "temp".db modules.db */
78454431Speter    if (CVS_RENAME (newdb, dotdb) < 0)
78554431Speter	db3_errno = errno;
78654431Speter
78717721Speter    /* OK -- make my day */
78817721Speter    SIG_endCrSect ();
78954431Speter
79054431Speter    /* I didn't want to call error() when we had signals blocked
79154431Speter       (unnecessary?), but do it now.  */
79254431Speter    if (dir1_errno && !existence_error (dir1_errno))
79354431Speter	error (0, dir1_errno, "cannot remove %s", bakdir);
79454431Speter    if (pag1_errno && !existence_error (pag1_errno))
79554431Speter	error (0, pag1_errno, "cannot remove %s", bakpag);
79654431Speter    if (db1_errno && !existence_error (db1_errno))
79754431Speter	error (0, db1_errno, "cannot remove %s", bakdb);
79854431Speter
79954431Speter    if (dir2_errno && !existence_error (dir2_errno))
80054431Speter	error (0, dir2_errno, "cannot remove %s", bakdir);
80154431Speter    if (pag2_errno && !existence_error (pag2_errno))
80254431Speter	error (0, pag2_errno, "cannot remove %s", bakpag);
80354431Speter    if (db2_errno && !existence_error (db2_errno))
80454431Speter	error (0, db2_errno, "cannot remove %s", bakdb);
80554431Speter
80654431Speter    if (dir3_errno && !existence_error (dir3_errno))
80754431Speter	error (0, dir3_errno, "cannot remove %s", bakdir);
80854431Speter    if (pag3_errno && !existence_error (pag3_errno))
80954431Speter	error (0, pag3_errno, "cannot remove %s", bakpag);
81054431Speter    if (db3_errno && !existence_error (db3_errno))
81154431Speter	error (0, db3_errno, "cannot remove %s", bakdb);
81217721Speter}
81317721Speter
81417721Speter#endif				/* !MY_NDBM */
81517721Speter
81617721Speterstatic void
81717721Speterrename_rcsfile (temp, real)
81817721Speter    char *temp;
81917721Speter    char *real;
82017721Speter{
82125843Speter    char *bak;
82217721Speter    struct stat statbuf;
82325843Speter    char *rcs;
82425843Speter
82517721Speter    /* Set "x" bits if set in original. */
82625843Speter    rcs = xmalloc (strlen (real) + sizeof (RCSEXT) + 10);
82717721Speter    (void) sprintf (rcs, "%s%s", real, RCSEXT);
82817721Speter    statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */
82954431Speter    if (CVS_STAT (rcs, &statbuf) < 0
83054431Speter	&& !existence_error (errno))
83154431Speter	error (0, errno, "cannot stat %s", rcs);
83225843Speter    free (rcs);
83317721Speter
83417721Speter    if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0)
83517721Speter	error (0, errno, "warning: cannot chmod %s", temp);
83625843Speter    bak = xmalloc (strlen (real) + sizeof (BAKPREFIX) + 10);
83717721Speter    (void) sprintf (bak, "%s%s", BAKPREFIX, real);
83854431Speter
83954431Speter    /* rm .#loginfo */
84054431Speter    if (unlink_file (bak) < 0
84154431Speter	&& !existence_error (errno))
84254431Speter	error (0, errno, "cannot remove %s", bak);
84354431Speter
84454431Speter    /* mv loginfo .#loginfo */
84554431Speter    if (CVS_RENAME (real, bak) < 0
84654431Speter	&& !existence_error (errno))
84754431Speter	error (0, errno, "cannot rename %s to %s", real, bak);
84854431Speter
84954431Speter    /* mv "temp" loginfo */
85054431Speter    if (CVS_RENAME (temp, real) < 0
85154431Speter	&& !existence_error (errno))
85254431Speter	error (0, errno, "cannot rename %s to %s", temp, real);
85354431Speter
85425843Speter    free (bak);
85517721Speter}
856177404Sobrien
857177404Sobrien/*
858177404Sobrien * Walk PATH backwards to the root directory looking for the root of a
859177404Sobrien * repository.
860177404Sobrien */
861177404Sobrienstatic char *
862177404Sobrienin_repository (const char *path)
863177404Sobrien{
864177404Sobrien    char *cp = xstrdup (path);
865177404Sobrien
866177404Sobrien    for (;;)
867177404Sobrien    {
868177404Sobrien	if (isdir (cp))
869177404Sobrien	{
870177404Sobrien	    int foundit;
871177404Sobrien	    char *adm = xmalloc (strlen(cp) + strlen(CVSROOTADM) + 2);
872177404Sobrien	    sprintf (adm, "%s/%s", cp, CVSROOTADM);
873177404Sobrien	    foundit = isdir (adm);
874177404Sobrien	    free (adm);
875177404Sobrien	    if (foundit) return cp;
876177404Sobrien	}
877177404Sobrien
878177404Sobrien	/* If last_component() returns the empty string, then cp either
879177404Sobrien	 * points at the system root or is the empty string itself.
880177404Sobrien	 */
881177404Sobrien	if (!*last_component (cp) || !strcmp (cp, ".")
882177404Sobrien	    || last_component(cp) == cp)
883177404Sobrien	    break;
884177404Sobrien
885177404Sobrien	cp[strlen(cp) - strlen(last_component(cp)) - 1] = '\0';
886177404Sobrien    }
887177404Sobrien
888177404Sobrien    return NULL;
889177404Sobrien}
890177404Sobrien
89117721Speter
89217721Speterconst char *const init_usage[] = {
89317721Speter    "Usage: %s %s\n",
89432788Speter    "(Specify the --help global option for a list of other help options)\n",
89517721Speter    NULL
89617721Speter};
89717721Speter
89817721Speterint
89917721Speterinit (argc, argv)
90017721Speter    int argc;
90117721Speter    char **argv;
90217721Speter{
90317721Speter    /* Name of CVSROOT directory.  */
90417721Speter    char *adm;
90517721Speter    /* Name of this administrative file.  */
90617721Speter    char *info;
90717721Speter    /* Name of ,v file for this administrative file.  */
90817721Speter    char *info_v;
90926804Speter    /* Exit status.  */
910128269Speter    int err = 0;
91117721Speter
912177404Sobrien    char *root_dir;
91317721Speter    const struct admin_file *fileptr;
91417721Speter
915177404Sobrien    assert (!server_active);
916177404Sobrien
91717721Speter    umask (cvsumask);
91817721Speter
91918592Sjdp    if (argc == -1 || argc > 1)
92017721Speter	usage (init_usage);
92117721Speter
92225843Speter#ifdef CLIENT_SUPPORT
92381407Speter    if (current_parsed_root->isremote)
92417721Speter    {
92517721Speter	start_server ();
92617721Speter
92717721Speter	ign_setup ();
92817721Speter	send_init_command ();
92917721Speter	return get_responses_and_close ();
93017721Speter    }
93125843Speter#endif /* CLIENT_SUPPORT */
93217721Speter
933177404Sobrien    root_dir = in_repository (current_parsed_root->directory);
934177404Sobrien
935177404Sobrien    if (root_dir && strcmp (root_dir, current_parsed_root->directory))
936177404Sobrien	error (1, 0,
937177404Sobrien	       "Cannot initialize repository under existing CVSROOT: `%s'",
938177404Sobrien	       root_dir);
939177404Sobrien    free (root_dir);
940177404Sobrien
94117721Speter    /* Note: we do *not* create parent directories as needed like the
94217721Speter       old cvsinit.sh script did.  Few utilities do that, and a
94317721Speter       non-existent parent directory is as likely to be a typo as something
94417721Speter       which needs to be created.  */
94581407Speter    mkdir_if_needed (current_parsed_root->directory);
94617721Speter
94781407Speter    adm = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + 2);
94881407Speter    sprintf (adm, "%s/%s", current_parsed_root->directory, CVSROOTADM);
94917721Speter    mkdir_if_needed (adm);
95017721Speter
95126804Speter    /* This is needed because we pass "fileptr->filename" not "info"
95226804Speter       to add_rcs_file below.  I think this would be easy to change,
95326804Speter       thus nuking the need for CVS_CHDIR here, but I haven't looked
95426804Speter       closely (e.g. see wrappers calls within add_rcs_file).  */
95525843Speter    if ( CVS_CHDIR (adm) < 0)
95617721Speter	error (1, errno, "cannot change to directory %s", adm);
95717721Speter
95866528Speter    /* Make Emptydir so it's there if we need it */
95966528Speter    mkdir_if_needed (CVSNULLREPOS);
96066528Speter
96117721Speter    /* 80 is long enough for all the administrative file names, plus
96217721Speter       "/" and so on.  */
96317721Speter    info = xmalloc (strlen (adm) + 80);
96417721Speter    info_v = xmalloc (strlen (adm) + 80);
96517721Speter    for (fileptr = filelist; fileptr && fileptr->filename; ++fileptr)
96617721Speter    {
96717721Speter	if (fileptr->contents == NULL)
96817721Speter	    continue;
96917721Speter	strcpy (info, adm);
97017721Speter	strcat (info, "/");
97117721Speter	strcat (info, fileptr->filename);
97217721Speter	strcpy (info_v, info);
97317721Speter	strcat (info_v, RCSEXT);
97417721Speter	if (isfile (info_v))
97517721Speter	    /* We will check out this file in the mkmodules step.
97617721Speter	       Nothing else is required.  */
97717721Speter	    ;
97817721Speter	else
97917721Speter	{
98017721Speter	    int retcode;
98117721Speter
98217721Speter	    if (!isfile (info))
98317721Speter	    {
98417721Speter		FILE *fp;
98517721Speter		const char * const *p;
98617721Speter
98717721Speter		fp = open_file (info, "w");
98817721Speter		for (p = fileptr->contents; *p != NULL; ++p)
98917721Speter		    if (fputs (*p, fp) < 0)
99017721Speter			error (1, errno, "cannot write %s", info);
99117721Speter		if (fclose (fp) < 0)
99217721Speter		    error (1, errno, "cannot close %s", info);
99317721Speter	    }
99426804Speter	    /* The message used to say " of " and fileptr->filename after
99526804Speter	       "initial checkin" but I fail to see the point as we know what
99626804Speter	       file it is from the name.  */
99726804Speter	    retcode = add_rcs_file ("initial checkin", info_v,
99832788Speter				    fileptr->filename, "1.1", NULL,
99932788Speter
100032788Speter				    /* No vendor branch.  */
100132788Speter				    NULL, NULL, 0, NULL,
100232788Speter
100332788Speter				    NULL, 0, NULL);
100417721Speter	    if (retcode != 0)
100526804Speter		/* add_rcs_file already printed an error message.  */
100626804Speter		err = 1;
100717721Speter	}
100817721Speter    }
100917721Speter
101017721Speter    /* Turn on history logging by default.  The user can remove the file
101117721Speter       to disable it.  */
101217721Speter    strcpy (info, adm);
101317721Speter    strcat (info, "/");
101417721Speter    strcat (info, CVSROOTADM_HISTORY);
101517721Speter    if (!isfile (info))
101617721Speter    {
101717721Speter	FILE *fp;
101817721Speter
101917721Speter	fp = open_file (info, "w");
102017721Speter	if (fclose (fp) < 0)
102117721Speter	    error (1, errno, "cannot close %s", info);
102266528Speter
102366528Speter        /* Make the new history file world-writeable, since every CVS
102466528Speter           user will need to be able to write to it.  We use chmod()
102566528Speter           because xchmod() is too shy. */
102666528Speter        chmod (info, 0666);
102717721Speter    }
102817721Speter
102966528Speter    /* Make an empty val-tags file to prevent problems creating it later.  */
103066528Speter    strcpy (info, adm);
103166528Speter    strcat (info, "/");
103266528Speter    strcat (info, CVSROOTADM_VALTAGS);
103366528Speter    if (!isfile (info))
103466528Speter    {
103566528Speter	FILE *fp;
103666528Speter
103766528Speter	fp = open_file (info, "w");
103866528Speter	if (fclose (fp) < 0)
103966528Speter	    error (1, errno, "cannot close %s", info);
104066528Speter
104166528Speter        /* Make the new val-tags file world-writeable, since every CVS
104266528Speter           user will need to be able to write to it.  We use chmod()
104366528Speter           because xchmod() is too shy. */
104466528Speter        chmod (info, 0666);
104566528Speter    }
104666528Speter
104717721Speter    free (info);
104817721Speter    free (info_v);
104917721Speter
105017721Speter    mkmodules (adm);
105117721Speter
105217721Speter    free (adm);
1053128269Speter    return err;
105417721Speter}
1055