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 subsections 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