xref: /dflybsd-src/contrib/cvs-1.12/src/mkmodules.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3*86d7f5d3SJohn Marino  *
4*86d7f5d3SJohn Marino  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5*86d7f5d3SJohn Marino  *                                  and others.
6*86d7f5d3SJohn Marino  *
7*86d7f5d3SJohn Marino  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8*86d7f5d3SJohn Marino  * Portions Copyright (C) 1989-1992, Brian Berliner
9*86d7f5d3SJohn Marino  *
10*86d7f5d3SJohn Marino  * You may distribute under the terms of the GNU General Public License as
11*86d7f5d3SJohn Marino  * specified in the README file that comes with the CVS kit.  */
12*86d7f5d3SJohn Marino 
13*86d7f5d3SJohn Marino #include "cvs.h"
14*86d7f5d3SJohn Marino #include "getline.h"
15*86d7f5d3SJohn Marino #include "history.h"
16*86d7f5d3SJohn Marino #include "save-cwd.h"
17*86d7f5d3SJohn Marino 
18*86d7f5d3SJohn Marino #ifndef DBLKSIZ
19*86d7f5d3SJohn Marino #define	DBLKSIZ	4096			/* since GNU ndbm doesn't define it */
20*86d7f5d3SJohn Marino #endif
21*86d7f5d3SJohn Marino 
22*86d7f5d3SJohn Marino static int checkout_file (char *file, char *temp);
23*86d7f5d3SJohn Marino static char *make_tempfile (void);
24*86d7f5d3SJohn Marino static void rename_rcsfile (char *temp, char *real);
25*86d7f5d3SJohn Marino 
26*86d7f5d3SJohn Marino #ifndef MY_NDBM
27*86d7f5d3SJohn Marino static void rename_dbmfile (char *temp);
28*86d7f5d3SJohn Marino static void write_dbmfile (char *temp);
29*86d7f5d3SJohn Marino #endif				/* !MY_NDBM */
30*86d7f5d3SJohn Marino 
31*86d7f5d3SJohn Marino /* Structure which describes an administrative file.  */
32*86d7f5d3SJohn Marino struct admin_file {
33*86d7f5d3SJohn Marino    /* Name of the file, within the CVSROOT directory.  */
34*86d7f5d3SJohn Marino    char *filename;
35*86d7f5d3SJohn Marino 
36*86d7f5d3SJohn Marino    /* This is a one line description of what the file is for.  It is not
37*86d7f5d3SJohn Marino       currently used, although one wonders whether it should be, somehow.
38*86d7f5d3SJohn Marino       If NULL, then don't process this file in mkmodules (FIXME?: a bit of
39*86d7f5d3SJohn Marino       a kludge; probably should replace this with a flags field).  */
40*86d7f5d3SJohn Marino    char *errormsg;
41*86d7f5d3SJohn Marino 
42*86d7f5d3SJohn Marino    /* Contents which the file should have in a new repository.  To avoid
43*86d7f5d3SJohn Marino       problems with brain-dead compilers which choke on long string constants,
44*86d7f5d3SJohn Marino       this is a pointer to an array of char * terminated by NULL--each of
45*86d7f5d3SJohn Marino       the strings is concatenated.
46*86d7f5d3SJohn Marino 
47*86d7f5d3SJohn Marino       If this field is NULL, the file is not created in a new
48*86d7f5d3SJohn Marino       repository, but it can be added with "cvs add" (just as if one
49*86d7f5d3SJohn Marino       had created the repository with a version of CVS which didn't
50*86d7f5d3SJohn Marino       know about the file) and the checked-out copy will be updated
51*86d7f5d3SJohn Marino       without having to add it to checkoutlist.  */
52*86d7f5d3SJohn Marino    const char * const *contents;
53*86d7f5d3SJohn Marino };
54*86d7f5d3SJohn Marino 
55*86d7f5d3SJohn Marino static const char *const loginfo_contents[] = {
56*86d7f5d3SJohn Marino     "# The \"loginfo\" file controls where \"cvs commit\" log information is\n",
57*86d7f5d3SJohn Marino     "# sent. The first entry on a line is a regular expression which must\n",
58*86d7f5d3SJohn Marino     "# match the directory that the change is being made to, relative to the\n",
59*86d7f5d3SJohn Marino     "# $CVSROOT.  If a match is found, then the remainder of the line is a\n",
60*86d7f5d3SJohn Marino     "# filter program that should expect log information on its standard input.\n",
61*86d7f5d3SJohn Marino     "#\n",
62*86d7f5d3SJohn Marino     "# If the repository name does not match any of the regular expressions in this\n",
63*86d7f5d3SJohn Marino     "# file, the \"DEFAULT\" line is used, if it is specified.\n",
64*86d7f5d3SJohn Marino     "#\n",
65*86d7f5d3SJohn Marino     "# If the name ALL appears as a regular expression it is always used\n",
66*86d7f5d3SJohn Marino     "# in addition to the first matching regex or DEFAULT.\n",
67*86d7f5d3SJohn Marino     "#\n",
68*86d7f5d3SJohn Marino     "# If any format strings are present in the filter, they will be replaced\n",
69*86d7f5d3SJohn Marino     "# as follows:\n",
70*86d7f5d3SJohn Marino     "#    %c = canonical name of the command being executed\n",
71*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
72*86d7f5d3SJohn Marino     "#    %R = the name of the referrer, if any, otherwise the value NONE\n",
73*86d7f5d3SJohn Marino #endif
74*86d7f5d3SJohn Marino     "#    %p = path relative to repository\n",
75*86d7f5d3SJohn Marino     "#    %r = repository (path portion of $CVSROOT)\n",
76*86d7f5d3SJohn Marino     "#    %{sVv} = attribute list = file name, old version number (pre-checkin),\n",
77*86d7f5d3SJohn Marino     "#           new version number (post-checkin).  When either old or new revision\n",
78*86d7f5d3SJohn Marino     "#           is unknown, doesn't exist, or isn't applicable, the string \"NONE\"\n",
79*86d7f5d3SJohn Marino     "#           will be placed on the command line instead.\n",
80*86d7f5d3SJohn Marino     "#\n",
81*86d7f5d3SJohn Marino     "# Note that %{sVv} is a list operator and not all elements are necessary.\n",
82*86d7f5d3SJohn Marino     "# Thus %{sv} is a legal format string, but will only be replaced with\n",
83*86d7f5d3SJohn Marino     "# file name and new revision.\n",
84*86d7f5d3SJohn Marino     "# It also generates multiple arguments for each file being operated upon.\n",
85*86d7f5d3SJohn Marino     "# That is, if two files, file1 & file2, are being commited from 1.1 to\n",
86*86d7f5d3SJohn Marino     "# version 1.1.2.1 and from 1.1.2.2 to 1.1.2.3, respectively, %{sVv} will\n",
87*86d7f5d3SJohn Marino     "# generate the following six arguments in this order:\n",
88*86d7f5d3SJohn Marino     "# file1, 1.1, 1.1.2.1, file2, 1.1.2.2, 1.1.2.3.\n",
89*86d7f5d3SJohn Marino     "#\n",
90*86d7f5d3SJohn Marino     "# For example:\n",
91*86d7f5d3SJohn Marino     "#DEFAULT (echo \"\"; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog\n",
92*86d7f5d3SJohn Marino     "# or\n",
93*86d7f5d3SJohn Marino     "#DEFAULT (echo \"\"; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog\n",
94*86d7f5d3SJohn Marino     NULL
95*86d7f5d3SJohn Marino };
96*86d7f5d3SJohn Marino 
97*86d7f5d3SJohn Marino static const char *const rcsinfo_contents[] = {
98*86d7f5d3SJohn Marino     "# The \"rcsinfo\" file is used to control templates with which the editor\n",
99*86d7f5d3SJohn Marino     "# is invoked on commit and import.\n",
100*86d7f5d3SJohn Marino     "#\n",
101*86d7f5d3SJohn Marino     "# The first entry on a line is a regular expression which is tested\n",
102*86d7f5d3SJohn Marino     "# against the directory that the change is being made to, relative to the\n",
103*86d7f5d3SJohn Marino     "# $CVSROOT.  For the first match that is found, then the remainder of the\n",
104*86d7f5d3SJohn Marino     "# line is the name of the file that contains the template.\n",
105*86d7f5d3SJohn Marino     "#\n",
106*86d7f5d3SJohn Marino     "# If the repository name does not match any of the regular expressions in this\n",
107*86d7f5d3SJohn Marino     "# file, the \"DEFAULT\" line is used, if it is specified.\n",
108*86d7f5d3SJohn Marino     "#\n",
109*86d7f5d3SJohn Marino     "# If the name \"ALL\" appears as a regular expression it is always used\n",
110*86d7f5d3SJohn Marino     "# in addition to the first matching regex or \"DEFAULT\".\n",
111*86d7f5d3SJohn Marino     NULL
112*86d7f5d3SJohn Marino };
113*86d7f5d3SJohn Marino 
114*86d7f5d3SJohn Marino 
115*86d7f5d3SJohn Marino 
116*86d7f5d3SJohn Marino static const char *const verifymsg_contents[] = {
117*86d7f5d3SJohn Marino     "# The \"verifymsg\" file is used to allow verification of logging\n",
118*86d7f5d3SJohn Marino     "# information.  It works best when a template (as specified in the\n",
119*86d7f5d3SJohn Marino     "# rcsinfo file) is provided for the logging procedure.  Given a\n",
120*86d7f5d3SJohn Marino     "# template with locations for, a bug-id number, a list of people who\n",
121*86d7f5d3SJohn Marino     "# reviewed the code before it can be checked in, and an external\n",
122*86d7f5d3SJohn Marino     "# process to catalog the differences that were code reviewed, the\n",
123*86d7f5d3SJohn Marino     "# following test can be applied to the code:\n",
124*86d7f5d3SJohn Marino     "#\n",
125*86d7f5d3SJohn Marino     "#   Making sure that the entered bug-id number is correct.\n",
126*86d7f5d3SJohn Marino     "#   Validating that the code that was reviewed is indeed the code being\n",
127*86d7f5d3SJohn Marino     "#       checked in (using the bug-id number or a seperate review\n",
128*86d7f5d3SJohn Marino     "#       number to identify this particular code set.).\n",
129*86d7f5d3SJohn Marino     "#\n",
130*86d7f5d3SJohn Marino     "# If any of the above test failed, then the commit would be aborted.\n",
131*86d7f5d3SJohn Marino     "#\n",
132*86d7f5d3SJohn Marino     "# Format strings present in the filter will be replaced as follows:\n",
133*86d7f5d3SJohn Marino     "#    %c = canonical name of the command being executed\n",
134*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
135*86d7f5d3SJohn Marino     "#    %R = the name of the referrer, if any, otherwise the value NONE\n",
136*86d7f5d3SJohn Marino #endif
137*86d7f5d3SJohn Marino     "#    %p = path relative to repository\n",
138*86d7f5d3SJohn Marino     "#    %r = repository (path portion of $CVSROOT)\n",
139*86d7f5d3SJohn Marino     "#    %l = name of log file to be verified.\n",
140*86d7f5d3SJohn Marino     "#\n",
141*86d7f5d3SJohn Marino     "# If no format strings are present in the filter, a default \" %l\" will\n",
142*86d7f5d3SJohn Marino     "# be appended to the filter, but this usage is deprecated.\n",
143*86d7f5d3SJohn Marino     "#\n",
144*86d7f5d3SJohn Marino     "# Actions such as mailing a copy of the report to each reviewer are\n",
145*86d7f5d3SJohn Marino     "# better handled by an entry in the loginfo file.\n",
146*86d7f5d3SJohn Marino     "#\n",
147*86d7f5d3SJohn Marino     "# One thing that should be noted is the the ALL keyword is not\n",
148*86d7f5d3SJohn Marino     "# supported.  There can be only one entry that matches a given\n",
149*86d7f5d3SJohn Marino     "# repository.\n",
150*86d7f5d3SJohn Marino     NULL
151*86d7f5d3SJohn Marino };
152*86d7f5d3SJohn Marino 
153*86d7f5d3SJohn Marino static const char *const commitinfo_contents[] = {
154*86d7f5d3SJohn Marino     "# The \"commitinfo\" file is used to control pre-commit checks.\n",
155*86d7f5d3SJohn Marino     "# The filter on the right is invoked with the repository and a list \n",
156*86d7f5d3SJohn Marino     "# of files to check.  A non-zero exit of the filter program will \n",
157*86d7f5d3SJohn Marino     "# cause the commit to be aborted.\n",
158*86d7f5d3SJohn Marino     "#\n",
159*86d7f5d3SJohn Marino     "# The first entry on a line is a regular expression which is tested\n",
160*86d7f5d3SJohn Marino     "# against the directory that the change is being committed to, relative\n",
161*86d7f5d3SJohn Marino     "# to the $CVSROOT.  For the first match that is found, then the remainder\n",
162*86d7f5d3SJohn Marino     "# of the line is the name of the filter to run.\n",
163*86d7f5d3SJohn Marino     "#\n",
164*86d7f5d3SJohn Marino     "# Format strings present in the filter will be replaced as follows:\n",
165*86d7f5d3SJohn Marino     "#    %c = canonical name of the command being executed\n",
166*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
167*86d7f5d3SJohn Marino     "#    %R = the name of the referrer, if any, otherwise the value NONE\n",
168*86d7f5d3SJohn Marino #endif
169*86d7f5d3SJohn Marino     "#    %p = path relative to repository\n",
170*86d7f5d3SJohn Marino     "#    %r = repository (path portion of $CVSROOT)\n",
171*86d7f5d3SJohn Marino     "#    %{s} = file name, file name, ...\n",
172*86d7f5d3SJohn Marino     "#\n",
173*86d7f5d3SJohn Marino     "# If no format strings are present in the filter string, a default of\n",
174*86d7f5d3SJohn Marino     "# \" %r %s\" will be appended to the filter string, but this usage is\n",
175*86d7f5d3SJohn Marino     "# deprecated.\n",
176*86d7f5d3SJohn Marino     "#\n",
177*86d7f5d3SJohn Marino     "# If the repository name does not match any of the regular expressions in this\n",
178*86d7f5d3SJohn Marino     "# file, the \"DEFAULT\" line is used, if it is specified.\n",
179*86d7f5d3SJohn Marino     "#\n",
180*86d7f5d3SJohn Marino     "# If the name \"ALL\" appears as a regular expression it is always used\n",
181*86d7f5d3SJohn Marino     "# in addition to the first matching regex or \"DEFAULT\".\n",
182*86d7f5d3SJohn Marino     NULL
183*86d7f5d3SJohn Marino };
184*86d7f5d3SJohn Marino 
185*86d7f5d3SJohn Marino static const char *const taginfo_contents[] = {
186*86d7f5d3SJohn Marino     "# The \"taginfo\" file is used to control pre-tag checks.\n",
187*86d7f5d3SJohn Marino     "# The filter on the right is invoked with the following arguments\n",
188*86d7f5d3SJohn Marino     "# if no format strings are present:\n",
189*86d7f5d3SJohn Marino     "#\n",
190*86d7f5d3SJohn Marino     "# $1 -- tagname\n",
191*86d7f5d3SJohn Marino     "# $2 -- operation \"add\" for tag, \"mov\" for tag -F, and \"del\" for tag -d\n",
192*86d7f5d3SJohn Marino     "# $3 -- tagtype \"?\" on delete, \"T\" for branch, \"N\" for static\n",
193*86d7f5d3SJohn Marino     "# $4 -- repository\n",
194*86d7f5d3SJohn Marino     "# $5->  file revision [file revision ...]\n",
195*86d7f5d3SJohn Marino     "#\n",
196*86d7f5d3SJohn Marino     "# If any format strings are present in the filter, they will be replaced\n",
197*86d7f5d3SJohn Marino     "# as follows:\n",
198*86d7f5d3SJohn Marino     "#    %b = branch mode = \"?\" (delete ops - unknown) | \"T\" (branch)\n",
199*86d7f5d3SJohn Marino     "#                     | \"N\" (not branch)\n",
200*86d7f5d3SJohn Marino     "#    %o = operation = \"add\" | \"mov\" | \"del\"\n",
201*86d7f5d3SJohn Marino     "#    %c = canonical name of the command being executed\n",
202*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
203*86d7f5d3SJohn Marino     "#    %R = the name of the referrer, if any, otherwise the value NONE\n",
204*86d7f5d3SJohn Marino #endif
205*86d7f5d3SJohn Marino     "#    %p = path relative to repository\n",
206*86d7f5d3SJohn Marino     "#    %r = repository (path portion of $CVSROOT)\n",
207*86d7f5d3SJohn Marino     "#    %t = tagname\n",
208*86d7f5d3SJohn Marino     "#    %{sVv} = attribute list = file name, old version tag will be deleted\n",
209*86d7f5d3SJohn Marino     "#             from, new version tag will be added to (or deleted from, but\n",
210*86d7f5d3SJohn Marino     "#             this feature is deprecated.  When either old or new revision is\n",
211*86d7f5d3SJohn Marino     "#             unknown, doesn't exist, or isn't applicable, the string \"NONE\"\n",
212*86d7f5d3SJohn Marino     "#             will be placed on the command line.\n",
213*86d7f5d3SJohn Marino     "#\n",
214*86d7f5d3SJohn Marino     "# Note that %{sVv} is a list operator and not all elements are necessary.\n",
215*86d7f5d3SJohn Marino     "# Thus %{sV} is a legal format string, but will only be replaced with file\n",
216*86d7f5d3SJohn Marino     "# name and old revision. it also generates multiple arguments for each file\n",
217*86d7f5d3SJohn Marino     "# being operated upon.  i.e. if two files, file1 & file2, are having a tag\n",
218*86d7f5d3SJohn Marino     "# moved from version 1.1 to version 1.1.2.9, %{sVv} will generate the\n",
219*86d7f5d3SJohn Marino     "# following six arguments in this order:\n",
220*86d7f5d3SJohn Marino     "# file1, 1.1, 1.1.2.9, file2, 1.1, 1.1.2.9.\n",
221*86d7f5d3SJohn Marino     "#\n",
222*86d7f5d3SJohn Marino     "# A non-zero exit of the filter program will cause the tag to be aborted.\n",
223*86d7f5d3SJohn Marino     "#\n",
224*86d7f5d3SJohn Marino     "# The first entry on a line is a regular expression which is tested\n",
225*86d7f5d3SJohn Marino     "# against the directory that the change is being committed to, relative\n",
226*86d7f5d3SJohn Marino     "# to the $CVSROOT.  For the first match that is found, then the remainder\n",
227*86d7f5d3SJohn Marino     "# of the line is the name of the filter to run.\n",
228*86d7f5d3SJohn Marino     "#\n",
229*86d7f5d3SJohn Marino     "# If the repository name does not match any of the regular expressions in this\n",
230*86d7f5d3SJohn Marino     "# file, the \"DEFAULT\" line is used, if it is specified.\n",
231*86d7f5d3SJohn Marino     "#\n",
232*86d7f5d3SJohn Marino     "# If the name \"ALL\" appears as a regular expression it is always used\n",
233*86d7f5d3SJohn Marino     "# in addition to the first matching regex or \"DEFAULT\".\n",
234*86d7f5d3SJohn Marino     NULL
235*86d7f5d3SJohn Marino };
236*86d7f5d3SJohn Marino 
237*86d7f5d3SJohn Marino static const char *const preproxy_contents[] = {
238*86d7f5d3SJohn Marino     "# The \"preproxy\" file is called form the secondary server as soon as\n",
239*86d7f5d3SJohn Marino     "# the secondary server determines that it will be proxying a write\n",
240*86d7f5d3SJohn Marino     "# command to a primary server and immediately before it opens a\n",
241*86d7f5d3SJohn Marino     "# connection to the primary server.  This script might, for example, be\n",
242*86d7f5d3SJohn Marino     "# used to launch a dial up or VPN connection to the primary server's\n",
243*86d7f5d3SJohn Marino     "# network.\n",
244*86d7f5d3SJohn Marino     "#\n",
245*86d7f5d3SJohn Marino     "# If any format strings are present in the filter, they will be replaced\n",
246*86d7f5d3SJohn Marino     "# as follows:\n",
247*86d7f5d3SJohn Marino     "#    %c = canonical name of the command being executed\n",
248*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
249*86d7f5d3SJohn Marino     "#    %R = the name of the referrer, if any, otherwise the value NONE\n",
250*86d7f5d3SJohn Marino #endif
251*86d7f5d3SJohn Marino     "#    %p = path relative to repository (currently always \".\")\n",
252*86d7f5d3SJohn Marino     "#    %r = repository (path portion of $CVSROOT)\n",
253*86d7f5d3SJohn Marino     "#\n",
254*86d7f5d3SJohn Marino     "# The first entry on a line is a regular expression which is tested\n",
255*86d7f5d3SJohn Marino     "# against the directory that the change is being committed to, relative\n",
256*86d7f5d3SJohn Marino     "# to the $CVSROOT.  For the first match that is found, then the remainder\n",
257*86d7f5d3SJohn Marino     "# of the line is the name of the filter to run.\n",
258*86d7f5d3SJohn Marino     "#\n",
259*86d7f5d3SJohn Marino     "# If the repository name does not match any of the regular expressions in this\n",
260*86d7f5d3SJohn Marino     "# file, the \"DEFAULT\" line is used, if it is specified.\n",
261*86d7f5d3SJohn Marino     "#\n",
262*86d7f5d3SJohn Marino     "# If the name \"ALL\" appears as a regular expression it is always used\n",
263*86d7f5d3SJohn Marino     "# in addition to the first matching regex or \"DEFAULT\".\n",
264*86d7f5d3SJohn Marino     NULL
265*86d7f5d3SJohn Marino };
266*86d7f5d3SJohn Marino 
267*86d7f5d3SJohn Marino static const char *const postadmin_contents[] = {
268*86d7f5d3SJohn Marino     "# The \"postadmin\" file is called after the \"admin\" command finishes\n",
269*86d7f5d3SJohn Marino     "# processing a directory.\n",
270*86d7f5d3SJohn Marino     "#\n",
271*86d7f5d3SJohn Marino     "# If any format strings are present in the filter, they will be replaced\n",
272*86d7f5d3SJohn Marino     "# as follows:\n",
273*86d7f5d3SJohn Marino     "#    %c = canonical name of the command being executed\n",
274*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
275*86d7f5d3SJohn Marino     "#    %R = the name of the referrer, if any, otherwise the value NONE\n",
276*86d7f5d3SJohn Marino #endif
277*86d7f5d3SJohn Marino     "#    %p = path relative to repository\n",
278*86d7f5d3SJohn Marino     "#    %r = repository (path portion of $CVSROOT)\n",
279*86d7f5d3SJohn Marino     "#\n",
280*86d7f5d3SJohn Marino     "# The first entry on a line is a regular expression which is tested\n",
281*86d7f5d3SJohn Marino     "# against the directory that the change is being committed to, relative\n",
282*86d7f5d3SJohn Marino     "# to the $CVSROOT.  For the first match that is found, then the remainder\n",
283*86d7f5d3SJohn Marino     "# of the line is the name of the filter to run.\n",
284*86d7f5d3SJohn Marino     "#\n",
285*86d7f5d3SJohn Marino     "# If the repository name does not match any of the regular expressions in this\n",
286*86d7f5d3SJohn Marino     "# file, the \"DEFAULT\" line is used, if it is specified.\n",
287*86d7f5d3SJohn Marino     "#\n",
288*86d7f5d3SJohn Marino     "# If the name \"ALL\" appears as a regular expression it is always used\n",
289*86d7f5d3SJohn Marino     "# in addition to the first matching regex or \"DEFAULT\".\n",
290*86d7f5d3SJohn Marino     NULL
291*86d7f5d3SJohn Marino };
292*86d7f5d3SJohn Marino 
293*86d7f5d3SJohn Marino static const char *const postproxy_contents[] = {
294*86d7f5d3SJohn Marino     "# The \"postproxy\" file is called from a secondary server as soon as\n",
295*86d7f5d3SJohn Marino     "# the secondary server closes its connection to the primary server.\n",
296*86d7f5d3SJohn Marino     "# This script might, for example, be used to shut down a dial up\n",
297*86d7f5d3SJohn Marino     "# or VPN connection to the primary server's network.\n",
298*86d7f5d3SJohn Marino     "#\n",
299*86d7f5d3SJohn Marino     "# If any format strings are present in the filter, they will be replaced\n",
300*86d7f5d3SJohn Marino     "# as follows:\n",
301*86d7f5d3SJohn Marino     "#    %c = canonical name of the command being executed\n",
302*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
303*86d7f5d3SJohn Marino     "#    %R = the name of the referrer, if any, otherwise the value NONE\n",
304*86d7f5d3SJohn Marino #endif
305*86d7f5d3SJohn Marino     "#    %p = path relative to repository (currently always \".\")\n",
306*86d7f5d3SJohn Marino     "#    %r = repository (path portion of $CVSROOT)\n",
307*86d7f5d3SJohn Marino     "#\n",
308*86d7f5d3SJohn Marino     "# The first entry on a line is a regular expression which is tested\n",
309*86d7f5d3SJohn Marino     "# against the directory that the change is being committed to, relative\n",
310*86d7f5d3SJohn Marino     "# to the $CVSROOT.  For the first match that is found, then the remainder\n",
311*86d7f5d3SJohn Marino     "# of the line is the name of the filter to run.\n",
312*86d7f5d3SJohn Marino     "#\n",
313*86d7f5d3SJohn Marino     "# If the repository name does not match any of the regular expressions in this\n",
314*86d7f5d3SJohn Marino     "# file, the \"DEFAULT\" line is used, if it is specified.\n",
315*86d7f5d3SJohn Marino     "#\n",
316*86d7f5d3SJohn Marino     "# If the name \"ALL\" appears as a regular expression it is always used\n",
317*86d7f5d3SJohn Marino     "# in addition to the first matching regex or \"DEFAULT\".\n",
318*86d7f5d3SJohn Marino     NULL
319*86d7f5d3SJohn Marino };
320*86d7f5d3SJohn Marino 
321*86d7f5d3SJohn Marino static const char *const posttag_contents[] = {
322*86d7f5d3SJohn Marino     "# The \"posttag\" file is called after the \"tag\" command finishes\n",
323*86d7f5d3SJohn Marino     "# processing a directory.\n",
324*86d7f5d3SJohn Marino     "#\n",
325*86d7f5d3SJohn Marino     "# If any format strings are present in the filter, they will be replaced\n",
326*86d7f5d3SJohn Marino     "# as follows:\n",
327*86d7f5d3SJohn Marino     "#    %b = branch mode = \"?\" (delete ops - unknown) | \"T\" (branch)\n",
328*86d7f5d3SJohn Marino     "#                     | \"N\" (not branch)\n",
329*86d7f5d3SJohn Marino     "#    %o = operation = \"add\" | \"mov\" | \"del\"\n",
330*86d7f5d3SJohn Marino     "#    %c = canonical name of the command being executed\n",
331*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
332*86d7f5d3SJohn Marino     "#    %R = the name of the referrer, if any, otherwise the value NONE\n",
333*86d7f5d3SJohn Marino #endif
334*86d7f5d3SJohn Marino     "#    %p = path relative to repository\n",
335*86d7f5d3SJohn Marino     "#    %r = repository (path portion of $CVSROOT)\n",
336*86d7f5d3SJohn Marino     "#    %t = tagname\n",
337*86d7f5d3SJohn Marino     "#    %{sVv} = attribute list = file name, old version tag will be deleted\n",
338*86d7f5d3SJohn Marino     "#             from, new version tag will be added to (or deleted from, but\n",
339*86d7f5d3SJohn Marino     "#             this feature is deprecated.  When either old or new revision is\n",
340*86d7f5d3SJohn Marino     "#             unknown, doesn't exist, or isn't applicable, the string \"NONE\"\n",
341*86d7f5d3SJohn Marino     "#             will be placed on the command line.\n",
342*86d7f5d3SJohn Marino     "#\n",
343*86d7f5d3SJohn Marino     "# Note that %{sVv} is a list operator and not all elements are necessary.\n",
344*86d7f5d3SJohn Marino     "# Thus %{sV} is a legal format string, but will only be replaced with file\n",
345*86d7f5d3SJohn Marino     "# name and old revision. it also generates multiple arguments for each file\n",
346*86d7f5d3SJohn Marino     "# being operated upon.  i.e. if two files, file1 & file2, are having a tag\n",
347*86d7f5d3SJohn Marino     "# moved from version 1.1 to version 1.1.2.9, %{sVv} will generate the\n",
348*86d7f5d3SJohn Marino     "# following six arguments in this order:\n",
349*86d7f5d3SJohn Marino     "# file1, 1.1, 1.1.2.9, file2, 1.1, 1.1.2.9.\n",
350*86d7f5d3SJohn Marino     "#\n",
351*86d7f5d3SJohn Marino     "# The first entry on a line is a regular expression which is tested\n",
352*86d7f5d3SJohn Marino     "# against the directory that the change is being committed to, relative\n",
353*86d7f5d3SJohn Marino     "# to the $CVSROOT.  For the first match that is found, then the remainder\n",
354*86d7f5d3SJohn Marino     "# of the line is the name of the filter to run.\n",
355*86d7f5d3SJohn Marino     "#\n",
356*86d7f5d3SJohn Marino     "# If the repository name does not match any of the regular expressions in this\n",
357*86d7f5d3SJohn Marino     "# file, the \"DEFAULT\" line is used, if it is specified.\n",
358*86d7f5d3SJohn Marino     "#\n",
359*86d7f5d3SJohn Marino     "# If the name \"ALL\" appears as a regular expression it is always used\n",
360*86d7f5d3SJohn Marino     "# in addition to the first matching regex or \"DEFAULT\".\n",
361*86d7f5d3SJohn Marino     NULL
362*86d7f5d3SJohn Marino };
363*86d7f5d3SJohn Marino 
364*86d7f5d3SJohn Marino static const char *const postwatch_contents[] = {
365*86d7f5d3SJohn Marino     "# The \"postwatch\" file is called after any command finishes writing new\n",
366*86d7f5d3SJohn Marino     "# file attibute (watch/edit) information in a directory.\n",
367*86d7f5d3SJohn Marino     "#\n",
368*86d7f5d3SJohn Marino     "# If any format strings are present in the filter, they will be replaced\n",
369*86d7f5d3SJohn Marino     "# as follows:\n",
370*86d7f5d3SJohn Marino     "#    %c = canonical name of the command being executed\n",
371*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
372*86d7f5d3SJohn Marino     "#    %R = the name of the referrer, if any, otherwise the value NONE\n",
373*86d7f5d3SJohn Marino #endif
374*86d7f5d3SJohn Marino     "#    %p = path relative to repository\n",
375*86d7f5d3SJohn Marino     "#    %r = repository (path portion of $CVSROOT)\n",
376*86d7f5d3SJohn Marino     "#\n",
377*86d7f5d3SJohn Marino     "# The first entry on a line is a regular expression which is tested\n",
378*86d7f5d3SJohn Marino     "# against the directory that the change is being committed to, relative\n",
379*86d7f5d3SJohn Marino     "# to the $CVSROOT.  For the first match that is found, then the remainder\n",
380*86d7f5d3SJohn Marino     "# of the line is the name of the filter to run.\n",
381*86d7f5d3SJohn Marino     "#\n",
382*86d7f5d3SJohn Marino     "# If the repository name does not match any of the regular expressions in this\n",
383*86d7f5d3SJohn Marino     "# file, the \"DEFAULT\" line is used, if it is specified.\n",
384*86d7f5d3SJohn Marino     "#\n",
385*86d7f5d3SJohn Marino     "# If the name \"ALL\" appears as a regular expression it is always used\n",
386*86d7f5d3SJohn Marino     "# in addition to the first matching regex or \"DEFAULT\".\n",
387*86d7f5d3SJohn Marino     NULL
388*86d7f5d3SJohn Marino };
389*86d7f5d3SJohn Marino 
390*86d7f5d3SJohn Marino static const char *const checkoutlist_contents[] = {
391*86d7f5d3SJohn Marino     "# The \"checkoutlist\" file is used to support additional version controlled\n",
392*86d7f5d3SJohn Marino     "# administrative files in $CVSROOT/CVSROOT, such as template files.\n",
393*86d7f5d3SJohn Marino     "#\n",
394*86d7f5d3SJohn Marino     "# The first entry on a line is a filename which will be checked out from\n",
395*86d7f5d3SJohn Marino     "# the corresponding RCS file in the $CVSROOT/CVSROOT directory.\n",
396*86d7f5d3SJohn Marino     "# The remainder of the line is an error message to use if the file cannot\n",
397*86d7f5d3SJohn Marino     "# be checked out.\n",
398*86d7f5d3SJohn Marino     "#\n",
399*86d7f5d3SJohn Marino     "# File format:\n",
400*86d7f5d3SJohn Marino     "#\n",
401*86d7f5d3SJohn Marino     "#	[<whitespace>]<filename>[<whitespace><error message>]<end-of-line>\n",
402*86d7f5d3SJohn Marino     "#\n",
403*86d7f5d3SJohn Marino     "# comment lines begin with '#'\n",
404*86d7f5d3SJohn Marino     NULL
405*86d7f5d3SJohn Marino };
406*86d7f5d3SJohn Marino 
407*86d7f5d3SJohn Marino static const char *const cvswrappers_contents[] = {
408*86d7f5d3SJohn Marino     "# This file affects handling of files based on their names.\n",
409*86d7f5d3SJohn Marino     "#\n",
410*86d7f5d3SJohn Marino #if 0    /* see comments in wrap_add in wrapper.c */
411*86d7f5d3SJohn Marino     "# The -t/-f options allow one to treat directories of files\n",
412*86d7f5d3SJohn Marino     "# as a single file, or to transform a file in other ways on\n",
413*86d7f5d3SJohn Marino     "# its way in and out of CVS.\n",
414*86d7f5d3SJohn Marino     "#\n",
415*86d7f5d3SJohn Marino #endif
416*86d7f5d3SJohn Marino     "# The -m option specifies whether CVS attempts to merge files.\n",
417*86d7f5d3SJohn Marino     "#\n",
418*86d7f5d3SJohn Marino     "# The -k option specifies keyword expansion (e.g. -kb for binary).\n",
419*86d7f5d3SJohn Marino     "#\n",
420*86d7f5d3SJohn Marino     "# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)\n",
421*86d7f5d3SJohn Marino     "#\n",
422*86d7f5d3SJohn Marino     "#  wildcard	[option value][option value]...\n",
423*86d7f5d3SJohn Marino     "#\n",
424*86d7f5d3SJohn Marino     "#  where option is one of\n",
425*86d7f5d3SJohn Marino     "#  -f		from cvs filter		value: path to filter\n",
426*86d7f5d3SJohn Marino     "#  -t		to cvs filter		value: path to filter\n",
427*86d7f5d3SJohn Marino     "#  -m		update methodology	value: MERGE or COPY\n",
428*86d7f5d3SJohn Marino     "#  -k		expansion mode		value: b, o, kkv, &c\n",
429*86d7f5d3SJohn Marino     "#\n",
430*86d7f5d3SJohn Marino     "#  and value is a single-quote delimited value.\n",
431*86d7f5d3SJohn Marino     "# For example:\n",
432*86d7f5d3SJohn Marino     "#*.gif -k 'b'\n",
433*86d7f5d3SJohn Marino     NULL
434*86d7f5d3SJohn Marino };
435*86d7f5d3SJohn Marino 
436*86d7f5d3SJohn Marino static const char *const notify_contents[] = {
437*86d7f5d3SJohn Marino     "# The \"notify\" file controls where notifications from watches set by\n",
438*86d7f5d3SJohn Marino     "# \"cvs watch add\" or \"cvs edit\" are sent.  The first entry on a line is\n",
439*86d7f5d3SJohn Marino     "# a regular expression which is tested against the directory that the\n",
440*86d7f5d3SJohn Marino     "# change is being made to, relative to the $CVSROOT.  If it matches,\n",
441*86d7f5d3SJohn Marino     "# then the remainder of the line is a filter program that should contain\n",
442*86d7f5d3SJohn Marino     "# one occurrence of %s for the user to notify, and information on its\n",
443*86d7f5d3SJohn Marino     "# standard input.\n",
444*86d7f5d3SJohn Marino     "#\n",
445*86d7f5d3SJohn Marino     "# \"ALL\" or \"DEFAULT\" can be used in place of the regular expression.\n",
446*86d7f5d3SJohn Marino     "#\n",
447*86d7f5d3SJohn Marino     "# format strings are replaceed as follows:\n",
448*86d7f5d3SJohn Marino     "#    %c = canonical name of the command being executed\n",
449*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
450*86d7f5d3SJohn Marino     "#    %R = the name of the referrer, if any, otherwise the value NONE\n",
451*86d7f5d3SJohn Marino #endif
452*86d7f5d3SJohn Marino     "#    %p = path relative to repository\n",
453*86d7f5d3SJohn Marino     "#    %r = repository (path portion of $CVSROOT)\n",
454*86d7f5d3SJohn Marino     "#    %s = user to notify\n",
455*86d7f5d3SJohn Marino     "#\n",
456*86d7f5d3SJohn Marino     "# For example:\n",
457*86d7f5d3SJohn Marino     "#ALL (echo Committed to %r/%p; cat) |mail %s -s \"CVS notification\"\n",
458*86d7f5d3SJohn Marino     NULL
459*86d7f5d3SJohn Marino };
460*86d7f5d3SJohn Marino 
461*86d7f5d3SJohn Marino static const char *const modules_contents[] = {
462*86d7f5d3SJohn Marino     "# Three different line formats are valid:\n",
463*86d7f5d3SJohn Marino     "#	key	-a    aliases...\n",
464*86d7f5d3SJohn Marino     "#	key [options] directory\n",
465*86d7f5d3SJohn Marino     "#	key [options] directory files...\n",
466*86d7f5d3SJohn Marino     "#\n",
467*86d7f5d3SJohn Marino     "# Where \"options\" are composed of:\n",
468*86d7f5d3SJohn Marino     "#	-i prog		Run \"prog\" on \"cvs commit\" from top-level of module.\n",
469*86d7f5d3SJohn Marino     "#	-o prog		Run \"prog\" on \"cvs checkout\" of module.\n",
470*86d7f5d3SJohn Marino     "#	-e prog		Run \"prog\" on \"cvs export\" of module.\n",
471*86d7f5d3SJohn Marino     "#	-t prog		Run \"prog\" on \"cvs rtag\" of module.\n",
472*86d7f5d3SJohn Marino     "#	-u prog		Run \"prog\" on \"cvs update\" of module.\n",
473*86d7f5d3SJohn Marino     "#	-d dir		Place module in directory \"dir\" instead of module name.\n",
474*86d7f5d3SJohn Marino     "#	-l		Top-level directory only -- do not recurse.\n",
475*86d7f5d3SJohn Marino     "#\n",
476*86d7f5d3SJohn Marino     "# NOTE:  If you change any of the \"Run\" options above, you'll have to\n",
477*86d7f5d3SJohn Marino     "# release and re-checkout any working directories of these modules.\n",
478*86d7f5d3SJohn Marino     "#\n",
479*86d7f5d3SJohn Marino     "# And \"directory\" is a path to a directory relative to $CVSROOT.\n",
480*86d7f5d3SJohn Marino     "#\n",
481*86d7f5d3SJohn Marino     "# The \"-a\" option specifies an alias.  An alias is interpreted as if\n",
482*86d7f5d3SJohn Marino     "# everything on the right of the \"-a\" had been typed on the command line.\n",
483*86d7f5d3SJohn Marino     "#\n",
484*86d7f5d3SJohn Marino     "# You can encode a module within a module by using the special '&'\n",
485*86d7f5d3SJohn Marino     "# character to interpose another module into the current module.  This\n",
486*86d7f5d3SJohn Marino     "# can be useful for creating a module that consists of many directories\n",
487*86d7f5d3SJohn Marino     "# spread out over the entire source repository.\n",
488*86d7f5d3SJohn Marino     NULL
489*86d7f5d3SJohn Marino };
490*86d7f5d3SJohn Marino 
491*86d7f5d3SJohn Marino static const char *const config_contents[] = {
492*86d7f5d3SJohn Marino     "# Set `SystemAuth' to `no' if pserver shouldn't check system users/passwords.\n",
493*86d7f5d3SJohn Marino     "#SystemAuth=no\n",
494*86d7f5d3SJohn Marino     "\n",
495*86d7f5d3SJohn Marino     "# Set `LocalKeyword' to specify a local alias for a standard keyword.\n",
496*86d7f5d3SJohn Marino     "#LocalKeyword=MYCVS=CVSHeader\n",
497*86d7f5d3SJohn Marino     "\n",
498*86d7f5d3SJohn Marino     "# Set `KeywordExpand' to `i' followed by a list of keywords to expand or\n",
499*86d7f5d3SJohn Marino     "# `e' followed by a list of keywords to not expand.\n"
500*86d7f5d3SJohn Marino     "#KeywordExpand=iMYCVS,Name,Date\n",
501*86d7f5d3SJohn Marino     "#KeywordExpand=eCVSHeader\n",
502*86d7f5d3SJohn Marino     "\n",
503*86d7f5d3SJohn Marino #ifdef PRESERVE_PERMISSIONS_SUPPORT
504*86d7f5d3SJohn Marino     "# Set `PreservePermissions' to `yes' to save file status information\n",
505*86d7f5d3SJohn Marino     "# in the repository.\n",
506*86d7f5d3SJohn Marino     "#PreservePermissions=no\n",
507*86d7f5d3SJohn Marino     "\n",
508*86d7f5d3SJohn Marino #endif
509*86d7f5d3SJohn Marino     "# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top\n",
510*86d7f5d3SJohn Marino     "# level of the new working directory when using the `cvs checkout'\n",
511*86d7f5d3SJohn Marino     "# command.\n",
512*86d7f5d3SJohn Marino     "#TopLevelAdmin=no\n",
513*86d7f5d3SJohn Marino     "\n",
514*86d7f5d3SJohn Marino     "# Put CVS lock files in this directory rather than directly in the repository.\n",
515*86d7f5d3SJohn Marino     "#LockDir=/var/lock/cvs\n",
516*86d7f5d3SJohn Marino     "\n",
517*86d7f5d3SJohn Marino     "# Set `LogHistory' to `all' or `" ALL_HISTORY_REC_TYPES "' to log all transactions to the\n",
518*86d7f5d3SJohn Marino     "# history file, or a subset as needed (ie `TMAR' logs all write operations)\n",
519*86d7f5d3SJohn Marino     "#LogHistory=" ALL_HISTORY_REC_TYPES "\n",
520*86d7f5d3SJohn Marino     "\n",
521*86d7f5d3SJohn Marino     "# Set `RereadLogAfterVerify' to `always' (the default) to allow the verifymsg\n",
522*86d7f5d3SJohn Marino     "# script to change the log message.  Set it to `stat' to force CVS to verify\n",
523*86d7f5d3SJohn Marino     "# that the file has changed before reading it (this can take up to an extra\n",
524*86d7f5d3SJohn Marino     "# second per directory being committed, so it is not recommended for large\n",
525*86d7f5d3SJohn Marino     "# repositories.  Set it to `never' (the previous CVS behavior) to prevent\n",
526*86d7f5d3SJohn Marino     "# verifymsg scripts from changing the log message.\n",
527*86d7f5d3SJohn Marino     "#RereadLogAfterVerify=always\n",
528*86d7f5d3SJohn Marino     "\n",
529*86d7f5d3SJohn Marino     "# Set `UserAdminOptions' to the list of `cvs admin' commands (options)\n",
530*86d7f5d3SJohn Marino     "# that users not in the `cvsadmin' group are allowed to run.  This\n",
531*86d7f5d3SJohn Marino     "# defaults to `k', or only allowing the changing of the default\n",
532*86d7f5d3SJohn Marino     "# keyword expansion mode for files for users not in the `cvsadmin' group.\n",
533*86d7f5d3SJohn Marino     "# This value is ignored if the `cvsadmin' group does not exist.\n",
534*86d7f5d3SJohn Marino     "#\n",
535*86d7f5d3SJohn Marino     "# The following string would enable all `cvs admin' commands for all\n",
536*86d7f5d3SJohn Marino     "# users:\n",
537*86d7f5d3SJohn Marino     "#UserAdminOptions=aAbceIklLmnNostuU\n",
538*86d7f5d3SJohn Marino #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
539*86d7f5d3SJohn Marino     "\n",
540*86d7f5d3SJohn Marino     "# Set `UseNewInfoFmtStrings' to `no' if you must support a legacy system by\n",
541*86d7f5d3SJohn Marino     "# enabling the deprecated old style info file command line format strings.\n",
542*86d7f5d3SJohn Marino     "# Be warned that these strings could be disabled in any new version of CVS.\n",
543*86d7f5d3SJohn Marino     "UseNewInfoFmtStrings=yes\n",
544*86d7f5d3SJohn Marino #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
545*86d7f5d3SJohn Marino     "\n",
546*86d7f5d3SJohn Marino     "# Set `ImportNewFilesToVendorBranchOnly' to `yes' if you wish to force\n",
547*86d7f5d3SJohn Marino     "# every `cvs import' command to behave as if the `-X' flag was\n",
548*86d7f5d3SJohn Marino     "# specified.\n",
549*86d7f5d3SJohn Marino     "#ImportNewFilesToVendorBranchOnly=no\n",
550*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
551*86d7f5d3SJohn Marino     "\n",
552*86d7f5d3SJohn Marino     "# Set `PrimaryServer' to the CVSROOT to the primary, or write, server when\n",
553*86d7f5d3SJohn Marino     "# establishing one or more read-only mirrors which serve as proxies for\n",
554*86d7f5d3SJohn Marino     "# the write server in write mode or redirect the client to the primary for\n",
555*86d7f5d3SJohn Marino     "# write requests.\n",
556*86d7f5d3SJohn Marino     "#\n",
557*86d7f5d3SJohn Marino     "# For example:\n",
558*86d7f5d3SJohn Marino     "#\n",
559*86d7f5d3SJohn Marino     "#   PrimaryServer=:fork:localhost/cvsroot\n",
560*86d7f5d3SJohn Marino     "\n",
561*86d7f5d3SJohn Marino     "# Set `MaxProxyBufferSize' to the the maximum allowable secondary\n",
562*86d7f5d3SJohn Marino     "# buffer memory cache size before the buffer begins being stored to disk, in\n",
563*86d7f5d3SJohn Marino     "# bytes.  Must be a positive integer but may end in `k', `M', `G', or `T' (for\n",
564*86d7f5d3SJohn Marino     "# kiilo, mega, giga, & tera, respectively).  If an otherwise valid number you\n",
565*86d7f5d3SJohn Marino     "# specify is greater than the SIZE_MAX defined by your system's C compiler,\n",
566*86d7f5d3SJohn Marino     "# then it will be resolved to SIZE_MAX without a warning.  Defaults to 8M (8\n",
567*86d7f5d3SJohn Marino     "# megabytes).\n",
568*86d7f5d3SJohn Marino     "#\n",
569*86d7f5d3SJohn Marino     "# High values for MaxProxyBufferSize may speed up a secondary server\n",
570*86d7f5d3SJohn Marino     "# with old hardware and a lot of available memory but can actually slow a\n",
571*86d7f5d3SJohn Marino     "# modern system down slightly.\n",
572*86d7f5d3SJohn Marino     "#\n",
573*86d7f5d3SJohn Marino     "# For example:\n",
574*86d7f5d3SJohn Marino     "#\n",
575*86d7f5d3SJohn Marino     "#   MaxProxyBufferSize=1G\n",
576*86d7f5d3SJohn Marino #endif /* PROXY_SUPPORT */
577*86d7f5d3SJohn Marino     "\n",
578*86d7f5d3SJohn Marino     "# Set `MaxCommentLeaderLength' to the maximum length permitted for the\n",
579*86d7f5d3SJohn Marino     "# automagically determined comment leader used when expanding the Log\n",
580*86d7f5d3SJohn Marino     "# keyword, in bytes.  CVS's behavior when the automagically determined\n",
581*86d7f5d3SJohn Marino     "# comment leader exceeds this length is dependant on the value of\n",
582*86d7f5d3SJohn Marino     "# `UseArchiveCommentLeader' set in this file.  `unlimited' is a valid\n",
583*86d7f5d3SJohn Marino     "# setting for this value.  Defaults to 20 bytes.\n",
584*86d7f5d3SJohn Marino     "#\n",
585*86d7f5d3SJohn Marino     "# For example:\n",
586*86d7f5d3SJohn Marino     "#\n",
587*86d7f5d3SJohn Marino     "#   MaxCommentLeaderLength=20\n",
588*86d7f5d3SJohn Marino     "\n",
589*86d7f5d3SJohn Marino     "# Set `UseArchiveCommentLeader' to `yes' to cause CVS to fall back on\n",
590*86d7f5d3SJohn Marino     "# the comment leader set in the RCS archive file, if any, when the\n",
591*86d7f5d3SJohn Marino     "# automagically determined comment leader exceeds `MaxCommentLeaderLength'\n",
592*86d7f5d3SJohn Marino     "# bytes.  If `UseArchiveCommentLeader' is not set and a comment leader\n",
593*86d7f5d3SJohn Marino     "# greater than `MaxCommentLeaderLength' is calculated, the Log keyword\n",
594*86d7f5d3SJohn Marino     "# being examined will not be expanded.  Defaults to `no'.\n",
595*86d7f5d3SJohn Marino     "#\n",
596*86d7f5d3SJohn Marino     "# For example:\n",
597*86d7f5d3SJohn Marino     "#\n",
598*86d7f5d3SJohn Marino     "#   UseArchiveCommentLeader=no\n",
599*86d7f5d3SJohn Marino     NULL
600*86d7f5d3SJohn Marino };
601*86d7f5d3SJohn Marino 
602*86d7f5d3SJohn Marino static const struct admin_file filelist[] = {
603*86d7f5d3SJohn Marino     {CVSROOTADM_CHECKOUTLIST,
604*86d7f5d3SJohn Marino 	"a %s file can specify extra CVSROOT files to auto-checkout",
605*86d7f5d3SJohn Marino 	checkoutlist_contents},
606*86d7f5d3SJohn Marino     {CVSROOTADM_COMMITINFO,
607*86d7f5d3SJohn Marino 	"a %s file can be used to configure 'cvs commit' checking",
608*86d7f5d3SJohn Marino 	commitinfo_contents},
609*86d7f5d3SJohn Marino     {CVSROOTADM_IGNORE,
610*86d7f5d3SJohn Marino 	"a %s file can be used to specify files to ignore",
611*86d7f5d3SJohn Marino 	NULL},
612*86d7f5d3SJohn Marino     {CVSROOTADM_LOGINFO,
613*86d7f5d3SJohn Marino 	"no logging of 'cvs commit' messages is done without a %s file",
614*86d7f5d3SJohn Marino 	&loginfo_contents[0]},
615*86d7f5d3SJohn Marino     {CVSROOTADM_MODULES,
616*86d7f5d3SJohn Marino 	/* modules is special-cased in mkmodules.  */
617*86d7f5d3SJohn Marino 	NULL,
618*86d7f5d3SJohn Marino 	modules_contents},
619*86d7f5d3SJohn Marino     {CVSROOTADM_NOTIFY,
620*86d7f5d3SJohn Marino 	"a %s file can be used to specify where notifications go",
621*86d7f5d3SJohn Marino 	notify_contents},
622*86d7f5d3SJohn Marino     {CVSROOTADM_POSTADMIN,
623*86d7f5d3SJohn Marino 	"a %s file can be used to configure 'cvs admin' logging",
624*86d7f5d3SJohn Marino 	postadmin_contents},
625*86d7f5d3SJohn Marino     {CVSROOTADM_POSTPROXY,
626*86d7f5d3SJohn Marino 	"a %s file can be used to close or log connections to a primary server",
627*86d7f5d3SJohn Marino 	postproxy_contents},
628*86d7f5d3SJohn Marino     {CVSROOTADM_POSTTAG,
629*86d7f5d3SJohn Marino 	"a %s file can be used to configure 'cvs tag' logging",
630*86d7f5d3SJohn Marino 	posttag_contents},
631*86d7f5d3SJohn Marino     {CVSROOTADM_POSTWATCH,
632*86d7f5d3SJohn Marino 	"a %s file can be used to configure 'cvs watch' logging",
633*86d7f5d3SJohn Marino 	postwatch_contents},
634*86d7f5d3SJohn Marino     {CVSROOTADM_PREPROXY,
635*86d7f5d3SJohn Marino 	"a %s file can be used to open or log connections to a primary server",
636*86d7f5d3SJohn Marino 	preproxy_contents},
637*86d7f5d3SJohn Marino     {CVSROOTADM_RCSINFO,
638*86d7f5d3SJohn Marino 	"a %s file can be used to configure 'cvs commit' templates",
639*86d7f5d3SJohn Marino 	rcsinfo_contents},
640*86d7f5d3SJohn Marino     {CVSROOTADM_READERS,
641*86d7f5d3SJohn Marino 	"a %s file specifies read-only users",
642*86d7f5d3SJohn Marino 	NULL},
643*86d7f5d3SJohn Marino     {CVSROOTADM_TAGINFO,
644*86d7f5d3SJohn Marino 	"a %s file can be used to configure 'cvs tag' checking",
645*86d7f5d3SJohn Marino 	taginfo_contents},
646*86d7f5d3SJohn Marino     {CVSROOTADM_VERIFYMSG,
647*86d7f5d3SJohn Marino 	"a %s file can be used to validate log messages",
648*86d7f5d3SJohn Marino 	verifymsg_contents},
649*86d7f5d3SJohn Marino     {CVSROOTADM_WRAPPER,
650*86d7f5d3SJohn Marino 	"a %s file can be used to specify files to treat as wrappers",
651*86d7f5d3SJohn Marino 	cvswrappers_contents},
652*86d7f5d3SJohn Marino     {CVSROOTADM_WRITERS,
653*86d7f5d3SJohn Marino 	"a %s file specifies read/write users",
654*86d7f5d3SJohn Marino 	NULL},
655*86d7f5d3SJohn Marino 
656*86d7f5d3SJohn Marino     /* Some have suggested listing CVSROOTADM_PASSWD here too.  This
657*86d7f5d3SJohn Marino        would mean that CVS commands which operate on the
658*86d7f5d3SJohn Marino        CVSROOTADM_PASSWD file would transmit hashed passwords over the
659*86d7f5d3SJohn Marino        net.  This might seem to be no big deal, as pserver normally
660*86d7f5d3SJohn Marino        transmits cleartext passwords, but the difference is that
661*86d7f5d3SJohn Marino        CVSROOTADM_PASSWD contains *all* passwords, not just the ones
662*86d7f5d3SJohn Marino        currently being used.  For example, it could be too easy to
663*86d7f5d3SJohn Marino        accidentally give someone readonly access to CVSROOTADM_PASSWD
664*86d7f5d3SJohn Marino        (e.g. via anonymous CVS or cvsweb), and then if there are any
665*86d7f5d3SJohn Marino        guessable passwords for read/write access (usually there will be)
666*86d7f5d3SJohn Marino        they get read/write access.
667*86d7f5d3SJohn Marino 
668*86d7f5d3SJohn Marino        Another worry is the implications of storing old passwords--if
669*86d7f5d3SJohn Marino        someone used a password in the past they might be using it
670*86d7f5d3SJohn Marino        elsewhere, using a similar password, etc, and so saving old
671*86d7f5d3SJohn Marino        passwords, even hashed, is probably not a good idea.  */
672*86d7f5d3SJohn Marino 
673*86d7f5d3SJohn Marino     {CVSROOTADM_CONFIG,
674*86d7f5d3SJohn Marino 	 "a %s file configures various behaviors",
675*86d7f5d3SJohn Marino 	 config_contents},
676*86d7f5d3SJohn Marino     {NULL, NULL, NULL}
677*86d7f5d3SJohn Marino };
678*86d7f5d3SJohn Marino 
679*86d7f5d3SJohn Marino /* Rebuild the checked out administrative files in directory DIR.  */
680*86d7f5d3SJohn Marino int
mkmodules(char * dir)681*86d7f5d3SJohn Marino mkmodules (char *dir)
682*86d7f5d3SJohn Marino {
683*86d7f5d3SJohn Marino     struct saved_cwd cwd;
684*86d7f5d3SJohn Marino     char *temp;
685*86d7f5d3SJohn Marino     char *cp, *last, *fname;
686*86d7f5d3SJohn Marino #ifdef MY_NDBM
687*86d7f5d3SJohn Marino     DBM *db;
688*86d7f5d3SJohn Marino #endif
689*86d7f5d3SJohn Marino     FILE *fp;
690*86d7f5d3SJohn Marino     char *line = NULL;
691*86d7f5d3SJohn Marino     size_t line_allocated = 0;
692*86d7f5d3SJohn Marino     const struct admin_file *fileptr;
693*86d7f5d3SJohn Marino 
694*86d7f5d3SJohn Marino     if (noexec)
695*86d7f5d3SJohn Marino 	return 0;
696*86d7f5d3SJohn Marino 
697*86d7f5d3SJohn Marino     if (save_cwd (&cwd))
698*86d7f5d3SJohn Marino 	error (1, errno, "Failed to save current directory.");
699*86d7f5d3SJohn Marino 
700*86d7f5d3SJohn Marino     if (CVS_CHDIR (dir) < 0)
701*86d7f5d3SJohn Marino 	error (1, errno, "cannot chdir to %s", dir);
702*86d7f5d3SJohn Marino 
703*86d7f5d3SJohn Marino     /*
704*86d7f5d3SJohn Marino      * First, do the work necessary to update the "modules" database.
705*86d7f5d3SJohn Marino      */
706*86d7f5d3SJohn Marino     temp = make_tempfile ();
707*86d7f5d3SJohn Marino     switch (checkout_file (CVSROOTADM_MODULES, temp))
708*86d7f5d3SJohn Marino     {
709*86d7f5d3SJohn Marino 
710*86d7f5d3SJohn Marino 	case 0:			/* everything ok */
711*86d7f5d3SJohn Marino #ifdef MY_NDBM
712*86d7f5d3SJohn Marino 	    /* open it, to generate any duplicate errors */
713*86d7f5d3SJohn Marino 	    if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL)
714*86d7f5d3SJohn Marino 		dbm_close (db);
715*86d7f5d3SJohn Marino #else
716*86d7f5d3SJohn Marino 	    write_dbmfile (temp);
717*86d7f5d3SJohn Marino 	    rename_dbmfile (temp);
718*86d7f5d3SJohn Marino #endif
719*86d7f5d3SJohn Marino 	    rename_rcsfile (temp, CVSROOTADM_MODULES);
720*86d7f5d3SJohn Marino 	    break;
721*86d7f5d3SJohn Marino 
722*86d7f5d3SJohn Marino 	default:
723*86d7f5d3SJohn Marino 	    error (0, 0,
724*86d7f5d3SJohn Marino 		"'cvs checkout' is less functional without a %s file",
725*86d7f5d3SJohn Marino 		CVSROOTADM_MODULES);
726*86d7f5d3SJohn Marino 	    break;
727*86d7f5d3SJohn Marino     }					/* switch on checkout_file() */
728*86d7f5d3SJohn Marino 
729*86d7f5d3SJohn Marino     if (unlink_file (temp) < 0
730*86d7f5d3SJohn Marino 	&& !existence_error (errno))
731*86d7f5d3SJohn Marino 	error (0, errno, "cannot remove %s", temp);
732*86d7f5d3SJohn Marino     free (temp);
733*86d7f5d3SJohn Marino 
734*86d7f5d3SJohn Marino     /* Checkout the files that need it in CVSROOT dir */
735*86d7f5d3SJohn Marino     for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) {
736*86d7f5d3SJohn Marino 	if (fileptr->errormsg == NULL)
737*86d7f5d3SJohn Marino 	    continue;
738*86d7f5d3SJohn Marino 	temp = make_tempfile ();
739*86d7f5d3SJohn Marino 	if (checkout_file (fileptr->filename, temp) == 0)
740*86d7f5d3SJohn Marino 	    rename_rcsfile (temp, fileptr->filename);
741*86d7f5d3SJohn Marino 	/* else
742*86d7f5d3SJohn Marino 	 *   If there was some problem other than the file not existing,
743*86d7f5d3SJohn Marino 	 *   checkout_file already printed a real error message.  If the
744*86d7f5d3SJohn Marino 	 *   file does not exist, it is harmless--it probably just means
745*86d7f5d3SJohn Marino 	 *   that the repository was created with an old version of CVS
746*86d7f5d3SJohn Marino 	 *   which didn't have so many files in CVSROOT.
747*86d7f5d3SJohn Marino 	 */
748*86d7f5d3SJohn Marino 
749*86d7f5d3SJohn Marino 	if (unlink_file (temp) < 0
750*86d7f5d3SJohn Marino 	    && !existence_error (errno))
751*86d7f5d3SJohn Marino 	    error (0, errno, "cannot remove %s", temp);
752*86d7f5d3SJohn Marino 	free (temp);
753*86d7f5d3SJohn Marino     }
754*86d7f5d3SJohn Marino 
755*86d7f5d3SJohn Marino     fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r");
756*86d7f5d3SJohn Marino     if (fp)
757*86d7f5d3SJohn Marino     {
758*86d7f5d3SJohn Marino 	/*
759*86d7f5d3SJohn Marino 	 * File format:
760*86d7f5d3SJohn Marino 	 *  [<whitespace>]<filename>[<whitespace><error message>]<end-of-line>
761*86d7f5d3SJohn Marino 	 *
762*86d7f5d3SJohn Marino 	 * comment lines begin with '#'
763*86d7f5d3SJohn Marino 	 */
764*86d7f5d3SJohn Marino 	while (getline (&line, &line_allocated, fp) >= 0)
765*86d7f5d3SJohn Marino 	{
766*86d7f5d3SJohn Marino 	    /* skip lines starting with # */
767*86d7f5d3SJohn Marino 	    if (line[0] == '#')
768*86d7f5d3SJohn Marino 		continue;
769*86d7f5d3SJohn Marino 
770*86d7f5d3SJohn Marino 	    if ((last = strrchr (line, '\n')) != NULL)
771*86d7f5d3SJohn Marino 		*last = '\0';			/* strip the newline */
772*86d7f5d3SJohn Marino 
773*86d7f5d3SJohn Marino 	    /* Skip leading white space. */
774*86d7f5d3SJohn Marino 	    for (fname = line;
775*86d7f5d3SJohn Marino 		 *fname && isspace ((unsigned char) *fname);
776*86d7f5d3SJohn Marino 		 fname++)
777*86d7f5d3SJohn Marino 		;
778*86d7f5d3SJohn Marino 
779*86d7f5d3SJohn Marino 	    /* Find end of filename. */
780*86d7f5d3SJohn Marino 	    for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++)
781*86d7f5d3SJohn Marino 		;
782*86d7f5d3SJohn Marino 	    *cp = '\0';
783*86d7f5d3SJohn Marino 
784*86d7f5d3SJohn Marino 	    temp = make_tempfile ();
785*86d7f5d3SJohn Marino 	    if (checkout_file (fname, temp) == 0)
786*86d7f5d3SJohn Marino 	    {
787*86d7f5d3SJohn Marino 		rename_rcsfile (temp, fname);
788*86d7f5d3SJohn Marino 	    }
789*86d7f5d3SJohn Marino 	    else
790*86d7f5d3SJohn Marino 	    {
791*86d7f5d3SJohn Marino 		/* Skip leading white space before the error message.  */
792*86d7f5d3SJohn Marino 		for (cp++;
793*86d7f5d3SJohn Marino 		     cp < last && *cp && isspace ((unsigned char) *cp);
794*86d7f5d3SJohn Marino 		     cp++)
795*86d7f5d3SJohn Marino 		    ;
796*86d7f5d3SJohn Marino 		if (cp < last && *cp)
797*86d7f5d3SJohn Marino 		    error (0, 0, "%s", cp);
798*86d7f5d3SJohn Marino 	    }
799*86d7f5d3SJohn Marino 	    if (unlink_file (temp) < 0
800*86d7f5d3SJohn Marino 		&& !existence_error (errno))
801*86d7f5d3SJohn Marino 		error (0, errno, "cannot remove %s", temp);
802*86d7f5d3SJohn Marino 	    free (temp);
803*86d7f5d3SJohn Marino 	}
804*86d7f5d3SJohn Marino 	if (line)
805*86d7f5d3SJohn Marino 	    free (line);
806*86d7f5d3SJohn Marino 	if (ferror (fp))
807*86d7f5d3SJohn Marino 	    error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST);
808*86d7f5d3SJohn Marino 	if (fclose (fp) < 0)
809*86d7f5d3SJohn Marino 	    error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST);
810*86d7f5d3SJohn Marino     }
811*86d7f5d3SJohn Marino     else
812*86d7f5d3SJohn Marino     {
813*86d7f5d3SJohn Marino 	/* Error from CVS_FOPEN.  */
814*86d7f5d3SJohn Marino 	if (!existence_error (errno))
815*86d7f5d3SJohn Marino 	    error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST);
816*86d7f5d3SJohn Marino     }
817*86d7f5d3SJohn Marino 
818*86d7f5d3SJohn Marino     if (restore_cwd (&cwd))
819*86d7f5d3SJohn Marino 	error (1, errno, "Failed to restore current directory, `%s'.",
820*86d7f5d3SJohn Marino 	       cwd.name);
821*86d7f5d3SJohn Marino     free_cwd (&cwd);
822*86d7f5d3SJohn Marino 
823*86d7f5d3SJohn Marino     return 0;
824*86d7f5d3SJohn Marino }
825*86d7f5d3SJohn Marino 
826*86d7f5d3SJohn Marino 
827*86d7f5d3SJohn Marino 
828*86d7f5d3SJohn Marino /*
829*86d7f5d3SJohn Marino  * Yeah, I know, there are NFS race conditions here.
830*86d7f5d3SJohn Marino  */
831*86d7f5d3SJohn Marino static char *
make_tempfile(void)832*86d7f5d3SJohn Marino make_tempfile (void)
833*86d7f5d3SJohn Marino {
834*86d7f5d3SJohn Marino     static int seed = 0;
835*86d7f5d3SJohn Marino     int fd;
836*86d7f5d3SJohn Marino     char *temp;
837*86d7f5d3SJohn Marino 
838*86d7f5d3SJohn Marino     if (seed == 0)
839*86d7f5d3SJohn Marino 	seed = getpid ();
840*86d7f5d3SJohn Marino     temp = xmalloc (sizeof (BAKPREFIX) + 40);
841*86d7f5d3SJohn Marino     while (1)
842*86d7f5d3SJohn Marino     {
843*86d7f5d3SJohn Marino 	(void) sprintf (temp, "%s%d", BAKPREFIX, seed++);
844*86d7f5d3SJohn Marino 	if ((fd = CVS_OPEN (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1)
845*86d7f5d3SJohn Marino 	    break;
846*86d7f5d3SJohn Marino 	if (errno != EEXIST)
847*86d7f5d3SJohn Marino 	    error (1, errno, "cannot create temporary file %s", temp);
848*86d7f5d3SJohn Marino     }
849*86d7f5d3SJohn Marino     if (close(fd) < 0)
850*86d7f5d3SJohn Marino 	error(1, errno, "cannot close temporary file %s", temp);
851*86d7f5d3SJohn Marino     return temp;
852*86d7f5d3SJohn Marino }
853*86d7f5d3SJohn Marino 
854*86d7f5d3SJohn Marino 
855*86d7f5d3SJohn Marino 
856*86d7f5d3SJohn Marino /* Get a file.  If the file does not exist, return 1 silently.  If
857*86d7f5d3SJohn Marino    there is an error, print a message and return 1 (FIXME: probably
858*86d7f5d3SJohn Marino    not a very clean convention).  On success, return 0.  */
859*86d7f5d3SJohn Marino static int
checkout_file(char * file,char * temp)860*86d7f5d3SJohn Marino checkout_file (char *file, char *temp)
861*86d7f5d3SJohn Marino {
862*86d7f5d3SJohn Marino     char *rcs;
863*86d7f5d3SJohn Marino     RCSNode *rcsnode;
864*86d7f5d3SJohn Marino     int retcode = 0;
865*86d7f5d3SJohn Marino 
866*86d7f5d3SJohn Marino     if (noexec)
867*86d7f5d3SJohn Marino 	return 0;
868*86d7f5d3SJohn Marino 
869*86d7f5d3SJohn Marino     rcs = Xasprintf ("%s%s", file, RCSEXT);
870*86d7f5d3SJohn Marino     if (!isfile (rcs))
871*86d7f5d3SJohn Marino     {
872*86d7f5d3SJohn Marino 	free (rcs);
873*86d7f5d3SJohn Marino 	return 1;
874*86d7f5d3SJohn Marino     }
875*86d7f5d3SJohn Marino 
876*86d7f5d3SJohn Marino     rcsnode = RCS_parsercsfile (rcs);
877*86d7f5d3SJohn Marino     if (!rcsnode)
878*86d7f5d3SJohn Marino     {
879*86d7f5d3SJohn Marino 	/* Probably not necessary (?); RCS_parsercsfile already printed a
880*86d7f5d3SJohn Marino 	   message.  */
881*86d7f5d3SJohn Marino 	error (0, 0, "Failed to parse `%s'.", rcs);
882*86d7f5d3SJohn Marino 	free (rcs);
883*86d7f5d3SJohn Marino 	return 1;
884*86d7f5d3SJohn Marino     }
885*86d7f5d3SJohn Marino 
886*86d7f5d3SJohn Marino     retcode = RCS_checkout (rcsnode, NULL, NULL, NULL, NULL, temp, NULL, NULL);
887*86d7f5d3SJohn Marino     if (retcode != 0)
888*86d7f5d3SJohn Marino     {
889*86d7f5d3SJohn Marino 	/* Probably not necessary (?); RCS_checkout already printed a
890*86d7f5d3SJohn Marino 	   message.  */
891*86d7f5d3SJohn Marino 	error (0, 0, "failed to check out %s file",
892*86d7f5d3SJohn Marino 	       file);
893*86d7f5d3SJohn Marino     }
894*86d7f5d3SJohn Marino     freercsnode (&rcsnode);
895*86d7f5d3SJohn Marino     free (rcs);
896*86d7f5d3SJohn Marino     return retcode;
897*86d7f5d3SJohn Marino }
898*86d7f5d3SJohn Marino 
899*86d7f5d3SJohn Marino 
900*86d7f5d3SJohn Marino 
901*86d7f5d3SJohn Marino #ifndef MY_NDBM
902*86d7f5d3SJohn Marino 
903*86d7f5d3SJohn Marino static void
write_dbmfile(char * temp)904*86d7f5d3SJohn Marino write_dbmfile( char *temp )
905*86d7f5d3SJohn Marino {
906*86d7f5d3SJohn Marino     char line[DBLKSIZ], value[DBLKSIZ];
907*86d7f5d3SJohn Marino     FILE *fp;
908*86d7f5d3SJohn Marino     DBM *db;
909*86d7f5d3SJohn Marino     char *cp, *vp;
910*86d7f5d3SJohn Marino     datum key, val;
911*86d7f5d3SJohn Marino     int len, cont, err = 0;
912*86d7f5d3SJohn Marino 
913*86d7f5d3SJohn Marino     fp = xfopen (temp, "r");
914*86d7f5d3SJohn Marino     if ((db = dbm_open (temp, O_RDWR | O_CREAT | O_TRUNC, 0666)) == NULL)
915*86d7f5d3SJohn Marino 	error (1, errno, "cannot open dbm file %s for creation", temp);
916*86d7f5d3SJohn Marino     for (cont = 0; fgets (line, sizeof (line), fp) != NULL;)
917*86d7f5d3SJohn Marino     {
918*86d7f5d3SJohn Marino 	if ((cp = strrchr (line, '\n')) != NULL)
919*86d7f5d3SJohn Marino 	    *cp = '\0';			/* strip the newline */
920*86d7f5d3SJohn Marino 
921*86d7f5d3SJohn Marino 	/*
922*86d7f5d3SJohn Marino 	 * Add the line to the value, at the end if this is a continuation
923*86d7f5d3SJohn Marino 	 * line; otherwise at the beginning, but only after any trailing
924*86d7f5d3SJohn Marino 	 * backslash is removed.
925*86d7f5d3SJohn Marino 	 */
926*86d7f5d3SJohn Marino 	vp = value;
927*86d7f5d3SJohn Marino 	if (cont)
928*86d7f5d3SJohn Marino 	    vp += strlen (value);
929*86d7f5d3SJohn Marino 
930*86d7f5d3SJohn Marino 	/*
931*86d7f5d3SJohn Marino 	 * See if the line we read is a continuation line, and strip the
932*86d7f5d3SJohn Marino 	 * backslash if so.
933*86d7f5d3SJohn Marino 	 */
934*86d7f5d3SJohn Marino 	len = strlen (line);
935*86d7f5d3SJohn Marino 	if (len > 0)
936*86d7f5d3SJohn Marino 	    cp = &line[len - 1];
937*86d7f5d3SJohn Marino 	else
938*86d7f5d3SJohn Marino 	    cp = line;
939*86d7f5d3SJohn Marino 	if (*cp == '\\')
940*86d7f5d3SJohn Marino 	{
941*86d7f5d3SJohn Marino 	    cont = 1;
942*86d7f5d3SJohn Marino 	    *cp = '\0';
943*86d7f5d3SJohn Marino 	}
944*86d7f5d3SJohn Marino 	else
945*86d7f5d3SJohn Marino 	{
946*86d7f5d3SJohn Marino 	    cont = 0;
947*86d7f5d3SJohn Marino 	}
948*86d7f5d3SJohn Marino 	(void) strcpy (vp, line);
949*86d7f5d3SJohn Marino 	if (value[0] == '#')
950*86d7f5d3SJohn Marino 	    continue;			/* comment line */
951*86d7f5d3SJohn Marino 	vp = value;
952*86d7f5d3SJohn Marino 	while (*vp && isspace ((unsigned char) *vp))
953*86d7f5d3SJohn Marino 	    vp++;
954*86d7f5d3SJohn Marino 	if (*vp == '\0')
955*86d7f5d3SJohn Marino 	    continue;			/* empty line */
956*86d7f5d3SJohn Marino 
957*86d7f5d3SJohn Marino 	/*
958*86d7f5d3SJohn Marino 	 * If this was not a continuation line, add the entry to the database
959*86d7f5d3SJohn Marino 	 */
960*86d7f5d3SJohn Marino 	if (!cont)
961*86d7f5d3SJohn Marino 	{
962*86d7f5d3SJohn Marino 	    key.dptr = vp;
963*86d7f5d3SJohn Marino 	    while (*vp && !isspace ((unsigned char) *vp))
964*86d7f5d3SJohn Marino 		vp++;
965*86d7f5d3SJohn Marino 	    key.dsize = vp - key.dptr;
966*86d7f5d3SJohn Marino 	    *vp++ = '\0';		/* NULL terminate the key */
967*86d7f5d3SJohn Marino 	    while (*vp && isspace ((unsigned char) *vp))
968*86d7f5d3SJohn Marino 		vp++;			/* skip whitespace to value */
969*86d7f5d3SJohn Marino 	    if (*vp == '\0')
970*86d7f5d3SJohn Marino 	    {
971*86d7f5d3SJohn Marino 		error (0, 0, "warning: NULL value for key `%s'", key.dptr);
972*86d7f5d3SJohn Marino 		continue;
973*86d7f5d3SJohn Marino 	    }
974*86d7f5d3SJohn Marino 	    val.dptr = vp;
975*86d7f5d3SJohn Marino 	    val.dsize = strlen (vp);
976*86d7f5d3SJohn Marino 	    if (dbm_store (db, key, val, DBM_INSERT) == 1)
977*86d7f5d3SJohn Marino 	    {
978*86d7f5d3SJohn Marino 		error (0, 0, "duplicate key found for `%s'", key.dptr);
979*86d7f5d3SJohn Marino 		err++;
980*86d7f5d3SJohn Marino 	    }
981*86d7f5d3SJohn Marino 	}
982*86d7f5d3SJohn Marino     }
983*86d7f5d3SJohn Marino     dbm_close (db);
984*86d7f5d3SJohn Marino     if (fclose (fp) < 0)
985*86d7f5d3SJohn Marino 	error (0, errno, "cannot close %s", temp);
986*86d7f5d3SJohn Marino     if (err)
987*86d7f5d3SJohn Marino     {
988*86d7f5d3SJohn Marino 	/* I think that the size of the buffer needed here is
989*86d7f5d3SJohn Marino 	   just determined by sizeof (CVSROOTADM_MODULES), the
990*86d7f5d3SJohn Marino 	   filenames created by make_tempfile, and other things that won't
991*86d7f5d3SJohn Marino 	   overflow.  */
992*86d7f5d3SJohn Marino 	char dotdir[50], dotpag[50], dotdb[50];
993*86d7f5d3SJohn Marino 
994*86d7f5d3SJohn Marino 	(void) sprintf (dotdir, "%s.dir", temp);
995*86d7f5d3SJohn Marino 	(void) sprintf (dotpag, "%s.pag", temp);
996*86d7f5d3SJohn Marino 	(void) sprintf (dotdb, "%s.db", temp);
997*86d7f5d3SJohn Marino 	if (unlink_file (dotdir) < 0
998*86d7f5d3SJohn Marino 	    && !existence_error (errno))
999*86d7f5d3SJohn Marino 	    error (0, errno, "cannot remove %s", dotdir);
1000*86d7f5d3SJohn Marino 	if (unlink_file (dotpag) < 0
1001*86d7f5d3SJohn Marino 	    && !existence_error (errno))
1002*86d7f5d3SJohn Marino 	    error (0, errno, "cannot remove %s", dotpag);
1003*86d7f5d3SJohn Marino 	if (unlink_file (dotdb) < 0
1004*86d7f5d3SJohn Marino 	    && !existence_error (errno))
1005*86d7f5d3SJohn Marino 	    error (0, errno, "cannot remove %s", dotdb);
1006*86d7f5d3SJohn Marino 	error (1, 0, "DBM creation failed; correct above errors");
1007*86d7f5d3SJohn Marino     }
1008*86d7f5d3SJohn Marino }
1009*86d7f5d3SJohn Marino 
1010*86d7f5d3SJohn Marino static void
rename_dbmfile(char * temp)1011*86d7f5d3SJohn Marino rename_dbmfile( char *temp )
1012*86d7f5d3SJohn Marino {
1013*86d7f5d3SJohn Marino     /* I think that the size of the buffer needed here is
1014*86d7f5d3SJohn Marino        just determined by sizeof (CVSROOTADM_MODULES), the
1015*86d7f5d3SJohn Marino        filenames created by make_tempfile, and other things that won't
1016*86d7f5d3SJohn Marino        overflow.  */
1017*86d7f5d3SJohn Marino     char newdir[50], newpag[50], newdb[50];
1018*86d7f5d3SJohn Marino     char dotdir[50], dotpag[50], dotdb[50];
1019*86d7f5d3SJohn Marino     char bakdir[50], bakpag[50], bakdb[50];
1020*86d7f5d3SJohn Marino 
1021*86d7f5d3SJohn Marino     int dir1_errno = 0, pag1_errno = 0, db1_errno = 0;
1022*86d7f5d3SJohn Marino     int dir2_errno = 0, pag2_errno = 0, db2_errno = 0;
1023*86d7f5d3SJohn Marino     int dir3_errno = 0, pag3_errno = 0, db3_errno = 0;
1024*86d7f5d3SJohn Marino 
1025*86d7f5d3SJohn Marino     (void) sprintf (dotdir, "%s.dir", CVSROOTADM_MODULES);
1026*86d7f5d3SJohn Marino     (void) sprintf (dotpag, "%s.pag", CVSROOTADM_MODULES);
1027*86d7f5d3SJohn Marino     (void) sprintf (dotdb, "%s.db", CVSROOTADM_MODULES);
1028*86d7f5d3SJohn Marino     (void) sprintf (bakdir, "%s%s.dir", BAKPREFIX, CVSROOTADM_MODULES);
1029*86d7f5d3SJohn Marino     (void) sprintf (bakpag, "%s%s.pag", BAKPREFIX, CVSROOTADM_MODULES);
1030*86d7f5d3SJohn Marino     (void) sprintf (bakdb, "%s%s.db", BAKPREFIX, CVSROOTADM_MODULES);
1031*86d7f5d3SJohn Marino     (void) sprintf (newdir, "%s.dir", temp);
1032*86d7f5d3SJohn Marino     (void) sprintf (newpag, "%s.pag", temp);
1033*86d7f5d3SJohn Marino     (void) sprintf (newdb, "%s.db", temp);
1034*86d7f5d3SJohn Marino 
1035*86d7f5d3SJohn Marino     (void) chmod (newdir, 0666);
1036*86d7f5d3SJohn Marino     (void) chmod (newpag, 0666);
1037*86d7f5d3SJohn Marino     (void) chmod (newdb, 0666);
1038*86d7f5d3SJohn Marino 
1039*86d7f5d3SJohn Marino     /* don't mess with me */
1040*86d7f5d3SJohn Marino     SIG_beginCrSect ();
1041*86d7f5d3SJohn Marino 
1042*86d7f5d3SJohn Marino     /* rm .#modules.dir .#modules.pag */
1043*86d7f5d3SJohn Marino     if (unlink_file (bakdir) < 0)
1044*86d7f5d3SJohn Marino 	dir1_errno = errno;
1045*86d7f5d3SJohn Marino     if (unlink_file (bakpag) < 0)
1046*86d7f5d3SJohn Marino 	pag1_errno = errno;
1047*86d7f5d3SJohn Marino     if (unlink_file (bakdb) < 0)
1048*86d7f5d3SJohn Marino 	db1_errno = errno;
1049*86d7f5d3SJohn Marino 
1050*86d7f5d3SJohn Marino     /* mv modules.dir .#modules.dir */
1051*86d7f5d3SJohn Marino     if (CVS_RENAME (dotdir, bakdir) < 0)
1052*86d7f5d3SJohn Marino 	dir2_errno = errno;
1053*86d7f5d3SJohn Marino     /* mv modules.pag .#modules.pag */
1054*86d7f5d3SJohn Marino     if (CVS_RENAME (dotpag, bakpag) < 0)
1055*86d7f5d3SJohn Marino 	pag2_errno = errno;
1056*86d7f5d3SJohn Marino     /* mv modules.db .#modules.db */
1057*86d7f5d3SJohn Marino     if (CVS_RENAME (dotdb, bakdb) < 0)
1058*86d7f5d3SJohn Marino 	db2_errno = errno;
1059*86d7f5d3SJohn Marino 
1060*86d7f5d3SJohn Marino     /* mv "temp".dir modules.dir */
1061*86d7f5d3SJohn Marino     if (CVS_RENAME (newdir, dotdir) < 0)
1062*86d7f5d3SJohn Marino 	dir3_errno = errno;
1063*86d7f5d3SJohn Marino     /* mv "temp".pag modules.pag */
1064*86d7f5d3SJohn Marino     if (CVS_RENAME (newpag, dotpag) < 0)
1065*86d7f5d3SJohn Marino 	pag3_errno = errno;
1066*86d7f5d3SJohn Marino     /* mv "temp".db modules.db */
1067*86d7f5d3SJohn Marino     if (CVS_RENAME (newdb, dotdb) < 0)
1068*86d7f5d3SJohn Marino 	db3_errno = errno;
1069*86d7f5d3SJohn Marino 
1070*86d7f5d3SJohn Marino     /* OK -- make my day */
1071*86d7f5d3SJohn Marino     SIG_endCrSect ();
1072*86d7f5d3SJohn Marino 
1073*86d7f5d3SJohn Marino     /* I didn't want to call error() when we had signals blocked
1074*86d7f5d3SJohn Marino        (unnecessary?), but do it now.  */
1075*86d7f5d3SJohn Marino     if (dir1_errno && !existence_error (dir1_errno))
1076*86d7f5d3SJohn Marino 	error (0, dir1_errno, "cannot remove %s", bakdir);
1077*86d7f5d3SJohn Marino     if (pag1_errno && !existence_error (pag1_errno))
1078*86d7f5d3SJohn Marino 	error (0, pag1_errno, "cannot remove %s", bakpag);
1079*86d7f5d3SJohn Marino     if (db1_errno && !existence_error (db1_errno))
1080*86d7f5d3SJohn Marino 	error (0, db1_errno, "cannot remove %s", bakdb);
1081*86d7f5d3SJohn Marino 
1082*86d7f5d3SJohn Marino     if (dir2_errno && !existence_error (dir2_errno))
1083*86d7f5d3SJohn Marino 	error (0, dir2_errno, "cannot remove %s", bakdir);
1084*86d7f5d3SJohn Marino     if (pag2_errno && !existence_error (pag2_errno))
1085*86d7f5d3SJohn Marino 	error (0, pag2_errno, "cannot remove %s", bakpag);
1086*86d7f5d3SJohn Marino     if (db2_errno && !existence_error (db2_errno))
1087*86d7f5d3SJohn Marino 	error (0, db2_errno, "cannot remove %s", bakdb);
1088*86d7f5d3SJohn Marino 
1089*86d7f5d3SJohn Marino     if (dir3_errno && !existence_error (dir3_errno))
1090*86d7f5d3SJohn Marino 	error (0, dir3_errno, "cannot remove %s", bakdir);
1091*86d7f5d3SJohn Marino     if (pag3_errno && !existence_error (pag3_errno))
1092*86d7f5d3SJohn Marino 	error (0, pag3_errno, "cannot remove %s", bakpag);
1093*86d7f5d3SJohn Marino     if (db3_errno && !existence_error (db3_errno))
1094*86d7f5d3SJohn Marino 	error (0, db3_errno, "cannot remove %s", bakdb);
1095*86d7f5d3SJohn Marino }
1096*86d7f5d3SJohn Marino 
1097*86d7f5d3SJohn Marino #endif				/* !MY_NDBM */
1098*86d7f5d3SJohn Marino 
1099*86d7f5d3SJohn Marino static void
rename_rcsfile(char * temp,char * real)1100*86d7f5d3SJohn Marino rename_rcsfile (char *temp, char *real)
1101*86d7f5d3SJohn Marino {
1102*86d7f5d3SJohn Marino     char *bak;
1103*86d7f5d3SJohn Marino     struct stat statbuf;
1104*86d7f5d3SJohn Marino     char *rcs;
1105*86d7f5d3SJohn Marino 
1106*86d7f5d3SJohn Marino     /* Set "x" bits if set in original. */
1107*86d7f5d3SJohn Marino     rcs = Xasprintf ("%s%s", real, RCSEXT);
1108*86d7f5d3SJohn Marino     statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */
1109*86d7f5d3SJohn Marino     if (stat (rcs, &statbuf) < 0
1110*86d7f5d3SJohn Marino 	&& !existence_error (errno))
1111*86d7f5d3SJohn Marino 	error (0, errno, "cannot stat %s", rcs);
1112*86d7f5d3SJohn Marino     free (rcs);
1113*86d7f5d3SJohn Marino 
1114*86d7f5d3SJohn Marino     if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0)
1115*86d7f5d3SJohn Marino 	error (0, errno, "warning: cannot chmod %s", temp);
1116*86d7f5d3SJohn Marino     bak = Xasprintf ("%s%s", BAKPREFIX, real);
1117*86d7f5d3SJohn Marino 
1118*86d7f5d3SJohn Marino     /* rm .#loginfo */
1119*86d7f5d3SJohn Marino     if (unlink_file (bak) < 0
1120*86d7f5d3SJohn Marino 	&& !existence_error (errno))
1121*86d7f5d3SJohn Marino 	error (0, errno, "cannot remove %s", bak);
1122*86d7f5d3SJohn Marino 
1123*86d7f5d3SJohn Marino     /* mv loginfo .#loginfo */
1124*86d7f5d3SJohn Marino     if (CVS_RENAME (real, bak) < 0
1125*86d7f5d3SJohn Marino 	&& !existence_error (errno))
1126*86d7f5d3SJohn Marino 	error (0, errno, "cannot rename %s to %s", real, bak);
1127*86d7f5d3SJohn Marino 
1128*86d7f5d3SJohn Marino     /* mv "temp" loginfo */
1129*86d7f5d3SJohn Marino     if (CVS_RENAME (temp, real) < 0
1130*86d7f5d3SJohn Marino 	&& !existence_error (errno))
1131*86d7f5d3SJohn Marino 	error (0, errno, "cannot rename %s to %s", temp, real);
1132*86d7f5d3SJohn Marino 
1133*86d7f5d3SJohn Marino     free (bak);
1134*86d7f5d3SJohn Marino }
1135*86d7f5d3SJohn Marino 
1136*86d7f5d3SJohn Marino const char *const init_usage[] = {
1137*86d7f5d3SJohn Marino     "Usage: %s %s\n",
1138*86d7f5d3SJohn Marino     "(Specify the --help global option for a list of other help options)\n",
1139*86d7f5d3SJohn Marino     NULL
1140*86d7f5d3SJohn Marino };
1141*86d7f5d3SJohn Marino 
1142*86d7f5d3SJohn Marino int
init(int argc,char ** argv)1143*86d7f5d3SJohn Marino init (int argc, char **argv)
1144*86d7f5d3SJohn Marino {
1145*86d7f5d3SJohn Marino     /* Name of CVSROOT directory.  */
1146*86d7f5d3SJohn Marino     char *adm;
1147*86d7f5d3SJohn Marino     /* Name of this administrative file.  */
1148*86d7f5d3SJohn Marino     char *info;
1149*86d7f5d3SJohn Marino     /* Name of ,v file for this administrative file.  */
1150*86d7f5d3SJohn Marino     char *info_v;
1151*86d7f5d3SJohn Marino     /* Exit status.  */
1152*86d7f5d3SJohn Marino     int err = 0;
1153*86d7f5d3SJohn Marino 
1154*86d7f5d3SJohn Marino     const struct admin_file *fileptr;
1155*86d7f5d3SJohn Marino 
1156*86d7f5d3SJohn Marino     umask (cvsumask);
1157*86d7f5d3SJohn Marino 
1158*86d7f5d3SJohn Marino     if (argc == -1 || argc > 1)
1159*86d7f5d3SJohn Marino 	usage (init_usage);
1160*86d7f5d3SJohn Marino 
1161*86d7f5d3SJohn Marino #ifdef CLIENT_SUPPORT
1162*86d7f5d3SJohn Marino     if (current_parsed_root->isremote)
1163*86d7f5d3SJohn Marino     {
1164*86d7f5d3SJohn Marino 	start_server ();
1165*86d7f5d3SJohn Marino 
1166*86d7f5d3SJohn Marino 	ign_setup ();
1167*86d7f5d3SJohn Marino 	send_init_command ();
1168*86d7f5d3SJohn Marino 	return get_responses_and_close ();
1169*86d7f5d3SJohn Marino     }
1170*86d7f5d3SJohn Marino #endif /* CLIENT_SUPPORT */
1171*86d7f5d3SJohn Marino 
1172*86d7f5d3SJohn Marino     /* Note: we do *not* create parent directories as needed like the
1173*86d7f5d3SJohn Marino        old cvsinit.sh script did.  Few utilities do that, and a
1174*86d7f5d3SJohn Marino        non-existent parent directory is as likely to be a typo as something
1175*86d7f5d3SJohn Marino        which needs to be created.  */
1176*86d7f5d3SJohn Marino     mkdir_if_needed (current_parsed_root->directory);
1177*86d7f5d3SJohn Marino 
1178*86d7f5d3SJohn Marino     adm = Xasprintf ("%s/%s", current_parsed_root->directory, CVSROOTADM);
1179*86d7f5d3SJohn Marino     mkdir_if_needed (adm);
1180*86d7f5d3SJohn Marino 
1181*86d7f5d3SJohn Marino     /* This is needed because we pass "fileptr->filename" not "info"
1182*86d7f5d3SJohn Marino        to add_rcs_file below.  I think this would be easy to change,
1183*86d7f5d3SJohn Marino        thus nuking the need for CVS_CHDIR here, but I haven't looked
1184*86d7f5d3SJohn Marino        closely (e.g. see wrappers calls within add_rcs_file).  */
1185*86d7f5d3SJohn Marino     if ( CVS_CHDIR (adm) < 0)
1186*86d7f5d3SJohn Marino 	error (1, errno, "cannot change to directory %s", adm);
1187*86d7f5d3SJohn Marino 
1188*86d7f5d3SJohn Marino     /* Make Emptydir so it's there if we need it */
1189*86d7f5d3SJohn Marino     mkdir_if_needed (CVSNULLREPOS);
1190*86d7f5d3SJohn Marino 
1191*86d7f5d3SJohn Marino     /* 80 is long enough for all the administrative file names, plus
1192*86d7f5d3SJohn Marino        "/" and so on.  */
1193*86d7f5d3SJohn Marino     info = xmalloc (strlen (adm) + 80);
1194*86d7f5d3SJohn Marino     info_v = xmalloc (strlen (adm) + 80);
1195*86d7f5d3SJohn Marino     for (fileptr = filelist; fileptr && fileptr->filename; ++fileptr)
1196*86d7f5d3SJohn Marino     {
1197*86d7f5d3SJohn Marino 	if (fileptr->contents == NULL)
1198*86d7f5d3SJohn Marino 	    continue;
1199*86d7f5d3SJohn Marino 	strcpy (info, adm);
1200*86d7f5d3SJohn Marino 	strcat (info, "/");
1201*86d7f5d3SJohn Marino 	strcat (info, fileptr->filename);
1202*86d7f5d3SJohn Marino 	strcpy (info_v, info);
1203*86d7f5d3SJohn Marino 	strcat (info_v, RCSEXT);
1204*86d7f5d3SJohn Marino 	if (isfile (info_v))
1205*86d7f5d3SJohn Marino 	    /* We will check out this file in the mkmodules step.
1206*86d7f5d3SJohn Marino 	       Nothing else is required.  */
1207*86d7f5d3SJohn Marino 	    ;
1208*86d7f5d3SJohn Marino 	else
1209*86d7f5d3SJohn Marino 	{
1210*86d7f5d3SJohn Marino 	    int retcode;
1211*86d7f5d3SJohn Marino 
1212*86d7f5d3SJohn Marino 	    if (!isfile (info))
1213*86d7f5d3SJohn Marino 	    {
1214*86d7f5d3SJohn Marino 		FILE *fp;
1215*86d7f5d3SJohn Marino 		const char * const *p;
1216*86d7f5d3SJohn Marino 
1217*86d7f5d3SJohn Marino 		fp = xfopen (info, "w");
1218*86d7f5d3SJohn Marino 		for (p = fileptr->contents; *p != NULL; ++p)
1219*86d7f5d3SJohn Marino 		    if (fputs (*p, fp) < 0)
1220*86d7f5d3SJohn Marino 			error (1, errno, "cannot write %s", info);
1221*86d7f5d3SJohn Marino 		if (fclose (fp) < 0)
1222*86d7f5d3SJohn Marino 		    error (1, errno, "cannot close %s", info);
1223*86d7f5d3SJohn Marino 	    }
1224*86d7f5d3SJohn Marino 	    /* The message used to say " of " and fileptr->filename after
1225*86d7f5d3SJohn Marino 	       "initial checkin" but I fail to see the point as we know what
1226*86d7f5d3SJohn Marino 	       file it is from the name.  */
1227*86d7f5d3SJohn Marino 	    retcode = add_rcs_file ("initial checkin", info_v,
1228*86d7f5d3SJohn Marino 				    fileptr->filename, "1.1", NULL,
1229*86d7f5d3SJohn Marino 
1230*86d7f5d3SJohn Marino 				    /* No vendor branch.  */
1231*86d7f5d3SJohn Marino 				    NULL, NULL, 0, NULL,
1232*86d7f5d3SJohn Marino 
1233*86d7f5d3SJohn Marino 				    NULL, 0, NULL, 0);
1234*86d7f5d3SJohn Marino 	    if (retcode != 0)
1235*86d7f5d3SJohn Marino 		/* add_rcs_file already printed an error message.  */
1236*86d7f5d3SJohn Marino 		err = 1;
1237*86d7f5d3SJohn Marino 	}
1238*86d7f5d3SJohn Marino     }
1239*86d7f5d3SJohn Marino 
1240*86d7f5d3SJohn Marino     /* Turn on history logging by default.  The user can remove the file
1241*86d7f5d3SJohn Marino        to disable it.  */
1242*86d7f5d3SJohn Marino     strcpy (info, adm);
1243*86d7f5d3SJohn Marino     strcat (info, "/");
1244*86d7f5d3SJohn Marino     strcat (info, CVSROOTADM_HISTORY);
1245*86d7f5d3SJohn Marino     if (!isfile (info))
1246*86d7f5d3SJohn Marino     {
1247*86d7f5d3SJohn Marino 	FILE *fp;
1248*86d7f5d3SJohn Marino 
1249*86d7f5d3SJohn Marino 	fp = xfopen (info, "w");
1250*86d7f5d3SJohn Marino 	if (fclose (fp) < 0)
1251*86d7f5d3SJohn Marino 	    error (1, errno, "cannot close %s", info);
1252*86d7f5d3SJohn Marino 
1253*86d7f5d3SJohn Marino         /* Make the new history file world-writeable, since every CVS
1254*86d7f5d3SJohn Marino            user will need to be able to write to it.  We use chmod()
1255*86d7f5d3SJohn Marino            because xchmod() is too shy. */
1256*86d7f5d3SJohn Marino         chmod (info, 0666);
1257*86d7f5d3SJohn Marino     }
1258*86d7f5d3SJohn Marino 
1259*86d7f5d3SJohn Marino     /* Make an empty val-tags file to prevent problems creating it later.  */
1260*86d7f5d3SJohn Marino     strcpy (info, adm);
1261*86d7f5d3SJohn Marino     strcat (info, "/");
1262*86d7f5d3SJohn Marino     strcat (info, CVSROOTADM_VALTAGS);
1263*86d7f5d3SJohn Marino     if (!isfile (info))
1264*86d7f5d3SJohn Marino     {
1265*86d7f5d3SJohn Marino 	FILE *fp;
1266*86d7f5d3SJohn Marino 
1267*86d7f5d3SJohn Marino 	fp = xfopen (info, "w");
1268*86d7f5d3SJohn Marino 	if (fclose (fp) < 0)
1269*86d7f5d3SJohn Marino 	    error (1, errno, "cannot close %s", info);
1270*86d7f5d3SJohn Marino 
1271*86d7f5d3SJohn Marino         /* Make the new val-tags file world-writeable, since every CVS
1272*86d7f5d3SJohn Marino            user will need to be able to write to it.  We use chmod()
1273*86d7f5d3SJohn Marino            because xchmod() is too shy. */
1274*86d7f5d3SJohn Marino         chmod (info, 0666);
1275*86d7f5d3SJohn Marino     }
1276*86d7f5d3SJohn Marino 
1277*86d7f5d3SJohn Marino     free (info);
1278*86d7f5d3SJohn Marino     free (info_v);
1279*86d7f5d3SJohn Marino 
1280*86d7f5d3SJohn Marino     mkmodules (adm);
1281*86d7f5d3SJohn Marino 
1282*86d7f5d3SJohn Marino     free (adm);
1283*86d7f5d3SJohn Marino     return err;
1284*86d7f5d3SJohn Marino }
1285