L4Re/departure

libext2fs/lib/libsupport/profile.c

617:2733e5770ee9
9 months ago Paul Boddie Made the run command wait for completion, introducing the spawn command to run programs in the background. Introduced conveniences for waiting for the last job to be initiated and for piping from the last job, also subscribing to signals from pipe-supplying jobs so that they may be transparently removed from the job list upon completion. Augmented the job listing with the "+" notation familiar from Unix. Prevented new jobs from being started when no job slots are available.
     1 /*     2  * profile.c -- A simple configuration file parsing "library in a file"     3  *     4  * The profile library was originally written by Theodore Ts'o in 1995     5  * for use in the MIT Kerberos v5 library.  It has been     6  * modified/enhanced/bug-fixed over time by other members of the MIT     7  * Kerberos team.  This version was originally taken from the Kerberos     8  * v5 distribution, version 1.4.2, and radically simplified for use in     9  * e2fsprogs.  (Support for locking for multi-threaded operations,    10  * being able to modify and update the configuration file    11  * programmatically, and Mac/Windows portability have been removed.    12  * It has been folded into a single C source file to make it easier to    13  * fold into an application program.)    14  *    15  * Copyright (C) 2005, 2006 by Theodore Ts'o.    16  *    17  * %Begin-Header%    18  * This file may be redistributed under the terms of the GNU Public    19  * License.    20  * %End-Header%    21  *    22  * Copyright (C) 1985-2005 by the Massachusetts Institute of Technology.    23  *    24  * All rights reserved.    25  *    26  * Export of this software from the United States of America may require    27  * a specific license from the United States Government.  It is the    28  * responsibility of any person or organization contemplating export to    29  * obtain such a license before exporting.    30  *    31  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and    32  * distribute this software and its documentation for any purpose and    33  * without fee is hereby granted, provided that the above copyright    34  * notice appear in all copies and that both that copyright notice and    35  * this permission notice appear in supporting documentation, and that    36  * the name of M.I.T. not be used in advertising or publicity pertaining    37  * to distribution of the software without specific, written prior    38  * permission.  Furthermore if you modify this software you must label    39  * your software as modified software and not distribute it in such a    40  * fashion that it might be confused with the original MIT software.    41  * M.I.T. makes no representations about the suitability of this software    42  * for any purpose.  It is provided "as is" without express or implied    43  * warranty.    44  *    45  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR    46  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED    47  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.    48  *    49  */    50     51 #include "config.h"    52 #ifdef HAVE_UNISTD_H    53 #include <unistd.h>    54 #endif    55 #include <stdio.h>    56 #ifdef HAVE_STDLIB_H    57 #include <stdlib.h>    58 #endif    59 #include <time.h>    60 #include <string.h>    61 #include <strings.h>    62 #include <errno.h>    63 #include <ctype.h>    64 #include <limits.h>    65 #include <stddef.h>    66 #include <sys/types.h>    67 #include <sys/stat.h>    68 #include <dirent.h>    69 #ifdef HAVE_PWD_H    70 #include <pwd.h>    71 #endif    72     73 #include <et/com_err.h>    74 #include "profile.h"    75 #include "prof_err.h"    76     77 #undef STAT_ONCE_PER_SECOND    78 #undef HAVE_STAT    79     80 /*    81  * prof_int.h    82  */    83     84 typedef long prf_magic_t;    85     86 /*    87  * This is the structure which stores the profile information for a    88  * particular configuration file.    89  */    90 struct _prf_file_t {    91 	prf_magic_t	magic;    92 	char		*filespec;    93 #ifdef STAT_ONCE_PER_SECOND    94 	time_t		last_stat;    95 #endif    96 	time_t		timestamp; /* time tree was last updated from file */    97 	int		flags;	/* r/w, dirty */    98 	int		upd_serial; /* incremented when data changes */    99 	struct profile_node *root;   100 	struct _prf_file_t *next;   101 };   102    103 typedef struct _prf_file_t *prf_file_t;   104    105 /*   106  * The profile flags   107  */   108 #define PROFILE_FILE_RW		0x0001   109 #define PROFILE_FILE_DIRTY	0x0002   110 #define PROFILE_FILE_NO_RELOAD	0x0004   111    112 /*   113  * This structure defines the high-level, user visible profile_t   114  * object, which is used as a handle by users who need to query some   115  * configuration file(s)   116  */   117 struct _profile_t {   118 	prf_magic_t	magic;   119 	prf_file_t	first_file;   120 };   121    122 /*   123  * Used by the profile iterator in prof_get.c   124  */   125 #define PROFILE_ITER_LIST_SECTION	0x0001   126 #define PROFILE_ITER_SECTIONS_ONLY	0x0002   127 #define PROFILE_ITER_RELATIONS_ONLY	0x0004   128    129 #define PROFILE_ITER_FINAL_SEEN		0x0100   130    131 /*   132  * Check if a filespec is last in a list (NULL on UNIX, invalid FSSpec on MacOS   133  */   134    135 #define	PROFILE_LAST_FILESPEC(x) (((x) == NULL) || ((x)[0] == '\0'))   136    137 struct profile_node {   138 	errcode_t	magic;   139 	char *name;   140 	char *value;   141 	int group_level;   142 	unsigned int final:1;		/* Indicate don't search next file */   143 	unsigned int deleted:1;   144 	struct profile_node *first_child;   145 	struct profile_node *parent;   146 	struct profile_node *next, *prev;   147 };   148    149 #define CHECK_MAGIC(node) \   150 	  if ((node)->magic != PROF_MAGIC_NODE) \   151 		  return PROF_MAGIC_NODE;   152    153 /* profile parser declarations */   154 struct parse_state {   155 	int	state;   156 	int	group_level;   157 	int	line_num;   158 	struct profile_node *root_section;   159 	struct profile_node *current_section;   160 };   161    162 static const char *default_filename = "<default>";   163    164 static profile_syntax_err_cb_t	syntax_err_cb;   165    166 static errcode_t parse_line(char *line, struct parse_state *state);   167    168 #ifdef DEBUG_PROGRAM   169 static errcode_t profile_write_tree_file   170 	(struct profile_node *root, FILE *dstfile);   171    172 static errcode_t profile_write_tree_to_buffer   173 	(struct profile_node *root, char **buf);   174 #endif   175    176    177 static void profile_free_node   178 	(struct profile_node *relation);   179    180 static errcode_t profile_create_node   181 	(const char *name, const char *value,   182 		   struct profile_node **ret_node);   183    184 #ifdef DEBUG_PROGRAM   185 static errcode_t profile_verify_node   186 	(struct profile_node *node);   187 #endif   188    189 static errcode_t profile_add_node   190 	(struct profile_node *section,   191 		    const char *name, const char *value,   192 		    struct profile_node **ret_node);   193    194 static errcode_t profile_find_node   195 	(struct profile_node *section,   196 		    const char *name, const char *value,   197 		    int section_flag, void **state,   198 		    struct profile_node **node);   199    200 static errcode_t profile_node_iterator   201 	(void	**iter_p, struct profile_node **ret_node,   202 		   char **ret_name, char **ret_value);   203    204 static errcode_t profile_open_file   205 	(const char * file, prf_file_t *ret_prof);   206    207 static errcode_t profile_update_file   208 	(prf_file_t prf);   209    210 static void profile_free_file   211 	(prf_file_t profile);   212    213 static errcode_t profile_get_value(profile_t profile, const char *name,   214 				   const char *subname, const char *subsubname,   215 				   const char **ret_value);   216    217    218 /*   219  * prof_init.c --- routines that manipulate the user-visible profile_t   220  * 	object.   221  */   222    223 static int compstr(const void *m1, const void *m2)   224 {   225 	const char *s1 = *((const char * const *) m1);   226 	const char *s2 = *((const char * const *) m2);   227    228 	return strcmp(s1, s2);   229 }   230    231 static void free_list(char **list)   232 {   233     char	**cp;   234    235     if (list == 0)   236 	    return;   237    238     for (cp = list; *cp; cp++)   239 	free(*cp);   240     free(list);   241 }   242    243 static errcode_t get_dirlist(const char *dirname, char***ret_array)   244 {   245 	DIR *dir;   246 	struct dirent *de;   247 	struct stat st;   248 	errcode_t retval;   249 	char *fn, *cp;   250 	char **array = 0, **new_array;   251 	int max = 0, num = 0;   252    253 	dir = opendir(dirname);   254 	if (!dir)   255 		return errno;   256    257 	while ((de = readdir(dir)) != NULL) {   258 		for (cp = de->d_name; *cp; cp++) {   259 			if (!isalnum(*cp) &&   260 			    (*cp != '-') &&   261 			    (*cp != '_'))   262 				break;   263 		}   264 		if (*cp)   265 			continue;   266 		fn = malloc(strlen(dirname) + strlen(de->d_name) + 2);   267 		if (!fn) {   268 			retval = ENOMEM;   269 			goto errout;   270 		}   271 		sprintf(fn, "%s/%s", dirname, de->d_name);   272 		if ((stat(fn, &st) < 0) || !S_ISREG(st.st_mode)) {   273 			free(fn);   274 			continue;   275 		}   276 		if (num >= max) {   277 			max += 10;   278 			new_array = realloc(array, sizeof(char *) * (max+1));   279 			if (!new_array) {   280 				retval = ENOMEM;   281 				free(fn);   282 				goto errout;   283 			}   284 			array = new_array;   285 		}   286 		array[num++] = fn;   287 	}   288 	if (array) {   289 		qsort(array, num, sizeof(char *), compstr);   290 		array[num++] = 0;   291 	}   292 	*ret_array = array;   293 	closedir(dir);   294 	return 0;   295 errout:   296 	if (array)   297 		array[num] = 0;   298 	closedir(dir);   299 	free_list(array);   300 	return retval;   301 }   302    303 errcode_t   304 profile_init(const char * const *files, profile_t *ret_profile)   305 {   306 	const char * const *fs;   307 	profile_t profile;   308 	prf_file_t  new_file, *last;   309 	errcode_t retval = 0;   310 	char **cpp, *cp, **array = 0;   311    312 	profile = malloc(sizeof(struct _profile_t));   313 	if (!profile)   314 		return ENOMEM;   315 	memset(profile, 0, sizeof(struct _profile_t));   316 	profile->magic = PROF_MAGIC_PROFILE;   317 	last = &profile->first_file;   318    319         /* if the filenames list is not specified return an empty profile */   320         if ( files ) {   321 	    for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) {   322 		if (array)   323 			free_list(array);   324 		array = NULL;   325 		retval = get_dirlist(*fs, &array);   326 		if (retval == 0) {   327 			if (!array)   328 				continue;   329 			for (cpp = array; (cp = *cpp); cpp++) {   330 				retval = profile_open_file(cp, &new_file);   331 				if (retval == EACCES)   332 					continue;   333 				if (retval)   334 					goto errout;   335 				*last = new_file;   336 				last = &new_file->next;   337 			}   338 		} else if ((retval != ENOTDIR) &&   339 			   strcmp(*fs, default_filename))   340 			goto errout;   341    342 		retval = profile_open_file(*fs, &new_file);   343 		/* if this file is missing, skip to the next */   344 		if (retval == ENOENT || retval == EACCES) {   345 			continue;   346 		}   347 		if (retval)   348 			goto errout;   349 		*last = new_file;   350 		last = &new_file->next;   351 	    }   352 	    /*   353 	     * If all the files were not found, return the appropriate error.   354 	     */   355 	    if (!profile->first_file) {   356 		retval = ENOENT;   357 		goto errout;   358 	    }   359 	}   360    361 	free_list(array);   362         *ret_profile = profile;   363         return 0;   364 errout:   365 	free_list(array);   366 	profile_release(profile);   367 	return retval;   368 }   369    370 void   371 profile_release(profile_t profile)   372 {   373 	prf_file_t	p, next;   374    375 	if (!profile || profile->magic != PROF_MAGIC_PROFILE)   376 		return;   377    378 	for (p = profile->first_file; p; p = next) {   379 		next = p->next;   380 		profile_free_file(p);   381 	}   382 	profile->magic = 0;   383 	free(profile);   384 }   385    386 /*   387  * This function sets the value of the pseudo file "<default>".  If   388  * the file "<default>" had previously been passed to profile_init(),   389  * then def_string parameter will be parsed and used as the profile   390  * information for the "<default>" file.   391  */   392 errcode_t profile_set_default(profile_t profile, const char *def_string)   393 {   394 	struct parse_state	state;   395 	prf_file_t		prf;   396 	errcode_t		retval;   397 	const char		*in;   398 	char			*line, *p, *end;   399 	int			line_size, len;   400    401 	if (!def_string || !profile || profile->magic != PROF_MAGIC_PROFILE)   402 		return PROF_MAGIC_PROFILE;   403    404 	for (prf = profile->first_file; prf; prf = prf->next) {   405 		if (strcmp(prf->filespec, default_filename) == 0)   406 			break;   407 	}   408 	if (!prf)   409 		return 0;   410    411 	if (prf->root) {   412 		profile_free_node(prf->root);   413 		prf->root = 0;   414 	}   415    416 	memset(&state, 0, sizeof(struct parse_state));   417 	retval = profile_create_node("(root)", 0, &state.root_section);   418 	if (retval)   419 		return retval;   420    421 	line = 0;   422 	line_size = 0;   423 	in = def_string;   424 	while (*in) {   425 		end = strchr(in, '\n');   426 		len = end ? (end - in) : (int) strlen(in);   427 		if (len >= line_size) {   428 			line_size = len+1;   429 			p = realloc(line, line_size);   430 			if (!p) {   431 				retval = ENOMEM;   432 				goto errout;   433 			}   434 			line = p;   435 		}   436 		memcpy(line, in, len);   437 		line[len] = 0;   438 		retval = parse_line(line, &state);   439 		if (retval) {   440 		errout:   441 			if (syntax_err_cb)   442 				(syntax_err_cb)(prf->filespec, retval,   443 						state.line_num);   444 			free(line);   445 			if (prf->root)   446 				profile_free_node(prf->root);   447 			return retval;   448 		}   449 		if (!end)   450 			break;   451 		in = end+1;   452 	}   453 	prf->root = state.root_section;   454 	free(line);   455    456 	return 0;   457 }   458    459 /*   460  * prof_file.c ---- routines that manipulate an individual profile file.   461  */   462    463 errcode_t profile_open_file(const char * filespec,   464 			    prf_file_t *ret_prof)   465 {   466 	prf_file_t	prf;   467 	errcode_t	retval;   468 	char		*home_env = 0;   469 	unsigned int	len;   470 	char		*expanded_filename;   471    472 	prf = malloc(sizeof(struct _prf_file_t));   473 	if (!prf)   474 		return ENOMEM;   475 	memset(prf, 0, sizeof(struct _prf_file_t));   476 	prf->magic = PROF_MAGIC_FILE;   477    478 	len = strlen(filespec)+1;   479 	if (filespec[0] == '~' && filespec[1] == '/') {   480 		home_env = getenv("HOME");   481 #ifdef HAVE_PWD_H   482 		if (home_env == NULL) {   483 #ifdef HAVE_GETWUID_R   484 		    struct passwd *pw, pwx;   485 		    uid_t uid;   486 		    char pwbuf[BUFSIZ];   487    488 		    uid = getuid();   489 		    if (!getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw)   490 			&& pw != NULL && pw->pw_dir[0] != 0)   491 			home_env = pw->pw_dir;   492 #else   493 		    struct passwd *pw;   494    495 		    pw = getpwuid(getuid());   496 		    home_env = pw->pw_dir;   497 #endif   498 		}   499 #endif   500 		if (home_env)   501 			len += strlen(home_env);   502 	}   503 	expanded_filename = malloc(len);   504 	if (expanded_filename == 0) {   505 	    profile_free_file(prf);   506 	    return errno;   507 	}   508 	if (home_env) {   509 	    strcpy(expanded_filename, home_env);   510 	    strcat(expanded_filename, filespec+1);   511 	} else   512 	    memcpy(expanded_filename, filespec, len);   513    514 	prf->filespec = expanded_filename;   515    516 	if (strcmp(prf->filespec, default_filename) != 0) {   517 		retval = profile_update_file(prf);   518 		if (retval) {   519 			profile_free_file(prf);   520 			return retval;   521 		}   522 	}   523    524 	*ret_prof = prf;   525 	return 0;   526 }   527    528 errcode_t profile_update_file(prf_file_t prf)   529 {   530 	errcode_t retval;   531 #ifdef HAVE_STAT   532 	struct stat st;   533 #ifdef STAT_ONCE_PER_SECOND   534 	time_t now;   535 #endif   536 #endif   537 	FILE *f;   538 	char buf[2048];   539 	struct parse_state state;   540    541 	if (prf->flags & PROFILE_FILE_NO_RELOAD)   542 		return 0;   543    544 #ifdef HAVE_STAT   545 #ifdef STAT_ONCE_PER_SECOND   546 	now = time(0);   547 	if (now == prf->last_stat && prf->root != NULL) {   548 	    return 0;   549 	}   550 #endif   551 	if (stat(prf->filespec, &st)) {   552 	    retval = errno;   553 	    return retval;   554 	}   555 #ifdef STAT_ONCE_PER_SECOND   556 	prf->last_stat = now;   557 #endif   558 	if (st.st_mtime == prf->timestamp && prf->root != NULL) {   559 	    return 0;   560 	}   561 	if (prf->root) {   562 		profile_free_node(prf->root);   563 		prf->root = 0;   564 	}   565 #else   566 	/*   567 	 * If we don't have the stat() call, assume that our in-core   568 	 * memory image is correct.  That is, we won't reread the   569 	 * profile file if it changes.   570 	 */   571 	if (prf->root) {   572 	    return 0;   573 	}   574 #endif   575 	memset(&state, 0, sizeof(struct parse_state));   576 	retval = profile_create_node("(root)", 0, &state.root_section);   577 	if (retval)   578 		return retval;   579 	errno = 0;   580 	f = fopen(prf->filespec, "r");   581 	if (f == NULL) {   582 		retval = errno;   583 		if (retval == 0)   584 			retval = ENOENT;   585 		return retval;   586 	}   587 	prf->upd_serial++;   588 	while (!feof(f)) {   589 		if (fgets(buf, sizeof(buf), f) == NULL)   590 			break;   591 		retval = parse_line(buf, &state);   592 		if (retval) {   593 			if (syntax_err_cb)   594 				(syntax_err_cb)(prf->filespec, retval,   595 						state.line_num);   596 			fclose(f);   597 			return retval;   598 		}   599 	}   600 	prf->root = state.root_section;   601    602 	fclose(f);   603    604 #ifdef HAVE_STAT   605 	prf->timestamp = st.st_mtime;   606 #endif   607 	return 0;   608 }   609    610 void profile_free_file(prf_file_t prf)   611 {   612     if (prf->root)   613 	profile_free_node(prf->root);   614     free(prf->filespec);   615     free(prf);   616 }   617    618 /* Begin the profile parser */   619    620 profile_syntax_err_cb_t profile_set_syntax_err_cb(profile_syntax_err_cb_t hook)   621 {   622 	profile_syntax_err_cb_t	old;   623    624 	old = syntax_err_cb;   625 	syntax_err_cb = hook;   626 	return(old);   627 }   628    629 #define STATE_INIT_COMMENT	0   630 #define STATE_STD_LINE		1   631 #define STATE_GET_OBRACE	2   632    633 static char *skip_over_blanks(char *cp)   634 {   635 	while (*cp && isspace((int) (*cp)))   636 		cp++;   637 	return cp;   638 }   639    640 static int end_or_comment(char ch)   641 {   642 	return (ch == 0 || ch == '#' || ch == ';');   643 }   644    645 static char *skip_over_nonblanks(char *cp)   646 {   647 	while (!end_or_comment(*cp) && !isspace(*cp))   648 		cp++;   649 	return cp;   650 }   651    652 static void strip_line(char *line)   653 {   654 	char *p = line + strlen(line);   655 	while (p > line && (p[-1] == '\n' || p[-1] == '\r'))   656 	    *p-- = 0;   657 }   658    659 static void parse_quoted_string(char *str)   660 {   661 	char *to, *from;   662    663 	to = from = str;   664    665 	for (to = from = str; *from && *from != '"'; to++, from++) {   666 		if (*from == '\\') {   667 			from++;   668 			switch (*from) {   669 			case 'n':   670 				*to = '\n';   671 				break;   672 			case 't':   673 				*to = '\t';   674 				break;   675 			case 'b':   676 				*to = '\b';   677 				break;   678 			default:   679 				*to = *from;   680 			}   681 			continue;   682 		}   683 		*to = *from;   684 	}   685 	*to = '\0';   686 }   687    688 static errcode_t parse_line(char *line, struct parse_state *state)   689 {   690 	char	*cp, ch, *tag, *value;   691 	char	*p;   692 	errcode_t retval;   693 	struct profile_node	*node;   694 	int do_subsection = 0;   695 	void *iter = 0;   696    697 	state->line_num++;   698 	if (state->state == STATE_GET_OBRACE) {   699 		cp = skip_over_blanks(line);   700 		if (*cp != '{')   701 			return PROF_MISSING_OBRACE;   702 		state->state = STATE_STD_LINE;   703 		return 0;   704 	}   705 	if (state->state == STATE_INIT_COMMENT) {   706 		if (line[0] != '[')   707 			return 0;   708 		state->state = STATE_STD_LINE;   709 	}   710    711 	if (*line == 0)   712 		return 0;   713 	strip_line(line);   714 	cp = skip_over_blanks(line);   715 	ch = *cp;   716 	if (end_or_comment(ch))   717 		return 0;   718 	if (ch == '[') {   719 		if (state->group_level > 0)   720 			return PROF_SECTION_NOTOP;   721 		cp++;   722 		cp = skip_over_blanks(cp);   723 		p = strchr(cp, ']');   724 		if (p == NULL)   725 			return PROF_SECTION_SYNTAX;   726 		if (*cp == '"') {   727 			cp++;   728 			parse_quoted_string(cp);   729 		} else {   730 			*p-- = '\0';   731 			while (isspace(*p) && (p > cp))   732 				*p-- = '\0';   733 			if (*cp == 0)   734 				return PROF_SECTION_SYNTAX;   735 		}   736 		retval = profile_find_node(state->root_section, cp, 0, 1,   737 					   &iter, &state->current_section);   738 		if (retval == PROF_NO_SECTION) {   739 			retval = profile_add_node(state->root_section,   740 						  cp, 0,   741 						  &state->current_section);   742 			if (retval)   743 				return retval;   744 		} else if (retval)   745 			return retval;   746    747 		/*   748 		 * Finish off the rest of the line.   749 		 */   750 		cp = p+1;   751 		if (*cp == '*') {   752 			state->current_section->final = 1;   753 			cp++;   754 		}   755 		/*   756 		 * Spaces or comments after ']' should not be fatal   757 		 */   758 		cp = skip_over_blanks(cp);   759 		if (!end_or_comment(*cp))   760 			return PROF_SECTION_SYNTAX;   761 		return 0;   762 	}   763 	if (ch == '}') {   764 		if (state->group_level == 0)   765 			return PROF_EXTRA_CBRACE;   766 		if (*(cp+1) == '*')   767 			state->current_section->final = 1;   768 		state->current_section = state->current_section->parent;   769 		state->group_level--;   770 		return 0;   771 	}   772 	/*   773 	 * Parse the relations   774 	 */   775 	tag = cp;   776 	cp = strchr(cp, '=');   777 	if (!cp)   778 		return PROF_RELATION_SYNTAX;   779 	if (cp == tag)   780 	    return PROF_RELATION_SYNTAX;   781 	*cp = '\0';   782 	if (*tag == '"') {   783 		tag++;   784 		parse_quoted_string(tag);   785 	} else {   786 		/* Look for whitespace on left-hand side.  */   787 		p = skip_over_nonblanks(tag);   788 		if (*p)   789 			*p++ = 0;   790 		p = skip_over_blanks(p);   791 		/* If we have more non-whitespace, it's an error.  */   792 		if (*p)   793 			return PROF_RELATION_SYNTAX;   794 	}   795    796 	cp = skip_over_blanks(cp+1);   797 	value = cp;   798 	ch = value[0];   799 	if (ch == '"') {   800 		value++;   801 		parse_quoted_string(value);   802 	} else if (end_or_comment(ch)) {   803 		do_subsection++;   804 		state->state = STATE_GET_OBRACE;   805 	} else if (value[0] == '{') {   806 		cp = skip_over_blanks(value+1);   807 		ch = *cp;   808 		if (end_or_comment(ch))   809 			do_subsection++;   810 		else   811 			return PROF_RELATION_SYNTAX;   812 	} else {   813 		cp = skip_over_nonblanks(value);   814 		p = skip_over_blanks(cp);   815 		ch = *p;   816 		*cp = 0;   817 		if (!end_or_comment(ch))   818 			return PROF_RELATION_SYNTAX;   819 	}   820 	if (do_subsection) {   821 		p = strchr(tag, '*');   822 		if (p)   823 			*p = '\0';   824 		retval = profile_add_node(state->current_section,   825 					  tag, 0, &state->current_section);   826 		if (retval)   827 			return retval;   828 		if (p)   829 			state->current_section->final = 1;   830 		state->group_level++;   831 		return 0;   832 	}   833 	p = strchr(tag, '*');   834 	if (p)   835 		*p = '\0';   836 	profile_add_node(state->current_section, tag, value, &node);   837 	if (p)   838 		node->final = 1;   839 	return 0;   840 }   841    842 #ifdef DEBUG_PROGRAM   843 /*   844  * Return TRUE if the string begins or ends with whitespace   845  */   846 static int need_double_quotes(char *str)   847 {   848 	if (!str || !*str)   849 		return 0;   850 	if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1))))   851 		return 1;   852 	if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b') ||   853 	    strchr(str, ' ') || strchr(str, '#') || strchr(str, ';'))   854 		return 1;   855 	return 0;   856 }   857    858 /*   859  * Output a string with double quotes, doing appropriate backquoting   860  * of characters as necessary.   861  */   862 static void output_quoted_string(char *str, void (*cb)(const char *,void *),   863 				 void *data)   864 {   865 	char	ch;   866 	char buf[2];   867    868 	cb("\"", data);   869 	if (!str) {   870 		cb("\"", data);   871 		return;   872 	}   873 	buf[1] = 0;   874 	while ((ch = *str++)) {   875 		switch (ch) {   876 		case '\\':   877 			cb("\\\\", data);   878 			break;   879 		case '\n':   880 			cb("\\n", data);   881 			break;   882 		case '\t':   883 			cb("\\t", data);   884 			break;   885 		case '\b':   886 			cb("\\b", data);   887 			break;   888 		default:   889 			/* This would be a lot faster if we scanned   890 			   forward for the next "interesting"   891 			   character.  */   892 			buf[0] = ch;   893 			cb(buf, data);   894 			break;   895 		}   896 	}   897 	cb("\"", data);   898 }   899    900 #ifndef EOL   901 #define EOL "\n"   902 #endif   903    904 /* Errors should be returned, not ignored!  */   905 static void dump_profile(struct profile_node *root, int level,   906 			 void (*cb)(const char *, void *), void *data)   907 {   908 	int i;   909 	struct profile_node *p;   910 	void *iter;   911 	long retval;   912    913 	iter = 0;   914 	do {   915 		retval = profile_find_node(root, 0, 0, 0, &iter, &p);   916 		if (retval)   917 			break;   918 		for (i=0; i < level; i++)   919 			cb("\t", data);   920 		if (need_double_quotes(p->name))   921 			output_quoted_string(p->name, cb, data);   922 		else   923 			cb(p->name, data);   924 		cb(" = ", data);   925 		if (need_double_quotes(p->value))   926 			output_quoted_string(p->value, cb, data);   927 		else   928 			cb(p->value, data);   929 		cb(EOL, data);   930 	} while (iter != 0);   931    932 	iter = 0;   933 	do {   934 		retval = profile_find_node(root, 0, 0, 1, &iter, &p);   935 		if (retval)   936 			break;   937 		if (level == 0)	{ /* [xxx] */   938 			cb("[", data);   939 			if (need_double_quotes(p->name))   940 				output_quoted_string(p->name, cb, data);   941 			else   942 				cb(p->name, data);   943 			cb("]", data);   944 			cb(p->final ? "*" : "", data);   945 			cb(EOL, data);   946 			dump_profile(p, level+1, cb, data);   947 			cb(EOL, data);   948 		} else { 	/* xxx = { ... } */   949 			for (i=0; i < level; i++)   950 				cb("\t", data);   951 			if (need_double_quotes(p->name))   952 				output_quoted_string(p->name, cb, data);   953 			else   954 				cb(p->name, data);   955 			cb(" = {", data);   956 			cb(EOL, data);   957 			dump_profile(p, level+1, cb, data);   958 			for (i=0; i < level; i++)   959 				cb("\t", data);   960 			cb("}", data);   961 			cb(p->final ? "*" : "", data);   962 			cb(EOL, data);   963 		}   964 	} while (iter != 0);   965 }   966    967 static void dump_profile_to_file_cb(const char *str, void *data)   968 {   969 	fputs(str, data);   970 }   971    972 errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile)   973 {   974 	dump_profile(root, 0, dump_profile_to_file_cb, dstfile);   975 	return 0;   976 }   977    978 struct prof_buf {   979 	char *base;   980 	size_t cur, max;   981 	int err;   982 };   983    984 static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len)   985 {   986 	if (b->err)   987 		return;   988 	if (b->max - b->cur < len) {   989 		size_t newsize;   990 		char *newptr;   991    992 		newsize = b->max + (b->max >> 1) + len + 1024;   993 		newptr = realloc(b->base, newsize);   994 		if (newptr == NULL) {   995 			b->err = 1;   996 			return;   997 		}   998 		b->base = newptr;   999 		b->max = newsize;  1000 	}  1001 	memcpy(b->base + b->cur, d, len);  1002 	b->cur += len; 		/* ignore overflow */  1003 }  1004   1005 static void dump_profile_to_buffer_cb(const char *str, void *data)  1006 {  1007 	add_data_to_buffer((struct prof_buf *)data, str, strlen(str));  1008 }  1009   1010 errcode_t profile_write_tree_to_buffer(struct profile_node *root,  1011 				       char **buf)  1012 {  1013 	struct prof_buf prof_buf = { 0, 0, 0, 0 };  1014   1015 	dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf);  1016 	if (prof_buf.err) {  1017 		*buf = NULL;  1018 		return ENOMEM;  1019 	}  1020 	add_data_to_buffer(&prof_buf, "", 1); /* append nul */  1021 	if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) {  1022 		char *newptr = realloc(prof_buf.base, prof_buf.cur);  1023 		if (newptr)  1024 			prof_buf.base = newptr;  1025 	}  1026 	*buf = prof_buf.base;  1027 	return 0;  1028 }  1029 #endif  1030   1031 /*  1032  * prof_tree.c --- these routines maintain the parse tree of the  1033  * 	config file.  1034  *  1035  * All of the details of how the tree is stored is abstracted away in  1036  * this file; all of the other profile routines build, access, and  1037  * modify the tree via the accessor functions found in this file.  1038  *  1039  * Each node may represent either a relation or a section header.  1040  *  1041  * A section header must have its value field set to 0, and may a one  1042  * or more child nodes, pointed to by first_child.  1043  *  1044  * A relation has as its value a pointer to allocated memory  1045  * containing a string.  Its first_child pointer must be null.  1046  *  1047  */  1048   1049 /*  1050  * Free a node, and any children  1051  */  1052 void profile_free_node(struct profile_node *node)  1053 {  1054 	struct profile_node *child, *next;  1055   1056 	if (node->magic != PROF_MAGIC_NODE)  1057 		return;  1058   1059 	free(node->name);  1060 	free(node->value);  1061   1062 	for (child=node->first_child; child; child = next) {  1063 		next = child->next;  1064 		profile_free_node(child);  1065 	}  1066 	node->magic = 0;  1067   1068 	free(node);  1069 }  1070   1071 #ifndef HAVE_STRDUP  1072 #undef strdup  1073 #define strdup MYstrdup  1074 static char *MYstrdup (const char *s)  1075 {  1076     size_t sz = strlen(s) + 1;  1077     char *p = malloc(sz);  1078     if (p != 0)  1079 	memcpy(p, s, sz);  1080     return p;  1081 }  1082 #endif  1083   1084 /*  1085  * Create a node  1086  */  1087 errcode_t profile_create_node(const char *name, const char *value,  1088 			      struct profile_node **ret_node)  1089 {  1090 	struct profile_node *new;  1091   1092 	new = malloc(sizeof(struct profile_node));  1093 	if (!new)  1094 		return ENOMEM;  1095 	memset(new, 0, sizeof(struct profile_node));  1096 	new->name = strdup(name);  1097 	if (new->name == 0) {  1098 	    profile_free_node(new);  1099 	    return ENOMEM;  1100 	}  1101 	if (value) {  1102 		new->value = strdup(value);  1103 		if (new->value == 0) {  1104 		    profile_free_node(new);  1105 		    return ENOMEM;  1106 		}  1107 	}  1108 	new->magic = PROF_MAGIC_NODE;  1109   1110 	*ret_node = new;  1111 	return 0;  1112 }  1113   1114 /*  1115  * This function verifies that all of the representation invariants of  1116  * the profile are true.  If not, we have a programming bug somewhere,  1117  * probably in this file.  1118  */  1119 #ifdef DEBUG_PROGRAM  1120 errcode_t profile_verify_node(struct profile_node *node)  1121 {  1122 	struct profile_node *p, *last;  1123 	errcode_t	retval;  1124   1125 	CHECK_MAGIC(node);  1126   1127 	if (node->value && node->first_child)  1128 		return PROF_SECTION_WITH_VALUE;  1129   1130 	last = 0;  1131 	for (p = node->first_child; p; last = p, p = p->next) {  1132 		if (p->prev != last)  1133 			return PROF_BAD_LINK_LIST;  1134 		if (last && (last->next != p))  1135 			return PROF_BAD_LINK_LIST;  1136 		if (node->group_level+1 != p->group_level)  1137 			return PROF_BAD_GROUP_LVL;  1138 		if (p->parent != node)  1139 			return PROF_BAD_PARENT_PTR;  1140 		retval = profile_verify_node(p);  1141 		if (retval)  1142 			return retval;  1143 	}  1144 	return 0;  1145 }  1146 #endif  1147   1148 /*  1149  * Add a node to a particular section  1150  */  1151 errcode_t profile_add_node(struct profile_node *section, const char *name,  1152 			   const char *value, struct profile_node **ret_node)  1153 {  1154 	errcode_t retval;  1155 	struct profile_node *p, *last, *new;  1156   1157 	CHECK_MAGIC(section);  1158   1159 	if (section->value)  1160 		return PROF_ADD_NOT_SECTION;  1161   1162 	/*  1163 	 * Find the place to insert the new node.  We look for the  1164 	 * place *after* the last match of the node name, since  1165 	 * order matters.  1166 	 */  1167 	for (p=section->first_child, last = 0; p; last = p, p = p->next) {  1168 		int cmp;  1169 		cmp = strcmp(p->name, name);  1170 		if (cmp > 0)  1171 			break;  1172 	}  1173 	retval = profile_create_node(name, value, &new);  1174 	if (retval)  1175 		return retval;  1176 	new->group_level = section->group_level+1;  1177 	new->deleted = 0;  1178 	new->parent = section;  1179 	new->prev = last;  1180 	new->next = p;  1181 	if (p)  1182 		p->prev = new;  1183 	if (last)  1184 		last->next = new;  1185 	else  1186 		section->first_child = new;  1187 	if (ret_node)  1188 		*ret_node = new;  1189 	return 0;  1190 }  1191   1192 /*  1193  * Iterate through the section, returning the nodes which match  1194  * the given name.  If name is NULL, then interate through all the  1195  * nodes in the section.  If section_flag is non-zero, only return the  1196  * section which matches the name; don't return relations.  If value  1197  * is non-NULL, then only return relations which match the requested  1198  * value.  (The value argument is ignored if section_flag is non-zero.)  1199  *  1200  * The first time this routine is called, the state pointer must be  1201  * null.  When this profile_find_node_relation() returns, if the state  1202  * pointer is non-NULL, then this routine should be called again.  1203  * (This won't happen if section_flag is non-zero, obviously.)  1204  *  1205  */  1206 errcode_t profile_find_node(struct profile_node *section, const char *name,  1207 			    const char *value, int section_flag, void **state,  1208 			    struct profile_node **node)  1209 {  1210 	struct profile_node *p;  1211   1212 	CHECK_MAGIC(section);  1213 	p = *state;  1214 	if (p) {  1215 		CHECK_MAGIC(p);  1216 	} else  1217 		p = section->first_child;  1218   1219 	for (; p; p = p->next) {  1220 		if (name && (strcmp(p->name, name)))  1221 			continue;  1222 		if (section_flag) {  1223 			if (p->value)  1224 				continue;  1225 		} else {  1226 			if (!p->value)  1227 				continue;  1228 			if (value && (strcmp(p->value, value)))  1229 				continue;  1230 		}  1231 		if (p->deleted)  1232 		    continue;  1233 		/* A match! */  1234 		if (node)  1235 			*node = p;  1236 		break;  1237 	}  1238 	if (p == 0) {  1239 		*state = 0;  1240 		return section_flag ? PROF_NO_SECTION : PROF_NO_RELATION;  1241 	}  1242 	/*  1243 	 * OK, we've found one match; now let's try to find another  1244 	 * one.  This way, if we return a non-zero state pointer,  1245 	 * there's guaranteed to be another match that's returned.  1246 	 */  1247 	for (p = p->next; p; p = p->next) {  1248 		if (name && (strcmp(p->name, name)))  1249 			continue;  1250 		if (section_flag) {  1251 			if (p->value)  1252 				continue;  1253 		} else {  1254 			if (!p->value)  1255 				continue;  1256 			if (value && (strcmp(p->value, value)))  1257 				continue;  1258 		}  1259 		/* A match! */  1260 		break;  1261 	}  1262 	*state = p;  1263 	return 0;  1264 }  1265   1266 /*  1267  * This is a general-purpose iterator for returning all nodes that  1268  * match the specified name array.  1269  */  1270 struct profile_iterator {  1271 	prf_magic_t		magic;  1272 	profile_t		profile;  1273 	int			flags;  1274 	const char 		*const *names;  1275 	const char		*name;  1276 	prf_file_t		file;  1277 	int			file_serial;  1278 	int			done_idx;  1279 	struct profile_node 	*node;  1280 	int			num;  1281 };  1282   1283 errcode_t  1284 profile_iterator_create(profile_t profile, const char *const *names, int flags,  1285 			void **ret_iter)  1286 {  1287 	struct profile_iterator *iter;  1288 	int	done_idx = 0;  1289   1290 	if (profile == 0)  1291 		return PROF_NO_PROFILE;  1292 	if (profile->magic != PROF_MAGIC_PROFILE)  1293 		return PROF_MAGIC_PROFILE;  1294 	if (!names)  1295 		return PROF_BAD_NAMESET;  1296 	if (!(flags & PROFILE_ITER_LIST_SECTION)) {  1297 		if (!names[0])  1298 			return PROF_BAD_NAMESET;  1299 		done_idx = 1;  1300 	}  1301   1302 	if ((iter = malloc(sizeof(struct profile_iterator))) == NULL)  1303 		return ENOMEM;  1304   1305 	iter->magic = PROF_MAGIC_ITERATOR;  1306 	iter->profile = profile;  1307 	iter->names = names;  1308 	iter->flags = flags;  1309 	iter->file = profile->first_file;  1310 	iter->done_idx = done_idx;  1311 	iter->node = 0;  1312 	iter->num = 0;  1313 	*ret_iter = iter;  1314 	return 0;  1315 }  1316   1317 void profile_iterator_free(void **iter_p)  1318 {  1319 	struct profile_iterator *iter;  1320   1321 	if (!iter_p)  1322 		return;  1323 	iter = *iter_p;  1324 	if (!iter || iter->magic != PROF_MAGIC_ITERATOR)  1325 		return;  1326 	free(iter);  1327 	*iter_p = 0;  1328 }  1329   1330 /*  1331  * Note: the returned character strings in ret_name and ret_value  1332  * points to the stored character string in the parse string.  Before  1333  * this string value is returned to a calling application  1334  * (profile_node_iterator is not an exported interface), it should be  1335  * strdup()'ed.  1336  */  1337 errcode_t profile_node_iterator(void **iter_p, struct profile_node **ret_node,  1338 				char **ret_name, char **ret_value)  1339 {  1340 	struct profile_iterator 	*iter = *iter_p;  1341 	struct profile_node 		*section, *p;  1342 	const char			*const *cpp;  1343 	errcode_t			retval;  1344 	int				skip_num = 0;  1345   1346 	if (!iter || iter->magic != PROF_MAGIC_ITERATOR)  1347 		return PROF_MAGIC_ITERATOR;  1348 	if (iter->file && iter->file->magic != PROF_MAGIC_FILE)  1349 	    return PROF_MAGIC_FILE;  1350 	/*  1351 	 * If the file has changed, then the node pointer is invalid,  1352 	 * so we'll have search the file again looking for it.  1353 	 */  1354 	if (iter->node && (iter->file &&  1355 			   iter->file->upd_serial != iter->file_serial)) {  1356 		iter->flags &= ~PROFILE_ITER_FINAL_SEEN;  1357 		skip_num = iter->num;  1358 		iter->node = 0;  1359 	}  1360 	if (iter->node && iter->node->magic != PROF_MAGIC_NODE) {  1361 	    return PROF_MAGIC_NODE;  1362 	}  1363 get_new_file:  1364 	if (iter->node == 0) {  1365 		if (iter->file == NULL ||  1366 		    (iter->flags & PROFILE_ITER_FINAL_SEEN)) {  1367 			profile_iterator_free(iter_p);  1368 			if (ret_node)  1369 				*ret_node = 0;  1370 			if (ret_name)  1371 				*ret_name = 0;  1372 			if (ret_value)  1373 				*ret_value =0;  1374 			return 0;  1375 		}  1376 		if ((retval = profile_update_file(iter->file))) {  1377 		    if (retval == ENOENT || retval == EACCES) {  1378 			/* XXX memory leak? */  1379 			if (iter->file)  1380 			    iter->file = iter->file->next;  1381 			skip_num = 0;  1382 			retval = 0;  1383 			goto get_new_file;  1384 		    } else {  1385 			profile_iterator_free(iter_p);  1386 			return retval;  1387 		    }  1388 		}  1389 		iter->file_serial = iter->file->upd_serial;  1390 		/*  1391 		 * Find the section to list if we are a LIST_SECTION,  1392 		 * or find the containing section if not.  1393 		 */  1394 		section = iter->file->root;  1395 		for (cpp = iter->names; cpp[iter->done_idx]; cpp++) {  1396 			for (p=section->first_child; p; p = p->next) {  1397 				if (!strcmp(p->name, *cpp) && !p->value)  1398 					break;  1399 			}  1400 			if (!p) {  1401 				section = 0;  1402 				break;  1403 			}  1404 			section = p;  1405 			if (p->final)  1406 				iter->flags |= PROFILE_ITER_FINAL_SEEN;  1407 		}  1408 		if (!section) {  1409 			if (iter->file)  1410 				iter->file = iter->file->next;  1411 			skip_num = 0;  1412 			goto get_new_file;  1413 		}  1414 		iter->name = *cpp;  1415 		iter->node = section->first_child;  1416 	}  1417 	/*  1418 	 * OK, now we know iter->node is set up correctly.  Let's do  1419 	 * the search.  1420 	 */  1421 	for (p = iter->node; p; p = p->next) {  1422 		if (iter->name && strcmp(p->name, iter->name))  1423 			continue;  1424 		if ((iter->flags & PROFILE_ITER_SECTIONS_ONLY) &&  1425 		    p->value)  1426 			continue;  1427 		if ((iter->flags & PROFILE_ITER_RELATIONS_ONLY) &&  1428 		    !p->value)  1429 			continue;  1430 		if (skip_num > 0) {  1431 			skip_num--;  1432 			continue;  1433 		}  1434 		if (p->deleted)  1435 			continue;  1436 		break;  1437 	}  1438 	iter->num++;  1439 	if (!p) {  1440 		if (iter->file)  1441 			iter->file = iter->file->next;  1442 		iter->node = 0;  1443 		skip_num = 0;  1444 		goto get_new_file;  1445 	}  1446 	if ((iter->node = p->next) == NULL)  1447 		if (iter->file)  1448 			iter->file = iter->file->next;  1449 	if (ret_node)  1450 		*ret_node = p;  1451 	if (ret_name)  1452 		*ret_name = p->name;  1453 	if (ret_value)  1454 		*ret_value = p->value;  1455 	return 0;  1456 }  1457   1458   1459 /*  1460  * prof_get.c --- routines that expose the public interfaces for  1461  * 	querying items from the profile.  1462  *  1463  */  1464   1465 /*  1466  * This function only gets the first value from the file; it is a  1467  * helper function for profile_get_string, profile_get_integer, etc.  1468  */  1469 errcode_t profile_get_value(profile_t profile, const char *name,  1470 			    const char *subname, const char *subsubname,  1471 			    const char **ret_value)  1472 {  1473 	errcode_t		retval;  1474 	void			*state;  1475 	char			*value;  1476 	const char		*names[4];  1477   1478 	names[0] = name;  1479 	names[1] = subname;  1480 	names[2] = subsubname;  1481 	names[3] = 0;  1482   1483 	if ((retval = profile_iterator_create(profile, names,  1484 					      PROFILE_ITER_RELATIONS_ONLY,  1485 					      &state)))  1486 		return retval;  1487   1488 	if ((retval = profile_node_iterator(&state, 0, 0, &value)))  1489 		goto cleanup;  1490   1491 	if (value)  1492 		*ret_value = value;  1493 	else  1494 		retval = PROF_NO_RELATION;  1495   1496 cleanup:  1497 	profile_iterator_free(&state);  1498 	return retval;  1499 }  1500   1501 errcode_t  1502 profile_get_string(profile_t profile, const char *name, const char *subname,  1503 		   const char *subsubname, const char *def_val,  1504 		   char **ret_string)  1505 {  1506 	const char	*value;  1507 	errcode_t	retval;  1508   1509 	if (profile) {  1510 		retval = profile_get_value(profile, name, subname,  1511 					   subsubname, &value);  1512 		if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION)  1513 			value = def_val;  1514 		else if (retval)  1515 			return retval;  1516 	} else  1517 		value = def_val;  1518   1519 	if (value) {  1520 		*ret_string = malloc(strlen(value)+1);  1521 		if (*ret_string == 0)  1522 			return ENOMEM;  1523 		strcpy(*ret_string, value);  1524 	} else  1525 		*ret_string = 0;  1526 	return 0;  1527 }  1528   1529 errcode_t  1530 profile_get_integer(profile_t profile, const char *name, const char *subname,  1531 		    const char *subsubname, int def_val, int *ret_int)  1532 {  1533 	const char	*value;  1534 	errcode_t	retval;  1535 	char            *end_value;  1536 	long		ret_long;  1537   1538 	*ret_int = def_val;  1539 	if (profile == 0)  1540 		return 0;  1541   1542 	retval = profile_get_value(profile, name, subname, subsubname, &value);  1543 	if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {  1544 		*ret_int = def_val;  1545 		return 0;  1546 	} else if (retval)  1547 		return retval;  1548   1549 	if (value[0] == 0)  1550 	    /* Empty string is no good.  */  1551 	    return PROF_BAD_INTEGER;  1552 	errno = 0;  1553 	ret_long = strtol(value, &end_value, 0);  1554   1555 	/* Overflow or underflow.  */  1556 	if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0)  1557 	    return PROF_BAD_INTEGER;  1558 	/* Value outside "int" range.  */  1559 	if ((long) (int) ret_long != ret_long)  1560 	    return PROF_BAD_INTEGER;  1561 	/* Garbage in string.  */  1562 	if (end_value != value + strlen (value))  1563 	    return PROF_BAD_INTEGER;  1564   1565   1566 	*ret_int = ret_long;  1567 	return 0;  1568 }  1569   1570 errcode_t  1571 profile_get_uint(profile_t profile, const char *name, const char *subname,  1572 		 const char *subsubname, unsigned int def_val,  1573 		 unsigned int *ret_int)  1574 {  1575 	const char	*value;  1576 	errcode_t	retval;  1577 	char            *end_value;  1578 	unsigned long	ret_long;  1579   1580 	*ret_int = def_val;  1581 	if (profile == 0)  1582 		return 0;  1583   1584 	retval = profile_get_value(profile, name, subname, subsubname, &value);  1585 	if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {  1586 		*ret_int = def_val;  1587 		return 0;  1588 	} else if (retval)  1589 		return retval;  1590   1591 	if (value[0] == 0)  1592 	    /* Empty string is no good.  */  1593 	    return PROF_BAD_INTEGER;  1594 	errno = 0;  1595 	ret_long = strtoul(value, &end_value, 0);  1596   1597 	/* Overflow or underflow.  */  1598 	if ((ret_long == ULONG_MAX) && errno != 0)  1599 	    return PROF_BAD_INTEGER;  1600 	/* Value outside "int" range.  */  1601 	if ((unsigned long) (unsigned int) ret_long != ret_long)  1602 	    return PROF_BAD_INTEGER;  1603 	/* Garbage in string.  */  1604 	if (end_value != value + strlen (value))  1605 	    return PROF_BAD_INTEGER;  1606   1607 	*ret_int = ret_long;  1608 	return 0;  1609 }  1610   1611 errcode_t  1612 profile_get_double(profile_t profile, const char *name, const char *subname,  1613 		   const char *subsubname, double def_val, double *ret_double)  1614 {  1615 	const char	*value;  1616 	errcode_t	  retval;  1617 	char        *end_value;  1618 	double      double_val;  1619   1620 	*ret_double = def_val;  1621 	if (profile == 0)  1622 		return 0;  1623   1624 	retval = profile_get_value(profile, name, subname, subsubname, &value);  1625 	if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {  1626 		*ret_double = def_val;  1627 		return 0;  1628 	} else if (retval)  1629 		return retval;  1630   1631 	if (value[0] == 0)  1632 		/* Empty string is no good.  */  1633 		return PROF_BAD_INTEGER;  1634 	errno = 0;  1635 	double_val = strtod(value, &end_value);  1636   1637 	/* Overflow or underflow.  */  1638 	if (errno != 0)  1639 		return PROF_BAD_INTEGER;  1640 	/* Garbage in string.  */  1641 	if (end_value != value + strlen(value))  1642 		return PROF_BAD_INTEGER;  1643   1644 	*ret_double = double_val;  1645 	return 0;  1646 }  1647   1648 static const char *const conf_yes[] = {  1649     "y", "yes", "true", "t", "1", "on",  1650     0,  1651 };  1652   1653 static const char *const conf_no[] = {  1654     "n", "no", "false", "nil", "0", "off",  1655     0,  1656 };  1657   1658 static errcode_t  1659 profile_parse_boolean(const char *s, int *ret_boolean)  1660 {  1661     const char *const *p;  1662   1663     if (ret_boolean == NULL)  1664     	return PROF_EINVAL;  1665   1666     for(p=conf_yes; *p; p++) {  1667 		if (!strcasecmp(*p,s)) {  1668 			*ret_boolean = 1;  1669 	    	return 0;  1670 		}  1671     }  1672   1673     for(p=conf_no; *p; p++) {  1674 		if (!strcasecmp(*p,s)) {  1675 			*ret_boolean = 0;  1676 			return 0;  1677 		}  1678     }  1679   1680 	return PROF_BAD_BOOLEAN;  1681 }  1682   1683 errcode_t  1684 profile_get_boolean(profile_t profile, const char *name, const char *subname,  1685 		    const char *subsubname, int def_val, int *ret_boolean)  1686 {  1687 	const char	*value;  1688 	errcode_t	retval;  1689   1690 	if (profile == 0) {  1691 		*ret_boolean = def_val;  1692 		return 0;  1693 	}  1694   1695 	retval = profile_get_value(profile, name, subname, subsubname, &value);  1696 	if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {  1697 		*ret_boolean = def_val;  1698 		return 0;  1699 	} else if (retval)  1700 		return retval;  1701   1702 	return profile_parse_boolean (value, ret_boolean);  1703 }  1704   1705 errcode_t  1706 profile_iterator(void **iter_p, char **ret_name, char **ret_value)  1707 {  1708 	char *name, *value;  1709 	errcode_t	retval;  1710   1711 	retval = profile_node_iterator(iter_p, 0, &name, &value);  1712 	if (retval)  1713 		return retval;  1714   1715 	if (ret_name) {  1716 		if (name) {  1717 			*ret_name = malloc(strlen(name)+1);  1718 			if (!*ret_name)  1719 				return ENOMEM;  1720 			strcpy(*ret_name, name);  1721 		} else  1722 			*ret_name = 0;  1723 	}  1724 	if (ret_value) {  1725 		if (value) {  1726 			*ret_value = malloc(strlen(value)+1);  1727 			if (!*ret_value) {  1728 				if (ret_name) {  1729 					free(*ret_name);  1730 					*ret_name = 0;  1731 				}  1732 				return ENOMEM;  1733 			}  1734 			strcpy(*ret_value, value);  1735 		} else  1736 			*ret_value = 0;  1737 	}  1738 	return 0;  1739 }  1740   1741 #ifdef DEBUG_PROGRAM  1742   1743 /*  1744  * test_profile.c --- testing program for the profile routine  1745  */  1746   1747 #include "argv_parse.h"  1748 #include "profile_helpers.h"  1749   1750 const char *program_name = "test_profile";  1751   1752 #define PRINT_VALUE	1  1753 #define PRINT_VALUES	2  1754   1755 static void do_cmd(profile_t profile, char **argv)  1756 {  1757 	errcode_t	retval;  1758 	const char	**names, *value;  1759 	char		**values, **cpp;  1760 	char	*cmd;  1761 	int		print_status;  1762   1763 	cmd = *(argv);  1764 	names = (const char **) argv + 1;  1765 	print_status = 0;  1766 	retval = 0;  1767 	if (cmd == 0)  1768 		return;  1769 	if (!strcmp(cmd, "query")) {  1770 		retval = profile_get_values(profile, names, &values);  1771 		print_status = PRINT_VALUES;  1772 	} else if (!strcmp(cmd, "query1")) {  1773 		const char *name = 0;  1774 		const char *subname = 0;  1775 		const char *subsubname = 0;  1776   1777 		name = names[0];  1778 		if (name)  1779 			subname = names[1];  1780 		if (subname)  1781 			subsubname = names[2];  1782 		if (subsubname && names[3]) {  1783 			fprintf(stderr,  1784 				"Only 3 levels are allowed with query1\n");  1785 			retval = EINVAL;  1786 		} else  1787 			retval = profile_get_value(profile, name, subname,  1788 						   subsubname, &value);  1789 		print_status = PRINT_VALUE;  1790 	} else if (!strcmp(cmd, "list_sections")) {  1791 		retval = profile_get_subsection_names(profile, names,  1792 						      &values);  1793 		print_status = PRINT_VALUES;  1794 	} else if (!strcmp(cmd, "list_relations")) {  1795 		retval = profile_get_relation_names(profile, names,  1796 						    &values);  1797 		print_status = PRINT_VALUES;  1798 	} else if (!strcmp(cmd, "dump")) {  1799 		retval = profile_write_tree_file  1800 			(profile->first_file->root, stdout);  1801 #if 0  1802 	} else if (!strcmp(cmd, "clear")) {  1803 		retval = profile_clear_relation(profile, names);  1804 	} else if (!strcmp(cmd, "update")) {  1805 		retval = profile_update_relation(profile, names+2,  1806 						 *names, *(names+1));  1807 #endif  1808 	} else if (!strcmp(cmd, "verify")) {  1809 		retval = profile_verify_node  1810 			(profile->first_file->root);  1811 #if 0  1812 	} else if (!strcmp(cmd, "rename_section")) {  1813 		retval = profile_rename_section(profile, names+1, *names);  1814 	} else if (!strcmp(cmd, "add")) {  1815 		value = *names;  1816 		if (strcmp(value, "NULL") == 0)  1817 			value = NULL;  1818 		retval = profile_add_relation(profile, names+1, value);  1819 	} else if (!strcmp(cmd, "flush")) {  1820 		retval = profile_flush(profile);  1821 #endif  1822 	} else {  1823 		printf("Invalid command.\n");  1824 	}  1825 	if (retval) {  1826 		com_err(cmd, retval, "");  1827 		print_status = 0;  1828 	}  1829 	switch (print_status) {  1830 	case PRINT_VALUE:  1831 		printf("%s\n", value);  1832 		break;  1833 	case PRINT_VALUES:  1834 		for (cpp = values; *cpp; cpp++)  1835 			printf("%s\n", *cpp);  1836 		profile_free_list(values);  1837 		break;  1838 	}  1839 }  1840   1841 static void do_batchmode(profile_t profile)  1842 {  1843 	int		argc, ret;  1844 	char		**argv;  1845 	char		buf[256];  1846   1847 	while (!feof(stdin)) {  1848 		if (fgets(buf, sizeof(buf), stdin) == NULL)  1849 			break;  1850 		printf(">%s", buf);  1851 		ret = argv_parse(buf, &argc, &argv);  1852 		if (ret != 0) {  1853 			printf("Argv_parse returned %d!\n", ret);  1854 			continue;  1855 		}  1856 		do_cmd(profile, argv);  1857 		printf("\n");  1858 		argv_free(argv);  1859 	}  1860 	profile_release(profile);  1861 	exit(0);  1862   1863 }  1864   1865 void syntax_err_report(const char *filename, long err, int line_num)  1866 {  1867 	fprintf(stderr, "Syntax error in %s, line number %d: %s\n",  1868 		filename, line_num, error_message(err));  1869 	exit(1);  1870 }  1871   1872 const char *default_str = "[foo]\n\tbar=quux\n\tsub = {\n\t\twin = true\n}\n";  1873   1874 int main(int argc, char **argv)  1875 {  1876     profile_t	profile;  1877     long	retval;  1878     char	*cmd;  1879   1880     if (argc < 2) {  1881 	    fprintf(stderr, "Usage: %s filename [cmd argset]\n", program_name);  1882 	    exit(1);  1883     }  1884   1885     initialize_prof_error_table();  1886   1887     profile_set_syntax_err_cb(syntax_err_report);  1888   1889     retval = profile_init_path(argv[1], &profile);  1890     if (retval) {  1891 	com_err(program_name, retval, "while initializing profile");  1892 	exit(1);  1893     }  1894     retval = profile_set_default(profile, default_str);  1895     if (retval) {  1896 	com_err(program_name, retval, "while setting default");  1897 	exit(1);  1898     }  1899   1900     cmd = *(argv+2);  1901     if (!cmd || !strcmp(cmd, "batch"))  1902 	    do_batchmode(profile);  1903     else  1904 	    do_cmd(profile, argv+2);  1905     profile_release(profile);  1906   1907     return 0;  1908 }  1909   1910 #endif