File: | src/gnu/usr.bin/cvs/src/mkmodules.c |
Warning: | line 939, column 3 Value stored to 'err' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* |
2 | * Copyright (c) 1992, Brian Berliner and Jeff Polk |
3 | * Copyright (c) 1989-1992, Brian Berliner |
4 | * |
5 | * You may distribute under the terms of the GNU General Public License as |
6 | * specified in the README file that comes with the CVS kit. */ |
7 | |
8 | #include "cvs.h" |
9 | #include "savecwd.h" |
10 | #include "getline.h" |
11 | |
12 | #ifndef DBLKSIZ4096 |
13 | #define DBLKSIZ4096 4096 /* since GNU ndbm doesn't define it */ |
14 | #endif |
15 | |
16 | static int checkout_file PROTO((char *file, char *temp))(char *file, char *temp); |
17 | static char *make_tempfile PROTO((void))(void); |
18 | static void rename_rcsfile PROTO((char *temp, char *real))(char *temp, char *real); |
19 | |
20 | #ifndef MY_NDBM |
21 | static void rename_dbmfile PROTO((char *temp))(char *temp); |
22 | static void write_dbmfile PROTO((char *temp))(char *temp); |
23 | #endif /* !MY_NDBM */ |
24 | |
25 | /* Structure which describes an administrative file. */ |
26 | struct admin_file { |
27 | /* Name of the file, within the CVSROOT directory. */ |
28 | char *filename; |
29 | |
30 | /* This is a one line description of what the file is for. It is not |
31 | currently used, although one wonders whether it should be, somehow. |
32 | If NULL, then don't process this file in mkmodules (FIXME?: a bit of |
33 | a kludge; probably should replace this with a flags field). */ |
34 | char *errormsg; |
35 | |
36 | /* Contents which the file should have in a new repository. To avoid |
37 | problems with brain-dead compilers which choke on long string constants, |
38 | this is a pointer to an array of char * terminated by NULL--each of |
39 | the strings is concatenated. |
40 | |
41 | If this field is NULL, the file is not created in a new |
42 | repository, but it can be added with "cvs add" (just as if one |
43 | had created the repository with a version of CVS which didn't |
44 | know about the file) and the checked-out copy will be updated |
45 | without having to add it to checkoutlist. */ |
46 | const char * const *contents; |
47 | }; |
48 | |
49 | static const char *const loginfo_contents[] = { |
50 | "# The \"loginfo\" file controls where \"cvs commit\" log information\n", |
51 | "# is sent. The first entry on a line is a regular expression which must match\n", |
52 | "# the directory that the change is being made to, relative to the\n", |
53 | "# $CVSROOT. If a match is found, then the remainder of the line is a filter\n", |
54 | "# program that should expect log information on its standard input.\n", |
55 | "#\n", |
56 | "# If the repository name does not match any of the regular expressions in this\n", |
57 | "# file, the \"DEFAULT\" line is used, if it is specified.\n", |
58 | "#\n", |
59 | "# If the name ALL appears as a regular expression it is always used\n", |
60 | "# in addition to the first matching regex or DEFAULT.\n", |
61 | "#\n", |
62 | "# You may specify a format string as part of the\n", |
63 | "# filter. The string is composed of a `%' followed\n", |
64 | "# by a single format character, or followed by a set of format\n", |
65 | "# characters surrounded by `{' and `}' as separators. The format\n", |
66 | "# characters are:\n", |
67 | "#\n", |
68 | "# s = file name\n", |
69 | "# t = tag name\n", |
70 | "# V = old version number (pre-checkin)\n", |
71 | "# v = new version number (post-checkin)\n", |
72 | "#\n", |
73 | "# For example:\n", |
74 | "#DEFAULT (echo \"\"; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", |
75 | "# or\n", |
76 | "#DEFAULT (echo \"\"; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", |
77 | NULL((void*)0) |
78 | }; |
79 | |
80 | static const char *const rcsinfo_contents[] = { |
81 | "# The \"rcsinfo\" file is used to control templates with which the editor\n", |
82 | "# is invoked on commit and import.\n", |
83 | "#\n", |
84 | "# The first entry on a line is a regular expression which is tested\n", |
85 | "# against the directory that the change is being made to, relative to the\n", |
86 | "# $CVSROOT. For the first match that is found, then the remainder of the\n", |
87 | "# line is the name of the file that contains the template.\n", |
88 | "#\n", |
89 | "# If the repository name does not match any of the regular expressions in this\n", |
90 | "# file, the \"DEFAULT\" line is used, if it is specified.\n", |
91 | "#\n", |
92 | "# If the name \"ALL\" appears as a regular expression it is always used\n", |
93 | "# in addition to the first matching regex or \"DEFAULT\".\n", |
94 | NULL((void*)0) |
95 | }; |
96 | |
97 | static const char *const editinfo_contents[] = { |
98 | "# The \"editinfo\" file is used to allow verification of logging\n", |
99 | "# information. It works best when a template (as specified in the\n", |
100 | "# rcsinfo file) is provided for the logging procedure. Given a\n", |
101 | "# template with locations for, a bug-id number, a list of people who\n", |
102 | "# reviewed the code before it can be checked in, and an external\n", |
103 | "# process to catalog the differences that were code reviewed, the\n", |
104 | "# following test can be applied to the code:\n", |
105 | "#\n", |
106 | "# Making sure that the entered bug-id number is correct.\n", |
107 | "# Validating that the code that was reviewed is indeed the code being\n", |
108 | "# checked in (using the bug-id number or a seperate review\n", |
109 | "# number to identify this particular code set.).\n", |
110 | "#\n", |
111 | "# If any of the above test failed, then the commit would be aborted.\n", |
112 | "#\n", |
113 | "# Actions such as mailing a copy of the report to each reviewer are\n", |
114 | "# better handled by an entry in the loginfo file.\n", |
115 | "#\n", |
116 | "# One thing that should be noted is the the ALL keyword is not\n", |
117 | "# supported. There can be only one entry that matches a given\n", |
118 | "# repository.\n", |
119 | NULL((void*)0) |
120 | }; |
121 | |
122 | static const char *const verifymsg_contents[] = { |
123 | "# The \"verifymsg\" file is used to allow verification of logging\n", |
124 | "# information. It works best when a template (as specified in the\n", |
125 | "# rcsinfo file) is provided for the logging procedure. Given a\n", |
126 | "# template with locations for, a bug-id number, a list of people who\n", |
127 | "# reviewed the code before it can be checked in, and an external\n", |
128 | "# process to catalog the differences that were code reviewed, the\n", |
129 | "# following test can be applied to the code:\n", |
130 | "#\n", |
131 | "# Making sure that the entered bug-id number is correct.\n", |
132 | "# Validating that the code that was reviewed is indeed the code being\n", |
133 | "# checked in (using the bug-id number or a seperate review\n", |
134 | "# number to identify this particular code set.).\n", |
135 | "#\n", |
136 | "# If any of the above test failed, then the commit would be aborted.\n", |
137 | "#\n", |
138 | "# Actions such as mailing a copy of the report to each reviewer are\n", |
139 | "# better handled by an entry in the loginfo file.\n", |
140 | "#\n", |
141 | "# One thing that should be noted is the the ALL keyword is not\n", |
142 | "# supported. There can be only one entry that matches a given\n", |
143 | "# repository.\n", |
144 | NULL((void*)0) |
145 | }; |
146 | |
147 | static const char *const commitinfo_contents[] = { |
148 | "# The \"commitinfo\" file is used to control pre-commit checks.\n", |
149 | "# The filter on the right is invoked with the repository and a list \n", |
150 | "# of files to check. A non-zero exit of the filter program will \n", |
151 | "# cause the commit to be aborted.\n", |
152 | "#\n", |
153 | "# The first entry on a line is a regular expression which is tested\n", |
154 | "# against the directory that the change is being committed to, relative\n", |
155 | "# to the $CVSROOT. For the first match that is found, then the remainder\n", |
156 | "# of the line is the name of the filter to run.\n", |
157 | "#\n", |
158 | "# If the repository name does not match any of the regular expressions in this\n", |
159 | "# file, the \"DEFAULT\" line is used, if it is specified.\n", |
160 | "#\n", |
161 | "# If the name \"ALL\" appears as a regular expression it is always used\n", |
162 | "# in addition to the first matching regex or \"DEFAULT\".\n", |
163 | NULL((void*)0) |
164 | }; |
165 | |
166 | static const char *const taginfo_contents[] = { |
167 | "# The \"taginfo\" file is used to control pre-tag checks.\n", |
168 | "# The filter on the right is invoked with the following arguments:\n", |
169 | "#\n", |
170 | "# $1 -- tagname\n", |
171 | "# $2 -- operation \"add\" for tag, \"mov\" for tag -F, and \"del\" for tag -d\n", |
172 | "# $3 -- repository\n", |
173 | "# $4-> file revision [file revision ...]\n", |
174 | "#\n", |
175 | "# A non-zero exit of the filter program will cause the tag to be aborted.\n", |
176 | "#\n", |
177 | "# The first entry on a line is a regular expression which is tested\n", |
178 | "# against the directory that the change is being committed to, relative\n", |
179 | "# to the $CVSROOT. For the first match that is found, then the remainder\n", |
180 | "# of the line is the name of the filter to run.\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((void*)0) |
188 | }; |
189 | |
190 | static const char *const checkoutlist_contents[] = { |
191 | "# The \"checkoutlist\" file is used to support additional version controlled\n", |
192 | "# administrative files in $CVSROOT/CVSROOT, such as template files.\n", |
193 | "#\n", |
194 | "# The first entry on a line is a filename which will be checked out from\n", |
195 | "# the corresponding RCS file in the $CVSROOT/CVSROOT directory.\n", |
196 | "# The remainder of the line is an error message to use if the file cannot\n", |
197 | "# be checked out.\n", |
198 | "#\n", |
199 | "# File format:\n", |
200 | "#\n", |
201 | "# [<whitespace>]<filename><whitespace><error message><end-of-line>\n", |
202 | "#\n", |
203 | "# comment lines begin with '#'\n", |
204 | NULL((void*)0) |
205 | }; |
206 | |
207 | static const char *const cvswrappers_contents[] = { |
208 | "# This file affects handling of files based on their names.\n", |
209 | "#\n", |
210 | "# The -t/-f options allow one to treat directories of files\n", |
211 | "# as a single file, or to transform a file in other ways on\n", |
212 | "# its way in and out of CVS.\n", |
213 | "#\n", |
214 | "# The -m option specifies whether CVS attempts to merge files.\n", |
215 | "#\n", |
216 | "# The -k option specifies keyword expansion (e.g. -kb for binary).\n", |
217 | "#\n", |
218 | "# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)\n", |
219 | "#\n", |
220 | "# wildcard [option value][option value]...\n", |
221 | "#\n", |
222 | "# where option is one of\n", |
223 | "# -f from cvs filter value: path to filter\n", |
224 | "# -t to cvs filter value: path to filter\n", |
225 | "# -m update methodology value: MERGE or COPY\n", |
226 | "# -k expansion mode value: b, o, kkv, &c\n", |
227 | "#\n", |
228 | "# and value is a single-quote delimited value.\n", |
229 | "# For example:\n", |
230 | "#*.gif -k 'b'\n", |
231 | NULL((void*)0) |
232 | }; |
233 | |
234 | static const char *const notify_contents[] = { |
235 | "# The \"notify\" file controls where notifications from watches set by\n", |
236 | "# \"cvs watch add\" or \"cvs edit\" are sent. The first entry on a line is\n", |
237 | "# a regular expression which is tested against the directory that the\n", |
238 | "# change is being made to, relative to the $CVSROOT. If it matches,\n", |
239 | "# then the remainder of the line is a filter program that should contain\n", |
240 | "# one occurrence of %s for the user to notify, and information on its\n", |
241 | "# standard input.\n", |
242 | "#\n", |
243 | "# \"ALL\" or \"DEFAULT\" can be used in place of the regular expression.\n", |
244 | "#\n", |
245 | "# For example:\n", |
246 | "#ALL mail %s -s \"CVS notification\"\n", |
247 | NULL((void*)0) |
248 | }; |
249 | |
250 | static const char *const modules_contents[] = { |
251 | "# Three different line formats are valid:\n", |
252 | "# key -a aliases...\n", |
253 | "# key [options] directory\n", |
254 | "# key [options] directory files...\n", |
255 | "#\n", |
256 | "# Where \"options\" are composed of:\n", |
257 | "# -i prog Run \"prog\" on \"cvs commit\" from top-level of module.\n", |
258 | "# -o prog Run \"prog\" on \"cvs checkout\" of module.\n", |
259 | "# -e prog Run \"prog\" on \"cvs export\" of module.\n", |
260 | "# -t prog Run \"prog\" on \"cvs rtag\" of module.\n", |
261 | "# -u prog Run \"prog\" on \"cvs update\" of module.\n", |
262 | "# -d dir Place module in directory \"dir\" instead of module name.\n", |
263 | "# -l Top-level directory only -- do not recurse.\n", |
264 | "#\n", |
265 | "# NOTE: If you change any of the \"Run\" options above, you'll have to\n", |
266 | "# release and re-checkout any working directories of these modules.\n", |
267 | "#\n", |
268 | "# And \"directory\" is a path to a directory relative to $CVSROOT.\n", |
269 | "#\n", |
270 | "# The \"-a\" option specifies an alias. An alias is interpreted as if\n", |
271 | "# everything on the right of the \"-a\" had been typed on the command line.\n", |
272 | "#\n", |
273 | "# You can encode a module within a module by using the special '&'\n", |
274 | "# character to interpose another module into the current module. This\n", |
275 | "# can be useful for creating a module that consists of many directories\n", |
276 | "# spread out over the entire source repository.\n", |
277 | NULL((void*)0) |
278 | }; |
279 | |
280 | static const char *const config_contents[] = { |
281 | "# Set this to \"no\" if pserver shouldn't check system users/passwords\n", |
282 | "#SystemAuth=no\n", |
283 | "\n", |
284 | "# Put CVS lock files in this directory rather than directly in the repository.\n", |
285 | "#LockDir=/var/lock/cvs\n", |
286 | "\n", |
287 | #ifdef PRESERVE_PERMISSIONS_SUPPORT |
288 | "# Set `PreservePermissions' to `yes' to save file status information\n", |
289 | "# in the repository.\n", |
290 | "#PreservePermissions=no\n", |
291 | "\n", |
292 | #endif |
293 | "# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top\n", |
294 | "# level of the new working directory when using the `cvs checkout'\n", |
295 | "# command.\n", |
296 | "#TopLevelAdmin=no\n", |
297 | "\n", |
298 | "# Set this to the name of a local tag to use in addition to Id\n", |
299 | "#tag=OurTag\n", |
300 | "\n", |
301 | "# Set this to the default umask to use when creating files and directories\n", |
302 | "#umask=002\n", |
303 | "\n", |
304 | "# Set this to the default data resource limit to use\n", |
305 | "#dlimit=65536\n", |
306 | "\n", |
307 | "# Set `LogHistory' to `all' or `TOFEWGCMAR' to log all transactions to the\n", |
308 | "# history file, or a subset as needed (ie `TMAR' logs all write operations)\n", |
309 | "#LogHistory=TOFEWGCMAR\n", |
310 | "\n", |
311 | "# Set DisableMdocdate=yes to turn off expansion of the Mdocdate keyword\n", |
312 | "#DisableMdocdate=no\n", |
313 | NULL((void*)0) |
314 | }; |
315 | |
316 | static const struct admin_file filelist[] = { |
317 | {CVSROOTADM_LOGINFO"loginfo", |
318 | "no logging of 'cvs commit' messages is done without a %s file", |
319 | &loginfo_contents[0]}, |
320 | {CVSROOTADM_RCSINFO"rcsinfo", |
321 | "a %s file can be used to configure 'cvs commit' templates", |
322 | rcsinfo_contents}, |
323 | {CVSROOTADM_EDITINFO"editinfo", |
324 | "a %s file can be used to validate log messages", |
325 | editinfo_contents}, |
326 | {CVSROOTADM_VERIFYMSG"verifymsg", |
327 | "a %s file can be used to validate log messages", |
328 | verifymsg_contents}, |
329 | {CVSROOTADM_COMMITINFO"commitinfo", |
330 | "a %s file can be used to configure 'cvs commit' checking", |
331 | commitinfo_contents}, |
332 | {CVSROOTADM_TAGINFO"taginfo", |
333 | "a %s file can be used to configure 'cvs tag' checking", |
334 | taginfo_contents}, |
335 | {CVSROOTADM_IGNORE"cvsignore", |
336 | "a %s file can be used to specify files to ignore", |
337 | NULL((void*)0)}, |
338 | {CVSROOTADM_CHECKOUTLIST"checkoutlist", |
339 | "a %s file can specify extra CVSROOT files to auto-checkout", |
340 | checkoutlist_contents}, |
341 | {CVSROOTADM_WRAPPER"cvswrappers", |
342 | "a %s file can be used to specify files to treat as wrappers", |
343 | cvswrappers_contents}, |
344 | {CVSROOTADM_NOTIFY"notify", |
345 | "a %s file can be used to specify where notifications go", |
346 | notify_contents}, |
347 | {CVSROOTADM_MODULES"modules", |
348 | /* modules is special-cased in mkmodules. */ |
349 | NULL((void*)0), |
350 | modules_contents}, |
351 | {CVSROOTADM_READERS"readers", |
352 | "a %s file specifies read-only users", |
353 | NULL((void*)0)}, |
354 | {CVSROOTADM_WRITERS"writers", |
355 | "a %s file specifies read/write users", |
356 | NULL((void*)0)}, |
357 | |
358 | /* Some have suggested listing CVSROOTADM_PASSWD here too. This |
359 | would mean that CVS commands which operate on the |
360 | CVSROOTADM_PASSWD file would transmit hashed passwords over the |
361 | net. This might seem to be no big deal, as pserver normally |
362 | transmits cleartext passwords, but the difference is that |
363 | CVSROOTADM_PASSWD contains *all* passwords, not just the ones |
364 | currently being used. For example, it could be too easy to |
365 | accidentally give someone readonly access to CVSROOTADM_PASSWD |
366 | (e.g. via anonymous CVS or cvsweb), and then if there are any |
367 | guessable passwords for read/write access (usually there will be) |
368 | they get read/write access. |
369 | |
370 | Another worry is the implications of storing old passwords--if |
371 | someone used a password in the past they might be using it |
372 | elsewhere, using a similar password, etc, and so saving old |
373 | passwords, even hashed, is probably not a good idea. */ |
374 | |
375 | {CVSROOTADM_CONFIG"config", |
376 | "a %s file configures various behaviors", |
377 | config_contents}, |
378 | {NULL((void*)0), NULL((void*)0), NULL((void*)0)} |
379 | }; |
380 | |
381 | /* Rebuild the checked out administrative files in directory DIR. */ |
382 | int |
383 | mkmodules (dir) |
384 | char *dir; |
385 | { |
386 | struct saved_cwd cwd; |
387 | char *temp; |
388 | char *cp, *last, *fname; |
389 | #ifdef MY_NDBM |
390 | DBM *db; |
391 | #endif |
392 | FILE *fp; |
393 | char *line = NULL((void*)0); |
394 | size_t line_allocated = 0; |
395 | const struct admin_file *fileptr; |
396 | |
397 | if (noexec) |
398 | return 0; |
399 | |
400 | if (save_cwd (&cwd)) |
401 | error_exit (); |
402 | |
403 | if ( CVS_CHDIRchdir (dir) < 0) |
404 | error (1, errno(*__errno()), "cannot chdir to %s", dir); |
405 | |
406 | /* |
407 | * First, do the work necessary to update the "modules" database. |
408 | */ |
409 | temp = make_tempfile (); |
410 | switch (checkout_file (CVSROOTADM_MODULES"modules", temp)) |
411 | { |
412 | |
413 | case 0: /* everything ok */ |
414 | #ifdef MY_NDBM |
415 | /* open it, to generate any duplicate errors */ |
416 | if ((db = dbm_openmydbm_open (temp, O_RDONLY0x0000, 0666)) != NULL((void*)0)) |
417 | dbm_closemydbm_close (db); |
418 | #else |
419 | write_dbmfile (temp); |
420 | rename_dbmfile (temp); |
421 | #endif |
422 | rename_rcsfile (temp, CVSROOTADM_MODULES"modules"); |
423 | break; |
424 | |
425 | default: |
426 | error (0, 0, |
427 | "'cvs checkout' is less functional without a %s file", |
428 | CVSROOTADM_MODULES"modules"); |
429 | break; |
430 | } /* switch on checkout_file() */ |
431 | |
432 | if (unlink_file (temp) < 0 |
433 | && !existence_error (errno)(((*__errno())) == 2)) |
434 | error (0, errno(*__errno()), "cannot remove %s", temp); |
435 | free (temp); |
436 | |
437 | /* Checkout the files that need it in CVSROOT dir */ |
438 | for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) { |
439 | if (fileptr->errormsg == NULL((void*)0)) |
440 | continue; |
441 | temp = make_tempfile (); |
442 | if (checkout_file (fileptr->filename, temp) == 0) |
443 | rename_rcsfile (temp, fileptr->filename); |
444 | #if 0 |
445 | /* |
446 | * If there was some problem other than the file not existing, |
447 | * checkout_file already printed a real error message. If the |
448 | * file does not exist, it is harmless--it probably just means |
449 | * that the repository was created with an old version of CVS |
450 | * which didn't have so many files in CVSROOT. |
451 | */ |
452 | else if (fileptr->errormsg) |
453 | error (0, 0, fileptr->errormsg, fileptr->filename); |
454 | #endif |
455 | if (unlink_file (temp) < 0 |
456 | && !existence_error (errno)(((*__errno())) == 2)) |
457 | error (0, errno(*__errno()), "cannot remove %s", temp); |
458 | free (temp); |
459 | } |
460 | |
461 | fp = CVS_FOPENfopen (CVSROOTADM_CHECKOUTLIST"checkoutlist", "r"); |
462 | if (fp) |
463 | { |
464 | /* |
465 | * File format: |
466 | * [<whitespace>]<filename><whitespace><error message><end-of-line> |
467 | * |
468 | * comment lines begin with '#' |
469 | */ |
470 | while (get_line (&line, &line_allocated, fp) >= 0) |
471 | { |
472 | /* skip lines starting with # */ |
473 | if (line[0] == '#') |
474 | continue; |
475 | |
476 | if ((last = strrchr (line, '\n')) != NULL((void*)0)) |
477 | *last = '\0'; /* strip the newline */ |
478 | |
479 | /* Skip leading white space. */ |
480 | for (fname = line; |
481 | *fname && isspace ((unsigned char) *fname); |
482 | fname++) |
483 | ; |
484 | |
485 | /* Find end of filename. */ |
486 | for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++) |
487 | ; |
488 | *cp = '\0'; |
489 | |
490 | temp = make_tempfile (); |
491 | if (checkout_file (fname, temp) == 0) |
492 | { |
493 | rename_rcsfile (temp, fname); |
494 | } |
495 | else |
496 | { |
497 | for (cp++; |
498 | cp < last && *last && isspace ((unsigned char) *last); |
499 | cp++) |
500 | ; |
501 | if (cp < last && *cp) |
502 | error (0, 0, cp, fname); |
503 | } |
504 | if (unlink_file (temp) < 0 |
505 | && !existence_error (errno)(((*__errno())) == 2)) |
506 | error (0, errno(*__errno()), "cannot remove %s", temp); |
507 | free (temp); |
508 | } |
509 | if (line) |
510 | free (line); |
511 | if (ferror (fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror )(fp))) |
512 | error (0, errno(*__errno()), "cannot read %s", CVSROOTADM_CHECKOUTLIST"checkoutlist"); |
513 | if (fclose (fp) < 0) |
514 | error (0, errno(*__errno()), "cannot close %s", CVSROOTADM_CHECKOUTLIST"checkoutlist"); |
515 | } |
516 | else |
517 | { |
518 | /* Error from CVS_FOPEN. */ |
519 | if (!existence_error (errno)(((*__errno())) == 2)) |
520 | error (0, errno(*__errno()), "cannot open %s", CVSROOTADM_CHECKOUTLIST"checkoutlist"); |
521 | } |
522 | |
523 | if (restore_cwd (&cwd, NULL((void*)0))) |
524 | error_exit (); |
525 | free_cwd (&cwd); |
526 | |
527 | return (0); |
528 | } |
529 | |
530 | /* |
531 | * Yeah, I know, there are NFS race conditions here. |
532 | */ |
533 | static char * |
534 | make_tempfile () |
535 | { |
536 | static int seed = 0; |
537 | int fd; |
538 | char *temp; |
539 | |
540 | if (seed == 0) |
541 | seed = getpid (); |
542 | temp = xmalloc (sizeof (BAKPREFIX".#") + 40); |
543 | while (1) |
544 | { |
545 | (void) sprintf (temp, "%s%d", BAKPREFIX".#", seed++); |
546 | if ((fd = CVS_OPENopen (temp, O_CREAT0x0200|O_EXCL0x0800|O_RDWR0x0002, 0666)) != -1) |
547 | break; |
548 | if (errno(*__errno()) != EEXIST17) |
549 | error (1, errno(*__errno()), "cannot create temporary file %s", temp); |
550 | } |
551 | if (close(fd) < 0) |
552 | error(1, errno(*__errno()), "cannot close temporary file %s", temp); |
553 | return temp; |
554 | } |
555 | |
556 | /* Get a file. If the file does not exist, return 1 silently. If |
557 | there is an error, print a message and return 1 (FIXME: probably |
558 | not a very clean convention). On success, return 0. */ |
559 | |
560 | static int |
561 | checkout_file (file, temp) |
562 | char *file; |
563 | char *temp; |
564 | { |
565 | char *rcs; |
566 | RCSNode *rcsnode; |
567 | int retcode = 0; |
568 | |
569 | if (noexec) |
570 | return 0; |
571 | |
572 | rcs = xmalloc (strlen (file) + 5); |
573 | strcpy (rcs, file); |
574 | strcat (rcs, RCSEXT",v"); |
575 | if (!isfile (rcs)) |
576 | { |
577 | free (rcs); |
578 | return (1); |
579 | } |
580 | rcsnode = RCS_parsercsfile (rcs); |
581 | retcode = RCS_checkout (rcsnode, NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0), temp, |
582 | (RCSCHECKOUTPROC) NULL((void*)0), (void *) NULL((void*)0)); |
583 | if (retcode != 0) |
584 | { |
585 | /* Probably not necessary (?); RCS_checkout already printed a |
586 | message. */ |
587 | error (0, 0, "failed to check out %s file", |
588 | file); |
589 | } |
590 | freercsnode (&rcsnode); |
591 | free (rcs); |
592 | return (retcode); |
593 | } |
594 | |
595 | #ifndef MY_NDBM |
596 | |
597 | static void |
598 | write_dbmfile (temp) |
599 | char *temp; |
600 | { |
601 | char line[DBLKSIZ4096], value[DBLKSIZ4096]; |
602 | FILE *fp; |
603 | DBM *db; |
604 | char *cp, *vp; |
605 | datum key, val; |
606 | int len, cont, err = 0; |
607 | |
608 | fp = open_file (temp, "r"); |
609 | if ((db = dbm_openmydbm_open (temp, O_RDWR0x0002 | O_CREAT0x0200 | O_TRUNC0x0400, 0666)) == NULL((void*)0)) |
610 | error (1, errno(*__errno()), "cannot open dbm file %s for creation", temp); |
611 | for (cont = 0; fgets (line, sizeof (line), fp) != NULL((void*)0);) |
612 | { |
613 | if ((cp = strrchr (line, '\n')) != NULL((void*)0)) |
614 | *cp = '\0'; /* strip the newline */ |
615 | |
616 | /* |
617 | * Add the line to the value, at the end if this is a continuation |
618 | * line; otherwise at the beginning, but only after any trailing |
619 | * backslash is removed. |
620 | */ |
621 | vp = value; |
622 | if (cont) |
623 | vp += strlen (value); |
624 | |
625 | /* |
626 | * See if the line we read is a continuation line, and strip the |
627 | * backslash if so. |
628 | */ |
629 | len = strlen (line); |
630 | if (len > 0) |
631 | cp = &line[len - 1]; |
632 | else |
633 | cp = line; |
634 | if (*cp == '\\') |
635 | { |
636 | cont = 1; |
637 | *cp = '\0'; |
638 | } |
639 | else |
640 | { |
641 | cont = 0; |
642 | } |
643 | (void) strcpy (vp, line); |
644 | if (value[0] == '#') |
645 | continue; /* comment line */ |
646 | vp = value; |
647 | while (*vp && isspace ((unsigned char) *vp)) |
648 | vp++; |
649 | if (*vp == '\0') |
650 | continue; /* empty line */ |
651 | |
652 | /* |
653 | * If this was not a continuation line, add the entry to the database |
654 | */ |
655 | if (!cont) |
656 | { |
657 | key.dptr = vp; |
658 | while (*vp && !isspace ((unsigned char) *vp)) |
659 | vp++; |
660 | key.dsize = vp - key.dptr; |
661 | *vp++ = '\0'; /* NULL terminate the key */ |
662 | while (*vp && isspace ((unsigned char) *vp)) |
663 | vp++; /* skip whitespace to value */ |
664 | if (*vp == '\0') |
665 | { |
666 | error (0, 0, "warning: NULL value for key `%s'", key.dptr); |
667 | continue; |
668 | } |
669 | val.dptr = vp; |
670 | val.dsize = strlen (vp); |
671 | if (dbm_storemydbm_store (db, key, val, DBM_INSERT0) == 1) |
672 | { |
673 | error (0, 0, "duplicate key found for `%s'", key.dptr); |
674 | err++; |
675 | } |
676 | } |
677 | } |
678 | dbm_closemydbm_close (db); |
679 | if (fclose (fp) < 0) |
680 | error (0, errno(*__errno()), "cannot close %s", temp); |
681 | if (err) |
682 | { |
683 | /* I think that the size of the buffer needed here is |
684 | just determined by sizeof (CVSROOTADM_MODULES), the |
685 | filenames created by make_tempfile, and other things that won't |
686 | overflow. */ |
687 | char dotdir[50], dotpag[50], dotdb[50]; |
688 | |
689 | (void) sprintf (dotdir, "%s.dir", temp); |
690 | (void) sprintf (dotpag, "%s.pag", temp); |
691 | (void) sprintf (dotdb, "%s.db", temp); |
692 | if (unlink_file (dotdir) < 0 |
693 | && !existence_error (errno)(((*__errno())) == 2)) |
694 | error (0, errno(*__errno()), "cannot remove %s", dotdir); |
695 | if (unlink_file (dotpag) < 0 |
696 | && !existence_error (errno)(((*__errno())) == 2)) |
697 | error (0, errno(*__errno()), "cannot remove %s", dotpag); |
698 | if (unlink_file (dotdb) < 0 |
699 | && !existence_error (errno)(((*__errno())) == 2)) |
700 | error (0, errno(*__errno()), "cannot remove %s", dotdb); |
701 | error (1, 0, "DBM creation failed; correct above errors"); |
702 | } |
703 | } |
704 | |
705 | static void |
706 | rename_dbmfile (temp) |
707 | char *temp; |
708 | { |
709 | /* I think that the size of the buffer needed here is |
710 | just determined by sizeof (CVSROOTADM_MODULES), the |
711 | filenames created by make_tempfile, and other things that won't |
712 | overflow. */ |
713 | char newdir[50], newpag[50], newdb[50]; |
714 | char dotdir[50], dotpag[50], dotdb[50]; |
715 | char bakdir[50], bakpag[50], bakdb[50]; |
716 | |
717 | int dir1_errno = 0, pag1_errno = 0, db1_errno = 0; |
718 | int dir2_errno = 0, pag2_errno = 0, db2_errno = 0; |
719 | int dir3_errno = 0, pag3_errno = 0, db3_errno = 0; |
720 | |
721 | (void) sprintf (dotdir, "%s.dir", CVSROOTADM_MODULES"modules"); |
722 | (void) sprintf (dotpag, "%s.pag", CVSROOTADM_MODULES"modules"); |
723 | (void) sprintf (dotdb, "%s.db", CVSROOTADM_MODULES"modules"); |
724 | (void) sprintf (bakdir, "%s%s.dir", BAKPREFIX".#", CVSROOTADM_MODULES"modules"); |
725 | (void) sprintf (bakpag, "%s%s.pag", BAKPREFIX".#", CVSROOTADM_MODULES"modules"); |
726 | (void) sprintf (bakdb, "%s%s.db", BAKPREFIX".#", CVSROOTADM_MODULES"modules"); |
727 | (void) sprintf (newdir, "%s.dir", temp); |
728 | (void) sprintf (newpag, "%s.pag", temp); |
729 | (void) sprintf (newdb, "%s.db", temp); |
730 | |
731 | (void) chmod (newdir, 0666); |
732 | (void) chmod (newpag, 0666); |
733 | (void) chmod (newdb, 0666); |
734 | |
735 | /* don't mess with me */ |
736 | SIG_beginCrSect (); |
737 | |
738 | /* rm .#modules.dir .#modules.pag */ |
739 | if (unlink_file (bakdir) < 0) |
740 | dir1_errno = errno(*__errno()); |
741 | if (unlink_file (bakpag) < 0) |
742 | pag1_errno = errno(*__errno()); |
743 | if (unlink_file (bakdb) < 0) |
744 | db1_errno = errno(*__errno()); |
745 | |
746 | /* mv modules.dir .#modules.dir */ |
747 | if (CVS_RENAMErename (dotdir, bakdir) < 0) |
748 | dir2_errno = errno(*__errno()); |
749 | /* mv modules.pag .#modules.pag */ |
750 | if (CVS_RENAMErename (dotpag, bakpag) < 0) |
751 | pag2_errno = errno(*__errno()); |
752 | /* mv modules.db .#modules.db */ |
753 | if (CVS_RENAMErename (dotdb, bakdb) < 0) |
754 | db2_errno = errno(*__errno()); |
755 | |
756 | /* mv "temp".dir modules.dir */ |
757 | if (CVS_RENAMErename (newdir, dotdir) < 0) |
758 | dir3_errno = errno(*__errno()); |
759 | /* mv "temp".pag modules.pag */ |
760 | if (CVS_RENAMErename (newpag, dotpag) < 0) |
761 | pag3_errno = errno(*__errno()); |
762 | /* mv "temp".db modules.db */ |
763 | if (CVS_RENAMErename (newdb, dotdb) < 0) |
764 | db3_errno = errno(*__errno()); |
765 | |
766 | /* OK -- make my day */ |
767 | SIG_endCrSect (); |
768 | |
769 | /* I didn't want to call error() when we had signals blocked |
770 | (unnecessary?), but do it now. */ |
771 | if (dir1_errno && !existence_error (dir1_errno)((dir1_errno) == 2)) |
772 | error (0, dir1_errno, "cannot remove %s", bakdir); |
773 | if (pag1_errno && !existence_error (pag1_errno)((pag1_errno) == 2)) |
774 | error (0, pag1_errno, "cannot remove %s", bakpag); |
775 | if (db1_errno && !existence_error (db1_errno)((db1_errno) == 2)) |
776 | error (0, db1_errno, "cannot remove %s", bakdb); |
777 | |
778 | if (dir2_errno && !existence_error (dir2_errno)((dir2_errno) == 2)) |
779 | error (0, dir2_errno, "cannot remove %s", bakdir); |
780 | if (pag2_errno && !existence_error (pag2_errno)((pag2_errno) == 2)) |
781 | error (0, pag2_errno, "cannot remove %s", bakpag); |
782 | if (db2_errno && !existence_error (db2_errno)((db2_errno) == 2)) |
783 | error (0, db2_errno, "cannot remove %s", bakdb); |
784 | |
785 | if (dir3_errno && !existence_error (dir3_errno)((dir3_errno) == 2)) |
786 | error (0, dir3_errno, "cannot remove %s", bakdir); |
787 | if (pag3_errno && !existence_error (pag3_errno)((pag3_errno) == 2)) |
788 | error (0, pag3_errno, "cannot remove %s", bakpag); |
789 | if (db3_errno && !existence_error (db3_errno)((db3_errno) == 2)) |
790 | error (0, db3_errno, "cannot remove %s", bakdb); |
791 | } |
792 | |
793 | #endif /* !MY_NDBM */ |
794 | |
795 | static void |
796 | rename_rcsfile (temp, real) |
797 | char *temp; |
798 | char *real; |
799 | { |
800 | char *bak; |
801 | struct stat statbuf; |
802 | char *rcs; |
803 | |
804 | /* Set "x" bits if set in original. */ |
805 | rcs = xmalloc (strlen (real) + sizeof (RCSEXT",v") + 10); |
806 | (void) sprintf (rcs, "%s%s", real, RCSEXT",v"); |
807 | statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */ |
808 | if (CVS_STATstat (rcs, &statbuf) < 0 |
809 | && !existence_error (errno)(((*__errno())) == 2)) |
810 | error (0, errno(*__errno()), "cannot stat %s", rcs); |
811 | free (rcs); |
812 | |
813 | if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0) |
814 | error (0, errno(*__errno()), "warning: cannot chmod %s", temp); |
815 | bak = xmalloc (strlen (real) + sizeof (BAKPREFIX".#") + 10); |
816 | (void) sprintf (bak, "%s%s", BAKPREFIX".#", real); |
817 | |
818 | /* rm .#loginfo */ |
819 | if (unlink_file (bak) < 0 |
820 | && !existence_error (errno)(((*__errno())) == 2)) |
821 | error (0, errno(*__errno()), "cannot remove %s", bak); |
822 | |
823 | /* mv loginfo .#loginfo */ |
824 | if (CVS_RENAMErename (real, bak) < 0 |
825 | && !existence_error (errno)(((*__errno())) == 2)) |
826 | error (0, errno(*__errno()), "cannot rename %s to %s", real, bak); |
827 | |
828 | /* mv "temp" loginfo */ |
829 | if (CVS_RENAMErename (temp, real) < 0 |
830 | && !existence_error (errno)(((*__errno())) == 2)) |
831 | error (0, errno(*__errno()), "cannot rename %s to %s", temp, real); |
832 | |
833 | free (bak); |
834 | } |
835 | |
836 | const char *const init_usage[] = { |
837 | "Usage: %s %s\n", |
838 | "(Specify the --help global option for a list of other help options)\n", |
839 | NULL((void*)0) |
840 | }; |
841 | |
842 | int |
843 | init (argc, argv) |
844 | int argc; |
845 | char **argv; |
846 | { |
847 | /* Name of CVSROOT directory. */ |
848 | char *adm; |
849 | /* Name of this administrative file. */ |
850 | char *info; |
851 | /* Name of ,v file for this administrative file. */ |
852 | char *info_v; |
853 | /* Exit status. */ |
854 | int err; |
855 | |
856 | const struct admin_file *fileptr; |
857 | |
858 | umask (cvsumask); |
859 | |
860 | if (argc == -1 || argc > 1) |
861 | usage (init_usage); |
862 | |
863 | #ifdef CLIENT_SUPPORT1 |
864 | if (current_parsed_root->isremote) |
865 | { |
866 | start_server (); |
867 | |
868 | ign_setup (); |
869 | send_init_command (); |
870 | return get_responses_and_close (); |
871 | } |
872 | #endif /* CLIENT_SUPPORT */ |
873 | |
874 | /* Note: we do *not* create parent directories as needed like the |
875 | old cvsinit.sh script did. Few utilities do that, and a |
876 | non-existent parent directory is as likely to be a typo as something |
877 | which needs to be created. */ |
878 | mkdir_if_needed (current_parsed_root->directory); |
879 | |
880 | adm = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM"CVSROOT") + 2); |
881 | sprintf (adm, "%s/%s", current_parsed_root->directory, CVSROOTADM"CVSROOT"); |
882 | mkdir_if_needed (adm); |
883 | |
884 | /* This is needed because we pass "fileptr->filename" not "info" |
885 | to add_rcs_file below. I think this would be easy to change, |
886 | thus nuking the need for CVS_CHDIR here, but I haven't looked |
887 | closely (e.g. see wrappers calls within add_rcs_file). */ |
888 | if ( CVS_CHDIRchdir (adm) < 0) |
889 | error (1, errno(*__errno()), "cannot change to directory %s", adm); |
890 | |
891 | /* Make Emptydir so it's there if we need it */ |
892 | mkdir_if_needed (CVSNULLREPOS"Emptydir"); |
893 | |
894 | /* 80 is long enough for all the administrative file names, plus |
895 | "/" and so on. */ |
896 | info = xmalloc (strlen (adm) + 80); |
897 | info_v = xmalloc (strlen (adm) + 80); |
898 | for (fileptr = filelist; fileptr && fileptr->filename; ++fileptr) |
899 | { |
900 | if (fileptr->contents == NULL((void*)0)) |
901 | continue; |
902 | strcpy (info, adm); |
903 | strcat (info, "/"); |
904 | strcat (info, fileptr->filename); |
905 | strcpy (info_v, info); |
906 | strcat (info_v, RCSEXT",v"); |
907 | if (isfile (info_v)) |
908 | /* We will check out this file in the mkmodules step. |
909 | Nothing else is required. */ |
910 | ; |
911 | else |
912 | { |
913 | int retcode; |
914 | |
915 | if (!isfile (info)) |
916 | { |
917 | FILE *fp; |
918 | const char * const *p; |
919 | |
920 | fp = open_file (info, "w"); |
921 | for (p = fileptr->contents; *p != NULL((void*)0); ++p) |
922 | if (fputs (*p, fp) < 0) |
923 | error (1, errno(*__errno()), "cannot write %s", info); |
924 | if (fclose (fp) < 0) |
925 | error (1, errno(*__errno()), "cannot close %s", info); |
926 | } |
927 | /* The message used to say " of " and fileptr->filename after |
928 | "initial checkin" but I fail to see the point as we know what |
929 | file it is from the name. */ |
930 | retcode = add_rcs_file ("initial checkin", info_v, |
931 | fileptr->filename, "1.1", NULL((void*)0), |
932 | |
933 | /* No vendor branch. */ |
934 | NULL((void*)0), NULL((void*)0), 0, NULL((void*)0), |
935 | |
936 | NULL((void*)0), 0, NULL((void*)0)); |
937 | if (retcode != 0) |
938 | /* add_rcs_file already printed an error message. */ |
939 | err = 1; |
Value stored to 'err' is never read | |
940 | } |
941 | } |
942 | |
943 | /* Turn on history logging by default. The user can remove the file |
944 | to disable it. */ |
945 | strcpy (info, adm); |
946 | strcat (info, "/"); |
947 | strcat (info, CVSROOTADM_HISTORY"history"); |
948 | if (!isfile (info)) |
949 | { |
950 | FILE *fp; |
951 | |
952 | fp = open_file (info, "w"); |
953 | if (fclose (fp) < 0) |
954 | error (1, errno(*__errno()), "cannot close %s", info); |
955 | |
956 | /* Make the new history file world-writeable, since every CVS |
957 | user will need to be able to write to it. We use chmod() |
958 | because xchmod() is too shy. */ |
959 | chmod (info, 0666); |
960 | } |
961 | |
962 | /* Make an empty val-tags file to prevent problems creating it later. */ |
963 | strcpy (info, adm); |
964 | strcat (info, "/"); |
965 | strcat (info, CVSROOTADM_VALTAGS"val-tags"); |
966 | if (!isfile (info)) |
967 | { |
968 | FILE *fp; |
969 | |
970 | fp = open_file (info, "w"); |
971 | if (fclose (fp) < 0) |
972 | error (1, errno(*__errno()), "cannot close %s", info); |
973 | |
974 | /* Make the new val-tags file world-writeable, since every CVS |
975 | user will need to be able to write to it. We use chmod() |
976 | because xchmod() is too shy. */ |
977 | chmod (info, 0666); |
978 | } |
979 | |
980 | free (info); |
981 | free (info_v); |
982 | |
983 | mkmodules (adm); |
984 | |
985 | free (adm); |
986 | return 0; |
987 | } |