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