PageRenderTime 63ms CodeModel.GetById 37ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/cvs/src/parseinfo.c

https://bitbucket.org/freebsd/freebsd-head/
C | 511 lines | 354 code | 42 blank | 115 comment | 168 complexity | 1f319c5475ca26303ae7393b82eee7ad MD5 | raw file
  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
 21extern 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 */
 31int
 32Parse_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.  */
237int
238parse_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, "\
392warning: 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    }
495set_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}