1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <strings.h>
29#include <secdb.h>
30#include <ctype.h>
31
32/* From libnsl */
33extern char *_strdup_null(char *);
34extern char *_strtok_escape(char *, char *, char **);
35extern char *_strpbrk_escape(char *, char *);
36extern char *_unescape(char *, char *);
37
38char *_do_unescape(char *);
39
40
41/*
42 * kva_match(): Given a key-value array and a key, return a pointer to the
43 * value that matches the key.
44 */
45char *
46kva_match(kva_t *kva, char *key)
47{
48	int	i;
49	kv_t	*data;
50
51	if (kva == NULL || key == NULL) {
52		return (NULL);
53	}
54	data = kva->data;
55	for (i = 0; i < kva->length; i++) {
56		if (strcmp(data[i].key, key) == 0) {
57			return (data[i].value);
58		}
59	}
60
61	return (NULL);
62}
63
64/*
65 * _kva_free(): Free up memory.
66 */
67void
68_kva_free(kva_t *kva)
69{
70	int	i;
71	kv_t	*data;
72
73	if (kva == NULL) {
74		return;
75	}
76	data = kva->data;
77	for (i = 0; i < kva->length; i++) {
78		if (data[i].key != NULL) {
79			free(data[i].key);
80			data[i].key = NULL;
81		}
82		if (data[i].value != NULL) {
83			free(data[i].value);
84			data[i].value = NULL;
85		}
86	}
87	free(kva->data);
88	free(kva);
89}
90
91/*
92 * _kva_free_value(): Free up memory (value) for all the occurrences of
93 * the given key.
94 */
95void
96_kva_free_value(kva_t *kva, char *key)
97{
98	int	ctr;
99	kv_t	*data;
100
101	if (kva == NULL) {
102		return;
103	}
104
105	ctr = kva->length;
106	data = kva->data;
107
108	while (ctr--) {
109		if (strcmp(data->key, key) == 0 && data->value != NULL) {
110			free(data->value);
111			data->value = NULL;
112		}
113		data++;
114	}
115}
116
117/*
118 * new_kva(): Allocate a key-value array.
119 */
120kva_t  *
121_new_kva(int size)
122{
123	kva_t	*new_kva;
124
125	if ((new_kva = (kva_t *)calloc(1, sizeof (kva_t))) == NULL) {
126		return (NULL);
127	}
128	if ((new_kva->data = (kv_t *)calloc(1, (size*sizeof (kv_t)))) == NULL) {
129		free(new_kva);
130		return (NULL);
131	}
132
133	return (new_kva);
134}
135
136/*
137 * _str2kva(): Given a string (s) of key-value pairs, separated by delimeter
138 * (del), place the values into the key value array (nkva).
139 */
140kva_t  *
141_str2kva(char *s, char *ass, char *del)
142{
143	int	n = 0;
144	int	m;
145	int	size = KV_ADD_KEYS;
146	char	*buf;
147	char	*p;
148	char	*pair;
149	char	*key;
150	char	*last_pair;
151	char	*last_key;
152	kv_t	*data;
153	kva_t	*nkva;
154
155	if (s == NULL ||
156	    ass == NULL ||
157	    del == NULL ||
158	    *s == '\0' ||
159	    *s == '\n' ||
160	    (strlen(s) <= 1)) {
161		return (NULL);
162	}
163	p = s;
164	while ((p = _strpbrk_escape(p, ass)) != NULL) {
165		n++;
166		p++;
167	}
168	if (n > size) {
169		m = n/size;
170		if (n%size) {
171			++m;
172		}
173		size = m * KV_ADD_KEYS;
174	}
175	if ((nkva = _new_kva(size)) == NULL) {
176		return (NULL);
177	}
178	data = nkva->data;
179	nkva->length = 0;
180	if ((buf = strdup(s)) == NULL) {
181		return (NULL);
182	}
183	pair = _strtok_escape(buf, del, &last_pair);
184	do {
185		key = _strtok_escape(pair, ass, &last_key);
186		if (key != NULL) {
187			data[nkva->length].key = _do_unescape(key);
188			data[nkva->length].value = _do_unescape(last_key);
189			nkva->length++;
190		}
191	} while ((pair = _strtok_escape(NULL, del, &last_pair)) != NULL);
192	free(buf);
193	return (nkva);
194}
195
196/*
197 * _kva2str(): Given an array of key-value pairs, place them into a string
198 * (buf). Use delimeter (del) to separate pairs.  Use assignment character
199 * (ass) to separate keys and values.
200 *
201 * Return Values: 0  Success 1  Buffer too small
202 */
203int
204_kva2str(kva_t *kva, char *buf, int buflen, char *ass, char *del)
205{
206	int	i;
207	int	len;
208	int	off = 0;
209	kv_t	*data;
210
211	if (kva == NULL) {
212		return (0);
213	}
214
215	buf[0] = '\0';
216	data = kva->data;
217
218	for (i = 0; i < kva->length; i++) {
219		if (data[i].value != NULL) {
220			len = snprintf(buf + off, buflen - off, "%s%s%s%s",
221			    data[i].key, ass, data[i].value, del);
222			if (len < 0 || len + off >= buflen) {
223				return (1);
224			}
225			off += len;
226		}
227	}
228
229	return (0);
230}
231
232int
233_insert2kva(kva_t *kva, char *key, char *value)
234{
235	int	i;
236	kv_t	*data;
237
238	if (kva == NULL) {
239		return (0);
240	}
241	data = kva->data;
242	for (i = 0; i < kva->length; i++) {
243		if (strcmp(data[i].key, key) == 0) {
244			if (data[i].value != NULL)
245				free(data[i].value);
246			data[i].value = _strdup_null(value);
247			return (0);
248		}
249	}
250	return (1);
251}
252
253kva_t  *
254_kva_dup(kva_t *old_kva)
255{
256	int	i;
257	int	size;
258	kv_t	*old_data;
259	kv_t	*new_data;
260	kva_t 	*nkva = NULL;
261
262	if (old_kva == NULL) {
263		return (NULL);
264	}
265	old_data = old_kva->data;
266	size = old_kva->length;
267	if ((nkva = _new_kva(size)) == NULL) {
268		return (NULL);
269	}
270	new_data = nkva->data;
271	nkva->length = old_kva->length;
272	for (i = 0; i < nkva->length; i++) {
273		new_data[i].key = _strdup_null(old_data[i].key);
274		new_data[i].value = _strdup_null(old_data[i].value);
275	}
276
277	return (nkva);
278}
279
280static void
281strip_spaces(char **valuep)
282{
283	char *p, *start;
284
285	/* Find first non-white space character and return pointer to it */
286	for (p = *valuep; *p != '\0' && isspace((unsigned char)*p); p++)
287		;
288
289	*valuep = start = p;
290
291	if (*p == '\0')
292		return;
293
294	p = p + strlen(p) - 1;
295
296	/* Remove trailing spaces */
297	while (p > start && isspace((unsigned char)*p))
298		p--;
299
300	p[1] = '\0';
301}
302
303char *
304_do_unescape(char *src)
305{
306	char *tmp = NULL;
307	char *dst = NULL;
308
309	if (src == NULL) {
310		dst = _strdup_null(src);
311	} else {
312		strip_spaces(&src);
313		tmp = _unescape(src, "=;:,\\");
314		dst = (tmp == NULL) ? _strdup_null(src) : tmp;
315	}
316
317	return (dst);
318}
319
320
321/*
322 * Some utilities for handling comma-separated lists.
323 */
324char *
325_argv_to_csl(char **strings)
326{
327	int len = 0;
328	int i = 0;
329	char *newstr = NULL;
330
331	if (strings == NULL)
332		return (NULL);
333	for (i = 0; strings[i] != NULL; i++) {
334		len += strlen(strings[i]) + 1;
335	}
336	if ((len > 0) && ((newstr = (char *)malloc(len + 1)) != NULL)) {
337		(void) memset(newstr, 0, len);
338		for (i = 0; strings[i] != NULL; i++) {
339			(void) strcat(newstr, strings[i]);
340			(void) strcat(newstr, ",");
341		}
342		newstr[len-1] = NULL;
343		return (newstr);
344	} else
345		return (NULL);
346}
347
348
349char **
350_csl_to_argv(char *csl)
351{
352	int len = 0;
353	int ncommas = 0;
354	int i = 0;
355	char **spc = NULL;
356	char *copy = NULL;
357	char *pc;
358	char *lasts = NULL;
359
360	len = strlen(csl);
361	for (i = 0; i < len; i++) {
362		if (csl[i] == ',')
363			ncommas++;
364	}
365	if ((spc = (char **)malloc((ncommas + 2) * sizeof (char *))) == NULL) {
366		return (NULL);
367	}
368	copy = strdup(csl);
369	for (pc = strtok_r(copy, ",", &lasts), i = 0; pc != NULL;
370	    pc = strtok_r(NULL, ",", &lasts), i++) {
371		spc[i] = strdup(pc);
372	}
373	spc[i] = NULL;
374	free(copy);
375	return (spc);
376}
377
378
379void
380_free_argv(char **p_argv)
381{
382	char **p_a;
383
384	for (p_a = p_argv; *p_a != NULL; p_a++)
385		free(*p_a);
386	free(p_argv);
387}
388
389
390#ifdef DEBUG
391void
392print_kva(kva_t *kva)
393{
394	int	i;
395	kv_t	*data;
396
397	if (kva == NULL) {
398		(void) printf("  (empty)\n");
399		return;
400	}
401	data = kva->data;
402	for (i = 0; i < kva->length; i++) {
403		(void) printf("  %s = %s\n",
404		    data[i].key != NULL ? data[i].key : "NULL",
405		    data[i].value != NULL ? data[i].value : "NULL");
406	}
407}
408#endif  /* DEBUG */
409