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