11e72d8d2Sderaadt /*
21e72d8d2Sderaadt * Copyright (c) 1992, Brian Berliner and Jeff Polk
31e72d8d2Sderaadt * Copyright (c) 1989-1992, Brian Berliner
41e72d8d2Sderaadt *
51e72d8d2Sderaadt * You may distribute under the terms of the GNU General Public License as
62286d8edStholo * specified in the README file that comes with the CVS source distribution.
71e72d8d2Sderaadt */
81e72d8d2Sderaadt
91e72d8d2Sderaadt #include "cvs.h"
10780d15dfStholo #include "getline.h"
112286d8edStholo #include <assert.h>
121e72d8d2Sderaadt
13bde78045Stholo extern char *logHistory;
14bde78045Stholo
151e72d8d2Sderaadt /*
161e72d8d2Sderaadt * Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for
171e72d8d2Sderaadt * the first line in the file that matches the REPOSITORY, or if ALL != 0, any lines
181e72d8d2Sderaadt * matching "ALL", or if no lines match, the last line matching "DEFAULT".
191e72d8d2Sderaadt *
201e72d8d2Sderaadt * Return 0 for success, -1 if there was not an INFOFILE, and >0 for failure.
211e72d8d2Sderaadt */
221e72d8d2Sderaadt int
Parse_Info(infofile,repository,callproc,all)231e72d8d2Sderaadt Parse_Info (infofile, repository, callproc, all)
241e72d8d2Sderaadt char *infofile;
251e72d8d2Sderaadt char *repository;
2613571821Stholo CALLPROC callproc;
271e72d8d2Sderaadt int all;
281e72d8d2Sderaadt {
291e72d8d2Sderaadt int err = 0;
301e72d8d2Sderaadt FILE *fp_info;
31780d15dfStholo char *infopath;
32780d15dfStholo char *line = NULL;
33780d15dfStholo size_t line_allocated = 0;
341e72d8d2Sderaadt char *default_value = NULL;
3513571821Stholo char *expanded_value= NULL;
361e72d8d2Sderaadt int callback_done, line_number;
37bde78045Stholo char *cp, *exp, *value, *srepos, bad;
381e72d8d2Sderaadt const char *regex_err;
391e72d8d2Sderaadt
4079822b2aStholo if (current_parsed_root == NULL)
411e72d8d2Sderaadt {
421e72d8d2Sderaadt /* XXX - should be error maybe? */
431e72d8d2Sderaadt error (0, 0, "CVSROOT variable not set");
441e72d8d2Sderaadt return (1);
451e72d8d2Sderaadt }
461e72d8d2Sderaadt
471e72d8d2Sderaadt /* find the info file and open it */
4879822b2aStholo infopath = xmalloc (strlen (current_parsed_root->directory)
49780d15dfStholo + strlen (infofile)
50780d15dfStholo + sizeof (CVSROOTADM)
5179822b2aStholo + 3);
5279822b2aStholo (void) sprintf (infopath, "%s/%s/%s", current_parsed_root->directory,
531e72d8d2Sderaadt CVSROOTADM, infofile);
54780d15dfStholo fp_info = CVS_FOPEN (infopath, "r");
55780d15dfStholo if (fp_info == NULL)
56780d15dfStholo {
57780d15dfStholo /* If no file, don't do anything special. */
58780d15dfStholo if (!existence_error (errno))
59780d15dfStholo error (0, errno, "cannot open %s", infopath);
602770ece5Stholo free (infopath);
61780d15dfStholo return 0;
62780d15dfStholo }
631e72d8d2Sderaadt
641e72d8d2Sderaadt /* strip off the CVSROOT if repository was absolute */
651e72d8d2Sderaadt srepos = Short_Repository (repository);
661e72d8d2Sderaadt
671e72d8d2Sderaadt if (trace)
681e72d8d2Sderaadt (void) fprintf (stderr, " -> ParseInfo(%s, %s, %s)\n",
691e72d8d2Sderaadt infopath, srepos, all ? "ALL" : "not ALL");
701e72d8d2Sderaadt
711e72d8d2Sderaadt /* search the info file for lines that match */
721e72d8d2Sderaadt callback_done = line_number = 0;
73*f9bbbf45Sfgsch while (get_line (&line, &line_allocated, fp_info) >= 0)
741e72d8d2Sderaadt {
751e72d8d2Sderaadt line_number++;
761e72d8d2Sderaadt
771e72d8d2Sderaadt /* skip lines starting with # */
781e72d8d2Sderaadt if (line[0] == '#')
791e72d8d2Sderaadt continue;
801e72d8d2Sderaadt
811e72d8d2Sderaadt /* skip whitespace at beginning of line */
829fe7c2c3Stholo for (cp = line; *cp && isspace ((unsigned char) *cp); cp++)
831e72d8d2Sderaadt ;
841e72d8d2Sderaadt
851e72d8d2Sderaadt /* if *cp is null, the whole line was blank */
861e72d8d2Sderaadt if (*cp == '\0')
871e72d8d2Sderaadt continue;
881e72d8d2Sderaadt
891e72d8d2Sderaadt /* the regular expression is everything up to the first space */
909fe7c2c3Stholo for (exp = cp; *cp && !isspace ((unsigned char) *cp); cp++)
911e72d8d2Sderaadt ;
921e72d8d2Sderaadt if (*cp != '\0')
931e72d8d2Sderaadt *cp++ = '\0';
941e72d8d2Sderaadt
951e72d8d2Sderaadt /* skip whitespace up to the start of the matching value */
969fe7c2c3Stholo while (*cp && isspace ((unsigned char) *cp))
971e72d8d2Sderaadt cp++;
981e72d8d2Sderaadt
991e72d8d2Sderaadt /* no value to match with the regular expression is an error */
1001e72d8d2Sderaadt if (*cp == '\0')
1011e72d8d2Sderaadt {
1021e72d8d2Sderaadt error (0, 0, "syntax error at line %d file %s; ignored",
1031e72d8d2Sderaadt line_number, infofile);
1041e72d8d2Sderaadt continue;
1051e72d8d2Sderaadt }
1061e72d8d2Sderaadt value = cp;
1071e72d8d2Sderaadt
1081e72d8d2Sderaadt /* strip the newline off the end of the value */
1091e72d8d2Sderaadt if ((cp = strrchr (value, '\n')) != NULL)
1101e72d8d2Sderaadt *cp = '\0';
1111e72d8d2Sderaadt
1122770ece5Stholo if (expanded_value != NULL)
1132770ece5Stholo free (expanded_value);
114c26070a5Stholo expanded_value = expand_path (value, infofile, line_number);
1151e72d8d2Sderaadt
1161e72d8d2Sderaadt /*
1171e72d8d2Sderaadt * At this point, exp points to the regular expression, and value
1181e72d8d2Sderaadt * points to the value to call the callback routine with. Evaluate
1191e72d8d2Sderaadt * the regular expression against srepos and callback with the value
1201e72d8d2Sderaadt * if it matches.
1211e72d8d2Sderaadt */
1221e72d8d2Sderaadt
1231e72d8d2Sderaadt /* save the default value so we have it later if we need it */
1241e72d8d2Sderaadt if (strcmp (exp, "DEFAULT") == 0)
1251e72d8d2Sderaadt {
1262770ece5Stholo /* Is it OK to silently ignore all but the last DEFAULT
1272770ece5Stholo expression? */
128bde78045Stholo if (default_value != NULL && default_value != &bad)
1292770ece5Stholo free (default_value);
130bde78045Stholo default_value = (expanded_value != NULL ?
131bde78045Stholo xstrdup (expanded_value) : &bad);
1321e72d8d2Sderaadt continue;
1331e72d8d2Sderaadt }
1341e72d8d2Sderaadt
1351e72d8d2Sderaadt /*
1361e72d8d2Sderaadt * For a regular expression of "ALL", do the callback always We may
1371e72d8d2Sderaadt * execute lots of ALL callbacks in addition to *one* regular matching
1381e72d8d2Sderaadt * callback or default
1391e72d8d2Sderaadt */
1401e72d8d2Sderaadt if (strcmp (exp, "ALL") == 0)
1411e72d8d2Sderaadt {
142bde78045Stholo if (!all)
1431e72d8d2Sderaadt error(0, 0, "Keyword `ALL' is ignored at line %d in %s file",
1441e72d8d2Sderaadt line_number, infofile);
145bde78045Stholo else if (expanded_value != NULL)
146bde78045Stholo err += callproc (repository, expanded_value);
147bde78045Stholo else
148bde78045Stholo err++;
1491e72d8d2Sderaadt continue;
1501e72d8d2Sderaadt }
1511e72d8d2Sderaadt
1521e72d8d2Sderaadt if (callback_done)
1531e72d8d2Sderaadt /* only first matching, plus "ALL"'s */
1541e72d8d2Sderaadt continue;
1551e72d8d2Sderaadt
1561e72d8d2Sderaadt /* see if the repository matched this regular expression */
1571e72d8d2Sderaadt if ((regex_err = re_comp (exp)) != NULL)
1581e72d8d2Sderaadt {
1591e72d8d2Sderaadt error (0, 0, "bad regular expression at line %d file %s: %s",
1601e72d8d2Sderaadt line_number, infofile, regex_err);
1611e72d8d2Sderaadt continue;
1621e72d8d2Sderaadt }
1631e72d8d2Sderaadt if (re_exec (srepos) == 0)
1641e72d8d2Sderaadt continue; /* no match */
1651e72d8d2Sderaadt
1661e72d8d2Sderaadt /* it did, so do the callback and note that we did one */
167bde78045Stholo if (expanded_value != NULL)
16813571821Stholo err += callproc (repository, expanded_value);
169bde78045Stholo else
170bde78045Stholo err++;
1711e72d8d2Sderaadt callback_done = 1;
1721e72d8d2Sderaadt }
173780d15dfStholo if (ferror (fp_info))
174780d15dfStholo error (0, errno, "cannot read %s", infopath);
175780d15dfStholo if (fclose (fp_info) < 0)
176780d15dfStholo error (0, errno, "cannot close %s", infopath);
1771e72d8d2Sderaadt
1781e72d8d2Sderaadt /* if we fell through and didn't callback at all, do the default */
1791e72d8d2Sderaadt if (callback_done == 0 && default_value != NULL)
180bde78045Stholo {
181bde78045Stholo if (default_value != &bad)
1821e72d8d2Sderaadt err += callproc (repository, default_value);
183bde78045Stholo else
184bde78045Stholo err++;
185bde78045Stholo }
1861e72d8d2Sderaadt
1871e72d8d2Sderaadt /* free up space if necessary */
188bde78045Stholo if (default_value != NULL && default_value != &bad)
1891e72d8d2Sderaadt free (default_value);
19013571821Stholo if (expanded_value != NULL)
19113571821Stholo free (expanded_value);
192780d15dfStholo free (infopath);
193780d15dfStholo if (line != NULL)
194780d15dfStholo free (line);
1951e72d8d2Sderaadt
1961e72d8d2Sderaadt return (err);
1971e72d8d2Sderaadt }
1982286d8edStholo
1992286d8edStholo
2002286d8edStholo /* Parse the CVS config file. The syntax right now is a bit ad hoc
2012286d8edStholo but tries to draw on the best or more common features of the other
2022286d8edStholo *info files and various unix (or non-unix) config file syntaxes.
2032286d8edStholo Lines starting with # are comments. Settings are lines of the form
2042286d8edStholo KEYWORD=VALUE. There is currently no way to have a multi-line
2052286d8edStholo VALUE (would be nice if there was, probably).
2062286d8edStholo
20779822b2aStholo CVSROOT is the $CVSROOT directory (current_parsed_root->directory might not be
2082286d8edStholo set yet).
2092286d8edStholo
2102286d8edStholo Returns 0 for success, negative value for failure. Call
2112286d8edStholo error(0, ...) on errors in addition to the return value. */
2122286d8edStholo int
parse_config(cvsroot)2132286d8edStholo parse_config (cvsroot)
2142286d8edStholo char *cvsroot;
2152286d8edStholo {
2162286d8edStholo char *infopath;
2172286d8edStholo FILE *fp_info;
2182286d8edStholo char *line = NULL;
2192286d8edStholo size_t line_allocated = 0;
2202286d8edStholo size_t len;
2212286d8edStholo char *p;
2222286d8edStholo /* FIXME-reentrancy: If we do a multi-threaded server, this would need
2232286d8edStholo to go to the per-connection data structures. */
2242286d8edStholo static int parsed = 0;
2252286d8edStholo
2262286d8edStholo /* Authentication code and serve_root might both want to call us.
2272286d8edStholo Let this happen smoothly. */
2282286d8edStholo if (parsed)
2292286d8edStholo return 0;
2302286d8edStholo parsed = 1;
2312286d8edStholo
2322286d8edStholo infopath = malloc (strlen (cvsroot)
2332286d8edStholo + sizeof (CVSROOTADM_CONFIG)
2342286d8edStholo + sizeof (CVSROOTADM)
2352286d8edStholo + 10);
2362286d8edStholo if (infopath == NULL)
2372286d8edStholo {
2382286d8edStholo error (0, 0, "out of memory; cannot allocate infopath");
2392286d8edStholo goto error_return;
2402286d8edStholo }
2412286d8edStholo
2422286d8edStholo strcpy (infopath, cvsroot);
2432286d8edStholo strcat (infopath, "/");
2442286d8edStholo strcat (infopath, CVSROOTADM);
2452286d8edStholo strcat (infopath, "/");
2462286d8edStholo strcat (infopath, CVSROOTADM_CONFIG);
2472286d8edStholo
2482286d8edStholo fp_info = CVS_FOPEN (infopath, "r");
2492286d8edStholo if (fp_info == NULL)
2502286d8edStholo {
2512286d8edStholo /* If no file, don't do anything special. */
2522286d8edStholo if (!existence_error (errno))
2532286d8edStholo {
2542286d8edStholo /* Just a warning message; doesn't affect return
2552286d8edStholo value, currently at least. */
2562286d8edStholo error (0, errno, "cannot open %s", infopath);
2572286d8edStholo }
2582286d8edStholo free (infopath);
2592286d8edStholo return 0;
2602286d8edStholo }
2612286d8edStholo
262*f9bbbf45Sfgsch while (get_line (&line, &line_allocated, fp_info) >= 0)
2632286d8edStholo {
2642286d8edStholo /* Skip comments. */
2652286d8edStholo if (line[0] == '#')
2662286d8edStholo continue;
2672286d8edStholo
2682286d8edStholo /* At least for the moment we don't skip whitespace at the start
2692286d8edStholo of the line. Too picky? Maybe. But being insufficiently
2702286d8edStholo picky leads to all sorts of confusion, and it is a lot easier
2712286d8edStholo to start out picky and relax it than the other way around.
2722286d8edStholo
2732286d8edStholo Is there any kind of written standard for the syntax of this
2742286d8edStholo sort of config file? Anywhere in POSIX for example (I guess
2752286d8edStholo makefiles are sort of close)? Red Hat Linux has a bunch of
2762286d8edStholo these too (with some GUI tools which edit them)...
2772286d8edStholo
2782286d8edStholo Along the same lines, we might want a table of keywords,
2792286d8edStholo with various types (boolean, string, &c), as a mechanism
2802286d8edStholo for making sure the syntax is consistent. Any good examples
2812286d8edStholo to follow there (Apache?)? */
2822286d8edStholo
2832286d8edStholo /* Strip the training newline. There will be one unless we
2842286d8edStholo read a partial line without a newline, and then got end of
2852286d8edStholo file (or error?). */
2862286d8edStholo
2872286d8edStholo len = strlen (line) - 1;
2882286d8edStholo if (line[len] == '\n')
2892286d8edStholo line[len] = '\0';
2902286d8edStholo
2912286d8edStholo /* Skip blank lines. */
2922286d8edStholo if (line[0] == '\0')
2932286d8edStholo continue;
2942286d8edStholo
2952286d8edStholo /* The first '=' separates keyword from value. */
2962286d8edStholo p = strchr (line, '=');
2972286d8edStholo if (p == NULL)
2982286d8edStholo {
2992286d8edStholo /* Probably should be printing line number. */
3002286d8edStholo error (0, 0, "syntax error in %s: line '%s' is missing '='",
3012286d8edStholo infopath, line);
3022286d8edStholo goto error_return;
3032286d8edStholo }
3042286d8edStholo
3052286d8edStholo *p++ = '\0';
3062286d8edStholo
3072286d8edStholo if (strcmp (line, "RCSBIN") == 0)
3082286d8edStholo {
3092286d8edStholo /* This option used to specify the directory for RCS
3102286d8edStholo executables. But since we don't run them any more,
3112286d8edStholo this is a noop. Silently ignore it so that a
3122286d8edStholo repository can work with either new or old CVS. */
3132286d8edStholo ;
3142286d8edStholo }
3152286d8edStholo else if (strcmp (line, "SystemAuth") == 0)
3162286d8edStholo {
3172286d8edStholo if (strcmp (p, "no") == 0)
3182286d8edStholo #ifdef AUTH_SERVER_SUPPORT
3192286d8edStholo system_auth = 0;
3202286d8edStholo #else
3212286d8edStholo /* Still parse the syntax but ignore the
3222286d8edStholo option. That way the same config file can
3232286d8edStholo be used for local and server. */
3242286d8edStholo ;
3252286d8edStholo #endif
3262286d8edStholo else if (strcmp (p, "yes") == 0)
3272286d8edStholo #ifdef AUTH_SERVER_SUPPORT
3282286d8edStholo system_auth = 1;
3292286d8edStholo #else
3302286d8edStholo ;
3312286d8edStholo #endif
3322286d8edStholo else
3332286d8edStholo {
3342286d8edStholo error (0, 0, "unrecognized value '%s' for SystemAuth", p);
3352286d8edStholo goto error_return;
3362286d8edStholo }
3372286d8edStholo }
3389c9c4491Stholo else if (strcmp (line, "tag") == 0) {
3399c9c4491Stholo RCS_citag = strdup(p);
3409c9c4491Stholo if (RCS_citag == NULL) {
3419c9c4491Stholo error (0, 0, "%s: no memory for local tag '%s'",
3429c9c4491Stholo infopath, p);
3439c9c4491Stholo goto error_return;
3449c9c4491Stholo }
3459c9c4491Stholo }
3469c9c4491Stholo else if (strcmp (line, "umask") == 0) {
3479c9c4491Stholo cvsumask = (mode_t)(strtol(p, NULL, 8) & 0777);
3489c9c4491Stholo }
3497f14e9e2Sdjm else if (strcmp (line, "DisableMdocdate") == 0) {
3507f14e9e2Sdjm if (strcmp (p, "no") == 0)
3517f14e9e2Sdjm disable_mdocdate = 0;
3527f14e9e2Sdjm else if (strcmp (p, "yes") == 0)
3537f14e9e2Sdjm disable_mdocdate = 1;
3547f14e9e2Sdjm else
3557f14e9e2Sdjm {
3567f14e9e2Sdjm error (0, 0, "unrecognized value '%s' for DisableMdocdate", p);
3577f14e9e2Sdjm goto error_return;
3587f14e9e2Sdjm }
3597f14e9e2Sdjm }
3609c9c4491Stholo else if (strcmp (line, "dlimit") == 0) {
3619c9c4491Stholo #ifdef BSD
3629c9c4491Stholo #include <sys/resource.h>
3639c9c4491Stholo struct rlimit rl;
3649c9c4491Stholo
3659c9c4491Stholo if (getrlimit(RLIMIT_DATA, &rl) != -1) {
3669c9c4491Stholo rl.rlim_cur = atoi(p);
3679c9c4491Stholo rl.rlim_cur *= 1024;
3689c9c4491Stholo
3699c9c4491Stholo (void) setrlimit(RLIMIT_DATA, &rl);
3709c9c4491Stholo }
3719c9c4491Stholo #endif /* BSD */
3729c9c4491Stholo }
373a6f8fe38Smillert else if (strcmp (line, "DisableXProg") == 0)
374a6f8fe38Smillert {
375a6f8fe38Smillert if (strcmp (p, "no") == 0)
376a6f8fe38Smillert #ifdef AUTH_SERVER_SUPPORT
377a6f8fe38Smillert disable_x_prog = 0;
378a6f8fe38Smillert #else
379a6f8fe38Smillert /* Still parse the syntax but ignore the
380a6f8fe38Smillert option. That way the same config file can
381a6f8fe38Smillert be used for local and server. */
382a6f8fe38Smillert ;
383a6f8fe38Smillert #endif
384a6f8fe38Smillert else if (strcmp (p, "yes") == 0)
385a6f8fe38Smillert #ifdef AUTH_SERVER_SUPPORT
386a6f8fe38Smillert disable_x_prog = 1;
387a6f8fe38Smillert #else
388a6f8fe38Smillert ;
389a6f8fe38Smillert #endif
390a6f8fe38Smillert else
391a6f8fe38Smillert {
392a6f8fe38Smillert error (0, 0, "unrecognized value '%s' for DisableXProg", p);
393a6f8fe38Smillert goto error_return;
394a6f8fe38Smillert }
395a6f8fe38Smillert }
39678c87a5cStholo else if (strcmp (line, "PreservePermissions") == 0)
39778c87a5cStholo {
39878c87a5cStholo if (strcmp (p, "no") == 0)
39978c87a5cStholo preserve_perms = 0;
40078c87a5cStholo else if (strcmp (p, "yes") == 0)
40178c87a5cStholo {
40278c87a5cStholo #ifdef PRESERVE_PERMISSIONS_SUPPORT
40378c87a5cStholo preserve_perms = 1;
40478c87a5cStholo #else
40578c87a5cStholo error (0, 0, "\
40678c87a5cStholo warning: this CVS does not support PreservePermissions");
40778c87a5cStholo #endif
40878c87a5cStholo }
40978c87a5cStholo else
41078c87a5cStholo {
41178c87a5cStholo error (0, 0, "unrecognized value '%s' for PreservePermissions",
41278c87a5cStholo p);
41378c87a5cStholo goto error_return;
41478c87a5cStholo }
41578c87a5cStholo }
416eea99451Stholo else if (strcmp (line, "TopLevelAdmin") == 0)
417eea99451Stholo {
418eea99451Stholo if (strcmp (p, "no") == 0)
419eea99451Stholo top_level_admin = 0;
420eea99451Stholo else if (strcmp (p, "yes") == 0)
421eea99451Stholo top_level_admin = 1;
422eea99451Stholo else
423eea99451Stholo {
424eea99451Stholo error (0, 0, "unrecognized value '%s' for TopLevelAdmin", p);
425eea99451Stholo goto error_return;
426eea99451Stholo }
427eea99451Stholo }
4289fe7c2c3Stholo else if (strcmp (line, "LockDir") == 0)
4299fe7c2c3Stholo {
4309fe7c2c3Stholo if (lock_dir != NULL)
4319fe7c2c3Stholo free (lock_dir);
4329fe7c2c3Stholo lock_dir = xstrdup (p);
4339fe7c2c3Stholo /* Could try some validity checking, like whether we can
4349fe7c2c3Stholo opendir it or something, but I don't see any particular
4359fe7c2c3Stholo reason to do that now rather than waiting until lock.c. */
4369fe7c2c3Stholo }
437bde78045Stholo else if (strcmp (line, "LogHistory") == 0)
438bde78045Stholo {
439bde78045Stholo if (strcmp (p, "all") != 0)
440bde78045Stholo {
441bde78045Stholo logHistory=malloc(strlen (p) + 1);
442bde78045Stholo strcpy (logHistory, p);
443bde78045Stholo }
444bde78045Stholo }
4452286d8edStholo else
4462286d8edStholo {
4472286d8edStholo /* We may be dealing with a keyword which was added in a
4482286d8edStholo subsequent version of CVS. In that case it is a good idea
4492286d8edStholo to complain, as (1) the keyword might enable a behavior like
4502286d8edStholo alternate locking behavior, in which it is dangerous and hard
4512286d8edStholo to detect if some CVS's have it one way and others have it
4522286d8edStholo the other way, (2) in general, having us not do what the user
4532286d8edStholo had in mind when they put in the keyword violates the
4542286d8edStholo principle of least surprise. Note that one corollary is
4552286d8edStholo adding new keywords to your CVSROOT/config file is not
4562286d8edStholo particularly recommended unless you are planning on using
4572286d8edStholo the new features. */
4582286d8edStholo error (0, 0, "%s: unrecognized keyword '%s'",
4592286d8edStholo infopath, line);
4602286d8edStholo goto error_return;
4612286d8edStholo }
4622286d8edStholo }
4632286d8edStholo if (ferror (fp_info))
4642286d8edStholo {
4652286d8edStholo error (0, errno, "cannot read %s", infopath);
4662286d8edStholo goto error_return;
4672286d8edStholo }
4682286d8edStholo if (fclose (fp_info) < 0)
4692286d8edStholo {
4702286d8edStholo error (0, errno, "cannot close %s", infopath);
4712286d8edStholo goto error_return;
4722286d8edStholo }
4732286d8edStholo free (infopath);
4742286d8edStholo if (line != NULL)
4752286d8edStholo free (line);
4762286d8edStholo return 0;
4772286d8edStholo
4782286d8edStholo error_return:
4792286d8edStholo if (infopath != NULL)
4802286d8edStholo free (infopath);
4812286d8edStholo if (line != NULL)
4822286d8edStholo free (line);
4832286d8edStholo return -1;
4842286d8edStholo }
485