paul@181 | 1 | /* |
paul@181 | 2 | * profile_helpers.c -- Helper functions for the profile library |
paul@181 | 3 | * |
paul@181 | 4 | * These functions are not part of the "core" profile library, and do |
paul@181 | 5 | * not require access to the internal functions and data structures of |
paul@181 | 6 | * the profile library. They are mainly convenience functions for |
paul@181 | 7 | * programs that want to do something unusual such as obtaining the |
paul@181 | 8 | * list of sections or relations, or accessing multiple values from a |
paul@181 | 9 | * relation that is listed more than once. This functionality can all |
paul@181 | 10 | * be done using the profile_iterator abstraction, but it is less |
paul@181 | 11 | * convenient. |
paul@181 | 12 | * |
paul@181 | 13 | * Copyright (C) 2006 by Theodore Ts'o. |
paul@181 | 14 | * |
paul@181 | 15 | * %Begin-Header% |
paul@181 | 16 | * This file may be redistributed under the terms of the GNU Public |
paul@181 | 17 | * License. |
paul@181 | 18 | * %End-Header% |
paul@181 | 19 | */ |
paul@181 | 20 | |
paul@181 | 21 | #include "config.h" |
paul@181 | 22 | #include <stdlib.h> |
paul@181 | 23 | #include <string.h> |
paul@181 | 24 | #include <errno.h> |
paul@181 | 25 | |
paul@181 | 26 | #include <et/com_err.h> |
paul@181 | 27 | #include "profile.h" |
paul@181 | 28 | #include "profile_helpers.h" |
paul@181 | 29 | #include "prof_err.h" |
paul@181 | 30 | |
paul@181 | 31 | /* |
paul@181 | 32 | * These functions --- init_list(), end_list(), and add_to_list() are |
paul@181 | 33 | * internal functions used to build up a null-terminated char ** list |
paul@181 | 34 | * of strings to be returned by functions like profile_get_values. |
paul@181 | 35 | * |
paul@181 | 36 | * The profile_string_list structure is used for internal booking |
paul@181 | 37 | * purposes to build up the list, which is returned in *ret_list by |
paul@181 | 38 | * the end_list() function. |
paul@181 | 39 | * |
paul@181 | 40 | * The publicly exported interface for freeing char** list is |
paul@181 | 41 | * profile_free_list(). |
paul@181 | 42 | */ |
paul@181 | 43 | |
paul@181 | 44 | struct profile_string_list { |
paul@181 | 45 | char **list; |
paul@181 | 46 | int num; |
paul@181 | 47 | int max; |
paul@181 | 48 | }; |
paul@181 | 49 | |
paul@181 | 50 | /* |
paul@181 | 51 | * Initialize the string list abstraction. |
paul@181 | 52 | */ |
paul@181 | 53 | static errcode_t init_list(struct profile_string_list *list) |
paul@181 | 54 | { |
paul@181 | 55 | list->num = 0; |
paul@181 | 56 | list->max = 10; |
paul@181 | 57 | list->list = malloc(list->max * sizeof(char *)); |
paul@181 | 58 | if (list->list == 0) |
paul@181 | 59 | return ENOMEM; |
paul@181 | 60 | list->list[0] = 0; |
paul@181 | 61 | return 0; |
paul@181 | 62 | } |
paul@181 | 63 | |
paul@181 | 64 | /* |
paul@181 | 65 | * Free any memory left over in the string abstraction, returning the |
paul@181 | 66 | * built up list in *ret_list if it is non-null. |
paul@181 | 67 | */ |
paul@181 | 68 | static void end_list(struct profile_string_list *list, char ***ret_list) |
paul@181 | 69 | { |
paul@181 | 70 | char **cp; |
paul@181 | 71 | |
paul@181 | 72 | if (list == 0) |
paul@181 | 73 | return; |
paul@181 | 74 | |
paul@181 | 75 | if (ret_list) { |
paul@181 | 76 | *ret_list = list->list; |
paul@181 | 77 | return; |
paul@181 | 78 | } else { |
paul@181 | 79 | for (cp = list->list; *cp; cp++) |
paul@181 | 80 | free(*cp); |
paul@181 | 81 | free(list->list); |
paul@181 | 82 | } |
paul@181 | 83 | list->num = list->max = 0; |
paul@181 | 84 | list->list = 0; |
paul@181 | 85 | } |
paul@181 | 86 | |
paul@181 | 87 | /* |
paul@181 | 88 | * Add a string to the list. |
paul@181 | 89 | */ |
paul@181 | 90 | static errcode_t add_to_list(struct profile_string_list *list, char *str) |
paul@181 | 91 | { |
paul@181 | 92 | char **newlist; |
paul@181 | 93 | int newmax; |
paul@181 | 94 | |
paul@181 | 95 | if (list->num+1 >= list->max) { |
paul@181 | 96 | newmax = list->max + 10; |
paul@181 | 97 | newlist = realloc(list->list, newmax * sizeof(char *)); |
paul@181 | 98 | if (newlist == 0) |
paul@181 | 99 | return ENOMEM; |
paul@181 | 100 | list->max = newmax; |
paul@181 | 101 | list->list = newlist; |
paul@181 | 102 | } |
paul@181 | 103 | |
paul@181 | 104 | list->list[list->num++] = str; |
paul@181 | 105 | list->list[list->num] = 0; |
paul@181 | 106 | return 0; |
paul@181 | 107 | } |
paul@181 | 108 | |
paul@181 | 109 | /* |
paul@181 | 110 | * Return TRUE if the string is already a member of the list. |
paul@181 | 111 | */ |
paul@181 | 112 | static int is_list_member(struct profile_string_list *list, const char *str) |
paul@181 | 113 | { |
paul@181 | 114 | char **cpp; |
paul@181 | 115 | |
paul@181 | 116 | if (!list->list) |
paul@181 | 117 | return 0; |
paul@181 | 118 | |
paul@181 | 119 | for (cpp = list->list; *cpp; cpp++) { |
paul@181 | 120 | if (!strcmp(*cpp, str)) |
paul@181 | 121 | return 1; |
paul@181 | 122 | } |
paul@181 | 123 | return 0; |
paul@181 | 124 | } |
paul@181 | 125 | |
paul@181 | 126 | /* |
paul@181 | 127 | * This function frees a null-terminated list as returned by |
paul@181 | 128 | * profile_get_values. |
paul@181 | 129 | */ |
paul@181 | 130 | void profile_free_list(char **list) |
paul@181 | 131 | { |
paul@181 | 132 | char **cp; |
paul@181 | 133 | |
paul@181 | 134 | if (list == 0) |
paul@181 | 135 | return; |
paul@181 | 136 | |
paul@181 | 137 | for (cp = list; *cp; cp++) |
paul@181 | 138 | free(*cp); |
paul@181 | 139 | free(list); |
paul@181 | 140 | } |
paul@181 | 141 | |
paul@181 | 142 | errcode_t |
paul@181 | 143 | profile_get_values(profile_t profile, const char *const *names, |
paul@181 | 144 | char ***ret_values) |
paul@181 | 145 | { |
paul@181 | 146 | errcode_t retval; |
paul@181 | 147 | void *state; |
paul@181 | 148 | char *value; |
paul@181 | 149 | struct profile_string_list values; |
paul@181 | 150 | |
paul@181 | 151 | if ((retval = profile_iterator_create(profile, names, |
paul@181 | 152 | PROFILE_ITER_RELATIONS_ONLY, |
paul@181 | 153 | &state))) |
paul@181 | 154 | return retval; |
paul@181 | 155 | |
paul@181 | 156 | if ((retval = init_list(&values))) |
paul@181 | 157 | goto cleanup_iterator; |
paul@181 | 158 | |
paul@181 | 159 | do { |
paul@181 | 160 | if ((retval = profile_iterator(&state, 0, &value))) |
paul@181 | 161 | goto cleanup; |
paul@181 | 162 | if (value) |
paul@181 | 163 | add_to_list(&values, value); |
paul@181 | 164 | } while (state); |
paul@181 | 165 | |
paul@181 | 166 | if (values.num == 0) { |
paul@181 | 167 | retval = PROF_NO_RELATION; |
paul@181 | 168 | goto cleanup; |
paul@181 | 169 | } |
paul@181 | 170 | |
paul@181 | 171 | end_list(&values, ret_values); |
paul@181 | 172 | return 0; |
paul@181 | 173 | |
paul@181 | 174 | cleanup: |
paul@181 | 175 | end_list(&values, 0); |
paul@181 | 176 | cleanup_iterator: |
paul@181 | 177 | profile_iterator_free(&state); |
paul@181 | 178 | return retval; |
paul@181 | 179 | } |
paul@181 | 180 | |
paul@181 | 181 | /* |
paul@181 | 182 | * This function will return the list of the names of subsections in the |
paul@181 | 183 | * under the specified section name. |
paul@181 | 184 | */ |
paul@181 | 185 | errcode_t |
paul@181 | 186 | profile_get_subsection_names(profile_t profile, const char **names, |
paul@181 | 187 | char ***ret_names) |
paul@181 | 188 | { |
paul@181 | 189 | errcode_t retval; |
paul@181 | 190 | void *state; |
paul@181 | 191 | char *name; |
paul@181 | 192 | struct profile_string_list values; |
paul@181 | 193 | |
paul@181 | 194 | if ((retval = profile_iterator_create(profile, names, |
paul@181 | 195 | PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY, |
paul@181 | 196 | &state))) |
paul@181 | 197 | return retval; |
paul@181 | 198 | |
paul@181 | 199 | if ((retval = init_list(&values))) |
paul@181 | 200 | goto cleanup_iterator; |
paul@181 | 201 | |
paul@181 | 202 | do { |
paul@181 | 203 | if ((retval = profile_iterator(&state, &name, 0))) |
paul@181 | 204 | goto cleanup; |
paul@181 | 205 | if (name) |
paul@181 | 206 | add_to_list(&values, name); |
paul@181 | 207 | } while (state); |
paul@181 | 208 | |
paul@181 | 209 | end_list(&values, ret_names); |
paul@181 | 210 | return 0; |
paul@181 | 211 | |
paul@181 | 212 | cleanup: |
paul@181 | 213 | end_list(&values, 0); |
paul@181 | 214 | cleanup_iterator: |
paul@181 | 215 | profile_iterator_free(&state); |
paul@181 | 216 | return retval; |
paul@181 | 217 | } |
paul@181 | 218 | |
paul@181 | 219 | /* |
paul@181 | 220 | * This function will return the list of the names of relations in the |
paul@181 | 221 | * under the specified section name. |
paul@181 | 222 | */ |
paul@181 | 223 | errcode_t |
paul@181 | 224 | profile_get_relation_names(profile_t profile, const char **names, |
paul@181 | 225 | char ***ret_names) |
paul@181 | 226 | { |
paul@181 | 227 | errcode_t retval; |
paul@181 | 228 | void *state; |
paul@181 | 229 | char *name; |
paul@181 | 230 | struct profile_string_list values; |
paul@181 | 231 | |
paul@181 | 232 | if ((retval = profile_iterator_create(profile, names, |
paul@181 | 233 | PROFILE_ITER_LIST_SECTION | PROFILE_ITER_RELATIONS_ONLY, |
paul@181 | 234 | &state))) |
paul@181 | 235 | return retval; |
paul@181 | 236 | |
paul@181 | 237 | if ((retval = init_list(&values))) |
paul@181 | 238 | goto cleanup_iterator; |
paul@181 | 239 | |
paul@181 | 240 | do { |
paul@181 | 241 | if ((retval = profile_iterator(&state, &name, 0))) |
paul@181 | 242 | goto cleanup; |
paul@181 | 243 | if (name) { |
paul@181 | 244 | if (is_list_member(&values, name)) |
paul@181 | 245 | free(name); |
paul@181 | 246 | else |
paul@181 | 247 | add_to_list(&values, name); |
paul@181 | 248 | } |
paul@181 | 249 | } while (state); |
paul@181 | 250 | |
paul@181 | 251 | end_list(&values, ret_names); |
paul@181 | 252 | return 0; |
paul@181 | 253 | |
paul@181 | 254 | cleanup: |
paul@181 | 255 | end_list(&values, 0); |
paul@181 | 256 | cleanup_iterator: |
paul@181 | 257 | profile_iterator_free(&state); |
paul@181 | 258 | return retval; |
paul@181 | 259 | } |
paul@181 | 260 | |
paul@181 | 261 | |
paul@181 | 262 | void |
paul@181 | 263 | profile_release_string(char *str) |
paul@181 | 264 | { |
paul@181 | 265 | free(str); |
paul@181 | 266 | } |
paul@181 | 267 | |
paul@181 | 268 | errcode_t |
paul@181 | 269 | profile_init_path(const char * filepath, |
paul@181 | 270 | profile_t *ret_profile) |
paul@181 | 271 | { |
paul@181 | 272 | int n_entries, i; |
paul@181 | 273 | unsigned int ent_len; |
paul@181 | 274 | const char *s, *t; |
paul@181 | 275 | char **filenames; |
paul@181 | 276 | errcode_t retval; |
paul@181 | 277 | |
paul@181 | 278 | /* count the distinct filename components */ |
paul@181 | 279 | for(s = filepath, n_entries = 1; *s; s++) { |
paul@181 | 280 | if (*s == ':') |
paul@181 | 281 | n_entries++; |
paul@181 | 282 | } |
paul@181 | 283 | |
paul@181 | 284 | /* the array is NULL terminated */ |
paul@181 | 285 | filenames = (char **) malloc((n_entries+1) * sizeof(char*)); |
paul@181 | 286 | if (filenames == 0) |
paul@181 | 287 | return ENOMEM; |
paul@181 | 288 | |
paul@181 | 289 | /* measure, copy, and skip each one */ |
paul@181 | 290 | for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) { |
paul@181 | 291 | ent_len = t-s; |
paul@181 | 292 | filenames[i] = (char*) malloc(ent_len + 1); |
paul@181 | 293 | if (filenames[i] == 0) { |
paul@181 | 294 | /* if malloc fails, free the ones that worked */ |
paul@181 | 295 | while(--i >= 0) free(filenames[i]); |
paul@181 | 296 | free(filenames); |
paul@181 | 297 | return ENOMEM; |
paul@181 | 298 | } |
paul@181 | 299 | strncpy(filenames[i], s, ent_len); |
paul@181 | 300 | filenames[i][ent_len] = 0; |
paul@181 | 301 | if (*t == 0) { |
paul@181 | 302 | i++; |
paul@181 | 303 | break; |
paul@181 | 304 | } |
paul@181 | 305 | } |
paul@181 | 306 | /* cap the array */ |
paul@181 | 307 | filenames[i] = 0; |
paul@181 | 308 | |
paul@181 | 309 | retval = profile_init((const char * const *) filenames, |
paul@181 | 310 | ret_profile); |
paul@181 | 311 | |
paul@181 | 312 | /* count back down and free the entries */ |
paul@181 | 313 | while(--i >= 0) free(filenames[i]); |
paul@181 | 314 | free(filenames); |
paul@181 | 315 | |
paul@181 | 316 | return retval; |
paul@181 | 317 | } |