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