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