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
44struct profile_string_list {
45	char	**list;
46	int	num;
47	int	max;
48};
49
50/*
51 * Initialize the string list abstraction.
52 */
53static 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 */
68static 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 */
90static 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 */
112static 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 */
130void 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
142errcode_t
143profile_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
174cleanup:
175	end_list(&values, 0);
176cleanup_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 */
185errcode_t
186profile_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
212cleanup:
213	end_list(&values, 0);
214cleanup_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 */
223errcode_t
224profile_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
254cleanup:
255	end_list(&values, 0);
256cleanup_iterator:
257	profile_iterator_free(&state);
258	return retval;
259}
260
261
262void
263profile_release_string(char *str)
264{
265	free(str);
266}
267
268errcode_t
269profile_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