]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/cvs/src/parseinfo.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / cvs / src / parseinfo.c
1 /*
2  * Copyright (C) 1986-2008 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 source distribution.
12  *
13  * $FreeBSD$
14  */
15
16 #include "cvs.h"
17 #include "getline.h"
18 #include <assert.h>
19 #include "history.h"
20
21 extern char *logHistory;
22
23 /*
24  * Parse the INFOFILE file for the specified REPOSITORY.  Invoke CALLPROC for
25  * the first line in the file that matches the REPOSITORY, or if ALL != 0, any
26  * lines matching "ALL", or if no lines match, the last line matching
27  * "DEFAULT".
28  *
29  * Return 0 for success, -1 if there was not an INFOFILE, and >0 for failure.
30  */
31 int
32 Parse_Info (infofile, repository, callproc, all)
33     const char *infofile;
34     const char *repository;
35     CALLPROC callproc;
36     int all;
37 {
38     int err = 0;
39     FILE *fp_info;
40     char *infopath;
41     char *line = NULL;
42     size_t line_allocated = 0;
43     char *default_value = NULL;
44     int default_line = 0;
45     char *expanded_value;
46     int callback_done, line_number;
47     char *cp, *exp, *value;
48     const char *srepos;
49     const char *regex_err;
50
51     assert (repository);
52
53     if (current_parsed_root == NULL)
54     {
55         /* XXX - should be error maybe? */
56         error (0, 0, "CVSROOT variable not set");
57         return (1);
58     }
59
60     /* find the info file and open it */
61     infopath = xmalloc (strlen (current_parsed_root->directory)
62                         + strlen (infofile)
63                         + sizeof (CVSROOTADM)
64                         + 3);
65     (void) sprintf (infopath, "%s/%s/%s", current_parsed_root->directory,
66                     CVSROOTADM, infofile);
67     fp_info = CVS_FOPEN (infopath, "r");
68     if (fp_info == NULL)
69     {
70         /* If no file, don't do anything special.  */
71         if (!existence_error (errno))
72             error (0, errno, "cannot open %s", infopath);
73         free (infopath);
74         return 0;
75     }
76
77     /* strip off the CVSROOT if repository was absolute */
78     srepos = Short_Repository (repository);
79
80     if (trace)
81         (void) fprintf (stderr, "%s-> Parse_Info (%s, %s, %s)\n",
82 #ifdef SERVER_SUPPORT
83                         server_active ? "S" : " ",
84 #else
85                         "",
86 #endif
87                         infopath, srepos, all ? "ALL" : "not ALL");
88
89     /* search the info file for lines that match */
90     callback_done = line_number = 0;
91     while (getline (&line, &line_allocated, fp_info) >= 0)
92     {
93         line_number++;
94
95         /* skip lines starting with # */
96         if (line[0] == '#')
97             continue;
98
99         /* skip whitespace at beginning of line */
100         for (cp = line; *cp && isspace ((unsigned char) *cp); cp++)
101             ;
102
103         /* if *cp is null, the whole line was blank */
104         if (*cp == '\0')
105             continue;
106
107         /* the regular expression is everything up to the first space */
108         for (exp = cp; *cp && !isspace ((unsigned char) *cp); cp++)
109             ;
110         if (*cp != '\0')
111             *cp++ = '\0';
112
113         /* skip whitespace up to the start of the matching value */
114         while (*cp && isspace ((unsigned char) *cp))
115             cp++;
116
117         /* no value to match with the regular expression is an error */
118         if (*cp == '\0')
119         {
120             error (0, 0, "syntax error at line %d file %s; ignored",
121                    line_number, infofile);
122             continue;
123         }
124         value = cp;
125
126         /* strip the newline off the end of the value */
127         if ((cp = strrchr (value, '\n')) != NULL)
128             *cp = '\0';
129
130         /*
131          * At this point, exp points to the regular expression, and value
132          * points to the value to call the callback routine with.  Evaluate
133          * the regular expression against srepos and callback with the value
134          * if it matches.
135          */
136
137         /* save the default value so we have it later if we need it */
138         if (strcmp (exp, "DEFAULT") == 0)
139         {
140             if (default_value != NULL)
141             {
142                 error (0, 0, "Multiple `DEFAULT' lines (%d and %d) in %s file",
143                        default_line, line_number, infofile);
144                 free (default_value);
145             }
146             default_value = xstrdup(value);
147             default_line = line_number;
148             continue;
149         }
150
151         /*
152          * For a regular expression of "ALL", do the callback always We may
153          * execute lots of ALL callbacks in addition to *one* regular matching
154          * callback or default
155          */
156         if (strcmp (exp, "ALL") == 0)
157         {
158             if (!all)
159                 error(0, 0, "Keyword `ALL' is ignored at line %d in %s file",
160                       line_number, infofile);
161             else if ((expanded_value = expand_path (value, infofile,
162                                                     line_number)) != NULL)
163             {
164                 err += callproc (repository, expanded_value);
165                 free (expanded_value);
166             }
167             else
168                 err++;
169             continue;
170         }
171
172         if (callback_done)
173             /* only first matching, plus "ALL"'s */
174             continue;
175
176         /* see if the repository matched this regular expression */
177         if ((regex_err = re_comp (exp)) != NULL)
178         {
179             error (0, 0, "bad regular expression at line %d file %s: %s",
180                    line_number, infofile, regex_err);
181             continue;
182         }
183         if (re_exec (srepos) == 0)
184             continue;                           /* no match */
185
186         /* it did, so do the callback and note that we did one */
187         if ((expanded_value = expand_path (value, infofile, line_number)) != NULL)
188         {
189             err += callproc (repository, expanded_value);
190             free (expanded_value);
191         }
192         else
193             err++;
194         callback_done = 1;
195     }
196     if (ferror (fp_info))
197         error (0, errno, "cannot read %s", infopath);
198     if (fclose (fp_info) < 0)
199         error (0, errno, "cannot close %s", infopath);
200
201     /* if we fell through and didn't callback at all, do the default */
202     if (callback_done == 0 && default_value != NULL)
203     {
204         if ((expanded_value = expand_path (default_value, infofile, default_line)) != NULL)
205         {
206             err += callproc (repository, expanded_value);
207             free (expanded_value);
208         }
209         else
210             err++;
211     }
212
213     /* free up space if necessary */
214     if (default_value != NULL)
215         free (default_value);
216     free (infopath);
217     if (line != NULL)
218         free (line);
219
220     return (err);
221 }
222
223
224 /* Parse the CVS config file.  The syntax right now is a bit ad hoc
225    but tries to draw on the best or more common features of the other
226    *info files and various unix (or non-unix) config file syntaxes.
227    Lines starting with # are comments.  Settings are lines of the form
228    KEYWORD=VALUE.  There is currently no way to have a multi-line
229    VALUE (would be nice if there was, probably).
230
231    CVSROOT is the $CVSROOT directory
232    (current_parsed_root->directory might not be set yet, so this
233    function takes the cvsroot as a function argument).
234
235    Returns 0 for success, negative value for failure.  Call
236    error(0, ...) on errors in addition to the return value.  */
237 int
238 parse_config (cvsroot)
239     char *cvsroot;
240 {
241     char *infopath;
242     FILE *fp_info;
243     char *line = NULL;
244     size_t line_allocated = 0;
245     size_t len;
246     char *p;
247     /* FIXME-reentrancy: If we do a multi-threaded server, this would need
248        to go to the per-connection data structures.  */
249     static int parsed = 0;
250     int ignore_unknown_config_keys = 0;
251
252     /* Authentication code and serve_root might both want to call us.
253        Let this happen smoothly.  */
254     if (parsed)
255         return 0;
256     parsed = 1;
257
258     infopath = xmalloc (strlen (cvsroot)
259                         + sizeof (CVSROOTADM_CONFIG)
260                         + sizeof (CVSROOTADM)
261                         + 10);
262     if (infopath == NULL)
263     {
264         error (0, 0, "out of memory; cannot allocate infopath");
265         goto error_return;
266     }
267
268     strcpy (infopath, cvsroot);
269     strcat (infopath, "/");
270     strcat (infopath, CVSROOTADM);
271     strcat (infopath, "/");
272     strcat (infopath, CVSROOTADM_CONFIG);
273
274     fp_info = CVS_FOPEN (infopath, "r");
275     if (fp_info == NULL)
276     {
277         /* If no file, don't do anything special.  */
278         if (!existence_error (errno))
279         {
280             /* Just a warning message; doesn't affect return
281                value, currently at least.  */
282             error (0, errno, "cannot open %s", infopath);
283         }
284         goto set_defaults_and_return;
285     }
286
287     while (getline (&line, &line_allocated, fp_info) >= 0)
288     {
289         /* Skip comments.  */
290         if (line[0] == '#')
291             continue;
292
293         /* At least for the moment we don't skip whitespace at the start
294            of the line.  Too picky?  Maybe.  But being insufficiently
295            picky leads to all sorts of confusion, and it is a lot easier
296            to start out picky and relax it than the other way around.
297
298            Is there any kind of written standard for the syntax of this
299            sort of config file?  Anywhere in POSIX for example (I guess
300            makefiles are sort of close)?  Red Hat Linux has a bunch of
301            these too (with some GUI tools which edit them)...
302
303            Along the same lines, we might want a table of keywords,
304            with various types (boolean, string, &c), as a mechanism
305            for making sure the syntax is consistent.  Any good examples
306            to follow there (Apache?)?  */
307
308         /* Strip the trailing newline.  There will be one unless we
309            read a partial line without a newline, and then got end of
310            file (or error?).  */
311
312         len = strlen (line) - 1;
313         if (line[len] == '\n')
314             line[len] = '\0';
315
316         /* Skip blank lines.  */
317         if (line[0] == '\0')
318             continue;
319
320         /* The first '=' separates keyword from value.  */
321         p = strchr (line, '=');
322         if (p == NULL)
323         {
324             /* Probably should be printing line number.  */
325             error (0, 0, "syntax error in %s: line '%s' is missing '='",
326                    infopath, line);
327             goto error_return;
328         }
329
330         *p++ = '\0';
331
332         if (strcmp (line, "RCSBIN") == 0)
333         {
334             /* This option used to specify the directory for RCS
335                executables.  But since we don't run them any more,
336                this is a noop.  Silently ignore it so that a
337                repository can work with either new or old CVS.  */
338             ;
339         }
340         else if (strcmp (line, "SystemAuth") == 0)
341         {
342             if (strcmp (p, "no") == 0)
343 #ifdef AUTH_SERVER_SUPPORT
344                 system_auth = 0;
345 #else
346                 /* Still parse the syntax but ignore the
347                    option.  That way the same config file can
348                    be used for local and server.  */
349                 ;
350 #endif
351             else if (strcmp (p, "yes") == 0)
352 #ifdef AUTH_SERVER_SUPPORT
353                 system_auth = 1;
354 #else
355                 ;
356 #endif
357             else
358             {
359                 error (0, 0, "unrecognized value '%s' for SystemAuth", p);
360                 goto error_return;
361             }
362         }
363         else if (strcmp (line, "tag") == 0) {
364                 RCS_setlocalid(p);
365         }
366         else if (strcmp (line, "umask") == 0) {
367             cvsumask = (mode_t)(strtol(p, NULL, 8) & 0777);
368         }
369         else if (strcmp (line, "dlimit") == 0) {
370 #ifdef BSD
371 #include <sys/resource.h>
372             struct rlimit rl;
373
374             if (getrlimit(RLIMIT_DATA, &rl) != -1) {
375                 rl.rlim_cur = atoi(p);
376                 rl.rlim_cur *= 1024;
377
378                 (void) setrlimit(RLIMIT_DATA, &rl);
379             }
380 #endif /* BSD */
381         }
382         else if (strcmp (line, "PreservePermissions") == 0)
383         {
384             if (strcmp (p, "no") == 0)
385                 preserve_perms = 0;
386             else if (strcmp (p, "yes") == 0)
387             {
388 #ifdef PRESERVE_PERMISSIONS_SUPPORT
389                 preserve_perms = 1;
390 #else
391                 error (0, 0, "\
392 warning: this CVS does not support PreservePermissions");
393 #endif
394             }
395             else
396             {
397                 error (0, 0, "unrecognized value '%s' for PreservePermissions",
398                        p);
399                 goto error_return;
400             }
401         }
402         else if (strcmp (line, "TopLevelAdmin") == 0)
403         {
404             if (strcmp (p, "no") == 0)
405                 top_level_admin = 0;
406             else if (strcmp (p, "yes") == 0)
407                 top_level_admin = 1;
408             else
409             {
410                 error (0, 0, "unrecognized value '%s' for TopLevelAdmin", p);
411                 goto error_return;
412             }
413         }
414         else if (strcmp (line, "LockDir") == 0)
415         {
416             if (lock_dir != NULL)
417                 free (lock_dir);
418             lock_dir = xstrdup (p);
419             /* Could try some validity checking, like whether we can
420                opendir it or something, but I don't see any particular
421                reason to do that now rather than waiting until lock.c.  */
422         }
423         else if (strcmp (line, "LogHistory") == 0)
424         {
425             if (strcmp (p, "all") != 0)
426             {
427                 if (logHistory) free (logHistory);
428                 logHistory = xstrdup (p);
429             }
430         }
431         else if (strcmp (line, "RereadLogAfterVerify") == 0)
432         {
433             if (strcmp (p, "no") == 0 || strcmp (p, "never") == 0)
434               RereadLogAfterVerify = LOGMSG_REREAD_NEVER;
435             else if (strcmp (p, "yes") == 0 || strcmp (p, "always") == 0)
436               RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
437             else if (strcmp (p, "stat") == 0)
438               RereadLogAfterVerify = LOGMSG_REREAD_STAT;
439         }
440         else if (strcmp(line, "LocalKeyword") == 0)
441         {
442             /* Recognize cvs-1.12-style keyword control rather than erroring out. */
443             RCS_setlocalid(p);
444         }
445         else if (strcmp(line, "KeywordExpand") == 0)
446         {
447             /* Recognize cvs-1.12-style keyword control rather than erroring out. */
448             RCS_setincexc(p);
449         }
450         else if (strcmp (line, "IgnoreUnknownConfigKeys") == 0)
451         {
452             if (strcmp (p, "no") == 0 || strcmp (p, "false") == 0
453                 || strcmp (p, "off") == 0 || strcmp (p, "0") == 0)
454                 ignore_unknown_config_keys = 0;
455             else if (strcmp (p, "yes") == 0 || strcmp (p, "true") == 0
456                      || strcmp (p, "on") == 0 || strcmp (p, "1") == 0)
457                 ignore_unknown_config_keys = 1;
458             else
459             {
460                 error (0, 0, "%s: unrecognized value '%s' for '%s'",
461                        infopath, p, line);
462                 goto error_return;
463             }
464         }
465         else if (ignore_unknown_config_keys)
466             ;
467         else
468         {
469             /* We may be dealing with a keyword which was added in a
470                subsequent version of CVS.  In that case it is a good idea
471                to complain, as (1) the keyword might enable a behavior like
472                alternate locking behavior, in which it is dangerous and hard
473                to detect if some CVS's have it one way and others have it
474                the other way, (2) in general, having us not do what the user
475                had in mind when they put in the keyword violates the
476                principle of least surprise.  Note that one corollary is
477                adding new keywords to your CVSROOT/config file is not
478                particularly recommended unless you are planning on using
479                the new features.  */
480             error (0, 0, "%s: unrecognized keyword '%s'",
481                    infopath, line);
482             goto error_return;
483         }
484     }
485     if (ferror (fp_info))
486     {
487         error (0, errno, "cannot read %s", infopath);
488         goto error_return;
489     }
490     if (fclose (fp_info) < 0)
491     {
492         error (0, errno, "cannot close %s", infopath);
493         goto error_return;
494     }
495 set_defaults_and_return:
496     if (!logHistory)
497         logHistory = xstrdup (ALL_HISTORY_REC_TYPES);
498     free (infopath);
499     if (line != NULL)
500         free (line);
501     return 0;
502
503  error_return:
504     if (!logHistory)
505         logHistory = xstrdup (ALL_HISTORY_REC_TYPES);
506     if (infopath != NULL)
507         free (infopath);
508     if (line != NULL)
509         free (line);
510     return -1;
511 }