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