318 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			318 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * profile_helpers.c -- Helper functions for the profile library
 | |
|  *
 | |
|  * These functions are not part of the "core" profile library, and do
 | |
|  * not require access to the internal functions and data structures of
 | |
|  * the profile library.  They are mainly convenience functions for
 | |
|  * programs that want to do something unusual such as obtaining the
 | |
|  * list of sections or relations, or accessing multiple values from a
 | |
|  * relation that is listed more than once.  This functionality can all
 | |
|  * be done using the profile_iterator abstraction, but it is less
 | |
|  * convenient.
 | |
|  *
 | |
|  * Copyright (C) 2006 by Theodore Ts'o.
 | |
|  *
 | |
|  * %Begin-Header%
 | |
|  * This file may be redistributed under the terms of the GNU Public
 | |
|  * License.
 | |
|  * %End-Header%
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| 
 | |
| #include <et/com_err.h>
 | |
| #include "profile.h"
 | |
| #include "profile_helpers.h"
 | |
| #include "prof_err.h"
 | |
| 
 | |
| /*
 | |
|  * These functions --- init_list(), end_list(), and add_to_list() are
 | |
|  * internal functions used to build up a null-terminated char ** list
 | |
|  * of strings to be returned by functions like profile_get_values.
 | |
|  *
 | |
|  * The profile_string_list structure is used for internal booking
 | |
|  * purposes to build up the list, which is returned in *ret_list by
 | |
|  * the end_list() function.
 | |
|  *
 | |
|  * The publicly exported interface for freeing char** list is
 | |
|  * profile_free_list().
 | |
|  */
 | |
| 
 | |
| struct profile_string_list {
 | |
| 	char	**list;
 | |
| 	int	num;
 | |
| 	int	max;
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Initialize the string list abstraction.
 | |
|  */
 | |
| static errcode_t init_list(struct profile_string_list *list)
 | |
| {
 | |
| 	list->num = 0;
 | |
| 	list->max = 10;
 | |
| 	list->list = malloc(list->max * sizeof(char *));
 | |
| 	if (list->list == 0)
 | |
| 		return ENOMEM;
 | |
| 	list->list[0] = 0;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Free any memory left over in the string abstraction, returning the
 | |
|  * built up list in *ret_list if it is non-null.
 | |
|  */
 | |
| static void end_list(struct profile_string_list *list, char ***ret_list)
 | |
| {
 | |
| 	char	**cp;
 | |
| 
 | |
| 	if (list == 0)
 | |
| 		return;
 | |
| 
 | |
| 	if (ret_list) {
 | |
| 		*ret_list = list->list;
 | |
| 		return;
 | |
| 	} else {
 | |
| 		for (cp = list->list; *cp; cp++)
 | |
| 			free(*cp);
 | |
| 		free(list->list);
 | |
| 	}
 | |
| 	list->num = list->max = 0;
 | |
| 	list->list = 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Add a string to the list.
 | |
|  */
 | |
| static errcode_t add_to_list(struct profile_string_list *list, char *str)
 | |
| {
 | |
| 	char 	**newlist;
 | |
| 	int	newmax;
 | |
| 
 | |
| 	if (list->num+1 >= list->max) {
 | |
| 		newmax = list->max + 10;
 | |
| 		newlist = realloc(list->list, newmax * sizeof(char *));
 | |
| 		if (newlist == 0)
 | |
| 			return ENOMEM;
 | |
| 		list->max = newmax;
 | |
| 		list->list = newlist;
 | |
| 	}
 | |
| 
 | |
| 	list->list[list->num++] = str;
 | |
| 	list->list[list->num] = 0;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Return TRUE if the string is already a member of the list.
 | |
|  */
 | |
| static int is_list_member(struct profile_string_list *list, const char *str)
 | |
| {
 | |
| 	char **cpp;
 | |
| 
 | |
| 	if (!list->list)
 | |
| 		return 0;
 | |
| 
 | |
| 	for (cpp = list->list; *cpp; cpp++) {
 | |
| 		if (!strcmp(*cpp, str))
 | |
| 			return 1;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This function frees a null-terminated list as returned by
 | |
|  * profile_get_values.
 | |
|  */
 | |
| void profile_free_list(char **list)
 | |
| {
 | |
|     char	**cp;
 | |
| 
 | |
|     if (list == 0)
 | |
| 	    return;
 | |
| 
 | |
|     for (cp = list; *cp; cp++)
 | |
| 	free(*cp);
 | |
|     free(list);
 | |
| }
 | |
| 
 | |
| errcode_t
 | |
| profile_get_values(profile_t profile, const char *const *names,
 | |
| 		   char ***ret_values)
 | |
| {
 | |
| 	errcode_t		retval;
 | |
| 	void			*state;
 | |
| 	char			*value;
 | |
| 	struct profile_string_list values;
 | |
| 
 | |
| 	if ((retval = profile_iterator_create(profile, names,
 | |
| 					      PROFILE_ITER_RELATIONS_ONLY,
 | |
| 					      &state)))
 | |
| 		return retval;
 | |
| 
 | |
| 	if ((retval = init_list(&values)))
 | |
| 		goto cleanup_iterator;
 | |
| 
 | |
| 	do {
 | |
| 		if ((retval = profile_iterator(&state, 0, &value)))
 | |
| 			goto cleanup;
 | |
| 		if (value)
 | |
| 			add_to_list(&values, value);
 | |
| 	} while (state);
 | |
| 
 | |
| 	if (values.num == 0) {
 | |
| 		retval = PROF_NO_RELATION;
 | |
| 		goto cleanup;
 | |
| 	}
 | |
| 
 | |
| 	end_list(&values, ret_values);
 | |
| 	return 0;
 | |
| 
 | |
| cleanup:
 | |
| 	end_list(&values, 0);
 | |
| cleanup_iterator:
 | |
| 	profile_iterator_free(&state);
 | |
| 	return retval;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This function will return the list of the names of subsections in the
 | |
|  * under the specified section name.
 | |
|  */
 | |
| errcode_t
 | |
| profile_get_subsection_names(profile_t profile, const char **names,
 | |
| 			     char ***ret_names)
 | |
| {
 | |
| 	errcode_t		retval;
 | |
| 	void			*state;
 | |
| 	char			*name;
 | |
| 	struct profile_string_list values;
 | |
| 
 | |
| 	if ((retval = profile_iterator_create(profile, names,
 | |
| 		   PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY,
 | |
| 		   &state)))
 | |
| 		return retval;
 | |
| 
 | |
| 	if ((retval = init_list(&values)))
 | |
| 		goto cleanup_iterator;
 | |
| 
 | |
| 	do {
 | |
| 		if ((retval = profile_iterator(&state, &name, 0)))
 | |
| 			goto cleanup;
 | |
| 		if (name)
 | |
| 			add_to_list(&values, name);
 | |
| 	} while (state);
 | |
| 
 | |
| 	end_list(&values, ret_names);
 | |
| 	return 0;
 | |
| 
 | |
| cleanup:
 | |
| 	end_list(&values, 0);
 | |
| cleanup_iterator:
 | |
| 	profile_iterator_free(&state);
 | |
| 	return retval;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This function will return the list of the names of relations in the
 | |
|  * under the specified section name.
 | |
|  */
 | |
| errcode_t
 | |
| profile_get_relation_names(profile_t profile, const char **names,
 | |
| 			   char ***ret_names)
 | |
| {
 | |
| 	errcode_t		retval;
 | |
| 	void			*state;
 | |
| 	char			*name;
 | |
| 	struct profile_string_list values;
 | |
| 
 | |
| 	if ((retval = profile_iterator_create(profile, names,
 | |
| 		   PROFILE_ITER_LIST_SECTION | PROFILE_ITER_RELATIONS_ONLY,
 | |
| 		   &state)))
 | |
| 		return retval;
 | |
| 
 | |
| 	if ((retval = init_list(&values)))
 | |
| 		goto cleanup_iterator;
 | |
| 
 | |
| 	do {
 | |
| 		if ((retval = profile_iterator(&state, &name, 0)))
 | |
| 			goto cleanup;
 | |
| 		if (name) {
 | |
| 			if (is_list_member(&values, name))
 | |
| 				free(name);
 | |
| 			else
 | |
| 				add_to_list(&values, name);
 | |
| 		}
 | |
| 	} while (state);
 | |
| 
 | |
| 	end_list(&values, ret_names);
 | |
| 	return 0;
 | |
| 
 | |
| cleanup:
 | |
| 	end_list(&values, 0);
 | |
| cleanup_iterator:
 | |
| 	profile_iterator_free(&state);
 | |
| 	return retval;
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| profile_release_string(char *str)
 | |
| {
 | |
| 	free(str);
 | |
| }
 | |
| 
 | |
| errcode_t
 | |
| profile_init_path(const char * filepath,
 | |
| 		  profile_t *ret_profile)
 | |
| {
 | |
| 	int n_entries, i;
 | |
| 	unsigned int ent_len;
 | |
| 	const char *s, *t;
 | |
| 	char **filenames;
 | |
| 	errcode_t retval;
 | |
| 
 | |
| 	/* count the distinct filename components */
 | |
| 	for(s = filepath, n_entries = 1; *s; s++) {
 | |
| 		if (*s == ':')
 | |
| 			n_entries++;
 | |
| 	}
 | |
| 
 | |
| 	/* the array is NULL terminated */
 | |
| 	filenames = (char **) malloc((n_entries+1) * sizeof(char*));
 | |
| 	if (filenames == 0)
 | |
| 		return ENOMEM;
 | |
| 
 | |
| 	/* measure, copy, and skip each one */
 | |
| 	for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) {
 | |
| 		ent_len = t-s;
 | |
| 		filenames[i] = (char*) malloc(ent_len + 1);
 | |
| 		if (filenames[i] == 0) {
 | |
| 			/* if malloc fails, free the ones that worked */
 | |
| 			while(--i >= 0) free(filenames[i]);
 | |
|                         free(filenames);
 | |
| 			return ENOMEM;
 | |
| 		}
 | |
| 		strncpy(filenames[i], s, ent_len);
 | |
| 		filenames[i][ent_len] = 0;
 | |
| 		if (*t == 0) {
 | |
| 			i++;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	/* cap the array */
 | |
| 	filenames[i] = 0;
 | |
| 
 | |
| 	retval = profile_init((const char * const *) filenames,
 | |
| 			      ret_profile);
 | |
| 
 | |
| 	/* count back down and free the entries */
 | |
| 	while(--i >= 0) free(filenames[i]);
 | |
| 	free(filenames);
 | |
| 
 | |
| 	return retval;
 | |
| }
 |