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