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