1/*
2 * This file is part of the ZFS Event Daemon (ZED).
3 *
4 * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
5 * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
6 * Refer to the OpenZFS git commit log for authoritative copyright attribution.
7 *
8 * The contents of this file are subject to the terms of the
9 * Common Development and Distribution License Version 1.0 (CDDL-1.0).
10 * You can obtain a copy of the license from the top-level file
11 * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
12 * You may not use this file except in compliance with the license.
13 */
14
15#include <assert.h>
16#include <errno.h>
17#include <stddef.h>
18#include <stdlib.h>
19#include <string.h>
20#include <sys/avl.h>
21#include <sys/sysmacros.h>
22#include "zed_strings.h"
23
24struct zed_strings {
25	avl_tree_t tree;
26	avl_node_t *iteratorp;
27};
28
29struct zed_strings_node {
30	avl_node_t node;
31	char *key;
32	char *val;
33};
34
35typedef struct zed_strings_node zed_strings_node_t;
36
37/*
38 * Compare zed_strings_node_t nodes [x1] and [x2].
39 * As required for the AVL tree, return -1 for <, 0 for ==, and +1 for >.
40 */
41static int
42_zed_strings_node_compare(const void *x1, const void *x2)
43{
44	const char *s1;
45	const char *s2;
46	int rv;
47
48	assert(x1 != NULL);
49	assert(x2 != NULL);
50
51	s1 = ((const zed_strings_node_t *) x1)->key;
52	assert(s1 != NULL);
53	s2 = ((const zed_strings_node_t *) x2)->key;
54	assert(s2 != NULL);
55	rv = strcmp(s1, s2);
56
57	if (rv < 0)
58		return (-1);
59
60	if (rv > 0)
61		return (1);
62
63	return (0);
64}
65
66/*
67 * Return a new string container, or NULL on error.
68 */
69zed_strings_t *
70zed_strings_create(void)
71{
72	zed_strings_t *zsp;
73
74	zsp = calloc(1, sizeof (*zsp));
75	if (!zsp)
76		return (NULL);
77
78	avl_create(&zsp->tree, _zed_strings_node_compare,
79	    sizeof (zed_strings_node_t), offsetof(zed_strings_node_t, node));
80
81	zsp->iteratorp = NULL;
82	return (zsp);
83}
84
85/*
86 * Destroy the string node [np].
87 */
88static void
89_zed_strings_node_destroy(zed_strings_node_t *np)
90{
91	if (!np)
92		return;
93
94	if (np->key) {
95		if (np->key != np->val)
96			free(np->key);
97		np->key = NULL;
98	}
99	if (np->val) {
100		free(np->val);
101		np->val = NULL;
102	}
103	free(np);
104}
105
106/*
107 * Return a new string node for storing the string [val], or NULL on error.
108 * If [key] is specified, it will be used to index the node; otherwise,
109 * the string [val] will be used.
110 */
111static zed_strings_node_t *
112_zed_strings_node_create(const char *key, const char *val)
113{
114	zed_strings_node_t *np;
115
116	assert(val != NULL);
117
118	np = calloc(1, sizeof (*np));
119	if (!np)
120		return (NULL);
121
122	np->val = strdup(val);
123	if (!np->val)
124		goto nomem;
125
126	if (key) {
127		np->key = strdup(key);
128		if (!np->key)
129			goto nomem;
130	} else {
131		np->key = np->val;
132	}
133	return (np);
134
135nomem:
136	_zed_strings_node_destroy(np);
137	return (NULL);
138}
139
140/*
141 * Destroy the string container [zsp] and all nodes within.
142 */
143void
144zed_strings_destroy(zed_strings_t *zsp)
145{
146	void *cookie;
147	zed_strings_node_t *np;
148
149	if (!zsp)
150		return;
151
152	cookie = NULL;
153	while ((np = avl_destroy_nodes(&zsp->tree, &cookie)))
154		_zed_strings_node_destroy(np);
155
156	avl_destroy(&zsp->tree);
157	free(zsp);
158}
159
160/*
161 * Add a copy of the string [s] indexed by [key] to the container [zsp].
162 * If [key] already exists within the container [zsp], it will be replaced
163 * with the new string [s].
164 * If [key] is NULL, the string [s] will be used as the key.
165 * Return 0 on success, or -1 on error.
166 */
167int
168zed_strings_add(zed_strings_t *zsp, const char *key, const char *s)
169{
170	zed_strings_node_t *newp, *oldp;
171
172	if (!zsp || !s) {
173		errno = EINVAL;
174		return (-1);
175	}
176	if (key == s)
177		key = NULL;
178
179	newp = _zed_strings_node_create(key, s);
180	if (!newp)
181		return (-1);
182
183	oldp = avl_find(&zsp->tree, newp, NULL);
184	if (oldp) {
185		avl_remove(&zsp->tree, oldp);
186		_zed_strings_node_destroy(oldp);
187	}
188	avl_add(&zsp->tree, newp);
189	return (0);
190}
191
192/*
193 * Return the first string in container [zsp].
194 * Return NULL if there are no strings, or on error.
195 * This can be called multiple times to re-traverse [zsp].
196 * XXX: Not thread-safe.
197 */
198const char *
199zed_strings_first(zed_strings_t *zsp)
200{
201	if (!zsp) {
202		errno = EINVAL;
203		return (NULL);
204	}
205	zsp->iteratorp = avl_first(&zsp->tree);
206	if (!zsp->iteratorp)
207		return (NULL);
208
209	return (((zed_strings_node_t *)zsp->iteratorp)->val);
210
211}
212
213/*
214 * Return the next string in container [zsp].
215 * Return NULL after the last string, or on error.
216 * This must be called after zed_strings_first().
217 * XXX: Not thread-safe.
218 */
219const char *
220zed_strings_next(zed_strings_t *zsp)
221{
222	if (!zsp) {
223		errno = EINVAL;
224		return (NULL);
225	}
226	if (!zsp->iteratorp)
227		return (NULL);
228
229	zsp->iteratorp = AVL_NEXT(&zsp->tree, zsp->iteratorp);
230	if (!zsp->iteratorp)
231		return (NULL);
232
233	return (((zed_strings_node_t *)zsp->iteratorp)->val);
234}
235
236/*
237 * Return the number of strings in container [zsp], or -1 on error.
238 */
239int
240zed_strings_count(zed_strings_t *zsp)
241{
242	if (!zsp) {
243		errno = EINVAL;
244		return (-1);
245	}
246	return (avl_numnodes(&zsp->tree));
247}
248