/* * 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 subections 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; }