/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * prof_get.c --- routines that expose the public interfaces for * querying items from the profile. * */ #include "prof_int.h" #include #include #ifdef HAVE_STDLIB_H #include #endif #include #include /* * 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; unsigned int num; unsigned 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, const char *str) { char *newstr, **newlist; unsigned 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; } newstr = strdup(str); if (newstr == 0) return ENOMEM; list->list[list->num++] = newstr; 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 KRB5_CALLCONV profile_free_list(char **list) { char **cp; if (list == 0) return; for (cp = list; *cp; cp++) free(*cp); free(list); } /* Look up a relation in a vtable profile. */ static errcode_t get_values_vt(profile_t profile, const char *const *names, char ***ret_values) { errcode_t retval; char **vtvalues, **val; struct profile_string_list values; retval = profile->vt->get_values(profile->cbdata, names, &vtvalues); if (retval) return retval; /* Copy the result into memory we can free. */ retval = init_list(&values); if (retval == 0) { for (val = vtvalues; *val; val++) add_to_list(&values, *val); end_list(&values, ret_values); } profile->vt->free_values(profile->cbdata, vtvalues); return retval; } errcode_t KRB5_CALLCONV 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; *ret_values = NULL; if (!profile) return PROF_NO_PROFILE; if (profile->vt) return get_values_vt(profile, names, ret_values); if ((retval = profile_node_iterator_create(profile, names, PROFILE_ITER_RELATIONS_ONLY, &state))) return retval; if ((retval = init_list(&values))) return retval; do { if ((retval = profile_node_iterator(&state, 0, 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); return retval; } /* Look up a relation in a vtable profile and return the first value in the * result. */ static errcode_t get_value_vt(profile_t profile, const char *const *names, char **ret_value) { errcode_t retval; char **vtvalues; retval = profile->vt->get_values(profile->cbdata, names, &vtvalues); if (retval) return retval; *ret_value = strdup(*vtvalues); if (*ret_value == NULL) retval = ENOMEM; profile->vt->free_values(profile->cbdata, vtvalues); return retval; } /* * This function only gets the first value from the file; it is a * helper function for profile_get_string, profile_get_integer, etc. */ errcode_t profile_get_value(profile_t profile, const char **names, char **ret_value) { errcode_t retval; void *state; char *value; *ret_value = NULL; if (!profile) return PROF_NO_PROFILE; if (profile->vt) return get_value_vt(profile, names, ret_value); retval = profile_iterator_create(profile, names, PROFILE_ITER_RELATIONS_ONLY, &state); if (retval) return retval; retval = profile_iterator(&state, NULL, &value); if (retval) goto cleanup; if (value) *ret_value = value; else retval = PROF_NO_RELATION; cleanup: profile_iterator_free(&state); return retval; } errcode_t KRB5_CALLCONV profile_get_string(profile_t profile, const char *name, const char *subname, const char *subsubname, const char *def_val, char **ret_string) { char *value; errcode_t retval; const char *names[4]; if (profile) { names[0] = name; names[1] = subname; names[2] = subsubname; names[3] = 0; retval = profile_get_value(profile, names, &value); if (retval == 0) { *ret_string = value; return 0; } else if (retval != PROF_NO_SECTION && retval != PROF_NO_RELATION) return retval; } if (def_val) { *ret_string = strdup(def_val); if (*ret_string == NULL) return ENOMEM; } else *ret_string = NULL; return 0; } static errcode_t parse_int(const char *value, int *ret_int) { char *end_value; long ret_long; if (value[0] == 0) /* Empty string is no good. */ return PROF_BAD_INTEGER; errno = 0; ret_long = strtol(value, &end_value, 10); /* Overflow or underflow. */ if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0) return PROF_BAD_INTEGER; /* Value outside "int" range. */ if ((long) (int) ret_long != ret_long) return PROF_BAD_INTEGER; /* Garbage in string. */ if (end_value != value + strlen (value)) return PROF_BAD_INTEGER; *ret_int = ret_long; return 0; } errcode_t KRB5_CALLCONV profile_get_integer(profile_t profile, const char *name, const char *subname, const char *subsubname, int def_val, int *ret_int) { char *value; errcode_t retval; const char *names[4]; *ret_int = def_val; if (profile == 0) return 0; names[0] = name; names[1] = subname; names[2] = subsubname; names[3] = 0; retval = profile_get_value(profile, names, &value); if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { *ret_int = def_val; return 0; } else if (retval) return retval; retval = parse_int(value, ret_int); free(value); return retval; } static const char *const conf_yes[] = { "y", "yes", "true", "t", "1", "on", 0, }; static const char *const conf_no[] = { "n", "no", "false", "nil", "0", "off", 0, }; static errcode_t profile_parse_boolean(const char *s, int *ret_boolean) { const char *const *p; if (ret_boolean == NULL) return PROF_EINVAL; for(p=conf_yes; *p; p++) { if (!strcasecmp(*p,s)) { *ret_boolean = 1; return 0; } } for(p=conf_no; *p; p++) { if (!strcasecmp(*p,s)) { *ret_boolean = 0; return 0; } } return PROF_BAD_BOOLEAN; } errcode_t KRB5_CALLCONV profile_get_boolean(profile_t profile, const char *name, const char *subname, const char *subsubname, int def_val, int *ret_boolean) { char *value; errcode_t retval; const char *names[4]; if (profile == 0) { *ret_boolean = def_val; return 0; } names[0] = name; names[1] = subname; names[2] = subsubname; names[3] = 0; retval = profile_get_value(profile, names, &value); if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { *ret_boolean = def_val; return 0; } else if (retval) return retval; retval = profile_parse_boolean(value, ret_boolean); free(value); return retval; } /* * This function will return the list of the names of subections in the * under the specified section name. */ errcode_t KRB5_CALLCONV 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))) return retval; do { if ((retval = profile_iterator(&state, &name, NULL))) goto cleanup; if (name) add_to_list(&values, name); free(name); } while (state); end_list(&values, ret_names); return 0; cleanup: end_list(&values, 0); return retval; } /* * This function will return the list of the names of relations in the * under the specified section name. */ errcode_t KRB5_CALLCONV 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))) return retval; do { if ((retval = profile_iterator(&state, &name, NULL))) goto cleanup; if (name && !is_list_member(&values, name)) add_to_list(&values, name); free(name); } while (state); end_list(&values, ret_names); return 0; cleanup: end_list(&values, 0); return retval; } struct profile_iterator { prf_magic_t magic; profile_t profile; void *idata; }; errcode_t KRB5_CALLCONV profile_iterator_create(profile_t profile, const char *const *names, int flags, void **ret_iter) { struct profile_iterator *iter; errcode_t retval; *ret_iter = NULL; if (!profile) return PROF_NO_PROFILE; iter = malloc(sizeof(*iter)); if (iter == NULL) return ENOMEM; iter->magic = PROF_MAGIC_ITERATOR; iter->profile = profile; /* Create the underlying iterator representation using the vtable or the * built-in node iterator. */ if (profile->vt) { if (!profile->vt->iterator_create) retval = PROF_UNSUPPORTED; else retval = profile->vt->iterator_create(profile->cbdata, names, flags, &iter->idata); } else { retval = profile_node_iterator_create(profile, names, flags, &iter->idata); } if (retval) { free(iter); return retval; } *ret_iter = iter; return 0; } void KRB5_CALLCONV profile_iterator_free(void **iter_p) { struct profile_iterator *iter; profile_t profile; if (!iter_p) return; iter = *iter_p; if (!iter || iter->magic != PROF_MAGIC_ITERATOR) return; profile = iter->profile; if (profile->vt) profile->vt->iterator_free(profile->cbdata, iter->idata); else profile_node_iterator_free(&iter->idata); free(iter); *iter_p = NULL; } /* Make copies of name and value into *ret_name and *ret_value. Handle null * values of any argument. */ static errcode_t set_results(const char *name, const char *value, char **ret_name, char **ret_value) { char *name_copy = NULL, *value_copy = NULL; if (ret_name && name) { name_copy = strdup(name); if (name_copy == NULL) goto oom; } if (ret_value && value) { value_copy = strdup(value); if (value_copy == NULL) goto oom; } if (ret_name) *ret_name = name_copy; if (ret_value) *ret_value = value_copy; return 0; oom: free(name_copy); free(value_copy); return ENOMEM; } errcode_t KRB5_CALLCONV profile_iterator(void **iter_p, char **ret_name, char **ret_value) { char *name, *value; errcode_t retval; struct profile_iterator *iter = *iter_p; profile_t profile; if (ret_name) *ret_name = NULL; if (ret_value) *ret_value = NULL; if (iter == NULL || iter->magic != PROF_MAGIC_ITERATOR) return PROF_MAGIC_ITERATOR; profile = iter->profile; if (profile->vt) { retval = profile->vt->iterator(profile->cbdata, iter->idata, &name, &value); if (retval) return retval; if (name == NULL) { profile->vt->iterator_free(profile->cbdata, iter->idata); free(iter); *iter_p = NULL; } retval = set_results(name, value, ret_name, ret_value); if (name) profile->vt->free_string(profile->cbdata, name); if (value) profile->vt->free_string(profile->cbdata, value); return retval; } retval = profile_node_iterator(&iter->idata, 0, &name, &value); if (iter->idata == NULL) { free(iter); *iter_p = NULL; } if (retval) return retval; return set_results(name, value, ret_name, ret_value); } void KRB5_CALLCONV profile_release_string(char *str) { free(str); }