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