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