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 
42 struct profile_string_list {
43 	char	**list;
44 	int	num;
45 	int	max;
46 };
47 
48 /*
49  * Initialize the string list abstraction.
50  */
init_list(struct profile_string_list * list)51 static 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  */
end_list(struct profile_string_list * list,char *** ret_list)66 static 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  */
add_to_list(struct profile_string_list * list,char * str)88 static 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  */
is_list_member(struct profile_string_list * list,const char * str)110 static 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  */
profile_free_list(char ** list)128 void 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 
140 errcode_t
profile_get_values(profile_t profile,const char * const * names,char *** ret_values)141 profile_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 
172 cleanup:
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  */
181 errcode_t
profile_get_subsection_names(profile_t profile,const char ** names,char *** ret_names)182 profile_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 
208 cleanup:
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  */
217 errcode_t
profile_get_relation_names(profile_t profile,const char ** names,char *** ret_names)218 profile_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 
248 cleanup:
249 	end_list(&values, 0);
250 	return retval;
251 }
252 
253 
254 void
profile_release_string(char * str)255 profile_release_string(char *str)
256 {
257 	free(str);
258 }
259 
260 errcode_t
profile_init_path(const char * filepath,profile_t * ret_profile)261 profile_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