xref: /freebsd/contrib/pkgconf/libpkgconf/tuple.c (revision f5dc2263ab1be8a35a7e27e82103f9ccd41ae584)
1 /*
2  * tuple.c
3  * management of key->value tuples
4  *
5  * SPDX-License-Identifier: pkgconf
6  *
7  * Copyright (c) 2011, 2012 pkgconf authors (see AUTHORS).
8  *
9  * Permission to use, copy, modify, and/or distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * This software is provided 'as is' and without any warranty, express or
14  * implied.  In no event shall the authors be liable for any damages arising
15  * from the use of this software.
16  */
17 
18 #include <libpkgconf/stdinc.h>
19 #include <libpkgconf/libpkgconf.h>
20 
21 /*
22  * !doc
23  *
24  * libpkgconf `tuple` module
25  * =========================
26  *
27  * The `tuple` module provides key-value mappings backed by a linked list.  The key-value
28  * mapping is mainly used for variable substitution when parsing .pc files.
29  *
30  * There are two sets of mappings: a ``pkgconf_pkg_t`` specific mapping, and a `global` mapping.
31  * The `tuple` module provides convenience wrappers for managing the `global` mapping, which is
32  * attached to a given client object.
33  */
34 
35 /*
36  * !doc
37  *
38  * .. c:function:: void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value)
39  *
40  *    Defines a global variable, replacing the previous declaration if one was set.
41  *
42  *    :param pkgconf_client_t* client: The pkgconf client object to modify.
43  *    :param char* key: The key for the mapping (variable name).
44  *    :param char* value: The value for the mapped entry.
45  *    :return: nothing
46  */
47 void
48 pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value)
49 {
50 	pkgconf_tuple_add(client, &client->global_vars, key, value, false, 0);
51 }
52 
53 /*
54  * !doc
55  *
56  * .. c:function:: void pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key)
57  *
58  *    Looks up a global variable.
59  *
60  *    :param pkgconf_client_t* client: The pkgconf client object to access.
61  *    :param char* key: The key or variable name to look up.
62  *    :return: the contents of the variable or ``NULL``
63  *    :rtype: char *
64  */
65 const char *
66 pkgconf_tuple_find_global(pkgconf_client_t *client, const char *key)
67 {
68 	pkgconf_variable_t *v;
69 	bool saw_sysroot = false;
70 
71 	if (client == NULL || key == NULL)
72 		return NULL;
73 
74 	v = pkgconf_variable_find(&client->global_vars, key);
75 
76 	pkgconf_buffer_reset(&client->_scratch_buffer);
77 	(void) pkgconf_variable_eval(client, &client->global_vars, v, &client->_scratch_buffer, &saw_sysroot);
78 
79 	return pkgconf_buffer_str_or_empty(&client->_scratch_buffer);
80 }
81 
82 /*
83  * !doc
84  *
85  * .. c:function:: void pkgconf_tuple_free_global(pkgconf_client_t *client)
86  *
87  *    Delete all global variables associated with a pkgconf client object.
88  *
89  *    :param pkgconf_client_t* client: The pkgconf client object to modify.
90  *    :return: nothing
91  */
92 void
93 pkgconf_tuple_free_global(pkgconf_client_t *client)
94 {
95 	pkgconf_tuple_free(&client->global_vars);
96 }
97 
98 /*
99  * !doc
100  *
101  * .. c:function:: void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv)
102  *
103  *    Parse and define a global variable.
104  *
105  *    :param pkgconf_client_t* client: The pkgconf client object to modify.
106  *    :param char* kv: The variable in the form of ``key=value``.
107  *    :return: nothing
108  */
109 void
110 pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv)
111 {
112 	char *workbuf = strdup(kv);
113 	char *value;
114 	pkgconf_tuple_t *tuple;
115 
116 	if (workbuf == NULL)
117 		goto out;
118 
119 	value = strchr(workbuf, '=');
120 	if (value == NULL)
121 		goto out;
122 
123 	*value++ = '\0';
124 
125 	tuple = pkgconf_tuple_add(client, &client->global_vars, workbuf, value, false, 0);
126 	if (tuple != NULL)
127 		tuple->flags = PKGCONF_PKG_TUPLEF_OVERRIDE;
128 
129 out:
130 	free(workbuf);
131 }
132 
133 static char *
134 dequote(const char *value)
135 {
136 	char *buf = calloc(1, (strlen(value) + 1) * 2);
137 	char *bptr = buf;
138 	const char *i;
139 	char quote = 0;
140 
141 	if (buf == NULL)
142 		return NULL;
143 
144 	if (*value == '\'' || *value == '"')
145 		quote = *value;
146 
147 	for (i = value; *i != '\0'; i++)
148 	{
149 		if (*i == '\\' && quote && *(i + 1) == quote)
150 		{
151 			i++;
152 			*bptr++ = *i;
153 		}
154 		else if (*i != quote)
155 			*bptr++ = *i;
156 	}
157 
158 	return buf;
159 }
160 
161 /*
162  * !doc
163  *
164  * .. c:function:: pkgconf_tuple_t *pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse)
165  *
166  *    Wrapper around pkgconf_variable_get_or_create(list, key) and bytecode compiler.
167  *
168  *    :param pkgconf_client_t* client: The pkgconf client object to access.
169  *    :param pkgconf_list_t* list: The variable list to add the new variable to.
170  *    :param char* key: The name of the variable being added.
171  *    :param char* value: The value of the variable being added.
172  *    :param bool parse: Whether or not to parse the value for variable substitution.
173  *    :return: a variable object
174  *    :rtype: pkgconf_tuple_t *
175  */
176 pkgconf_tuple_t *
177 pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse, unsigned int flags)
178 {
179 	char *dequote_value;
180 	pkgconf_buffer_t rhs_bcbuf = PKGCONF_BUFFER_INITIALIZER;
181 
182 	(void) client;
183 
184 	if (list == NULL || key == NULL || value == NULL)
185 		return NULL;
186 
187 	dequote_value = dequote(value);
188 	if (dequote_value == NULL)
189 		return NULL;
190 
191 	pkgconf_variable_t *v = pkgconf_variable_get_or_create(list, key);
192 	if (v == NULL)
193 	{
194 		free(dequote_value);
195 		return NULL;
196 	}
197 
198 	v->flags = flags;
199 
200 	if (!parse)
201 	{
202 		pkgconf_buffer_reset(&v->bcbuf);
203 		pkgconf_bytecode_emit_text(&v->bcbuf, dequote_value, strlen(dequote_value));
204 		pkgconf_bytecode_from_buffer(&v->bc, &v->bcbuf);
205 		free(dequote_value);
206 		return (pkgconf_tuple_t *) v;
207 	}
208 
209 	pkgconf_bytecode_compile(&rhs_bcbuf, dequote_value);
210 	free(dequote_value);
211 
212 	/* ugh, we are doing var=${var}/foo stuff */
213 	if (pkgconf_bytecode_references_var(&rhs_bcbuf, key))
214 	{
215 		pkgconf_buffer_t old_bcbuf = PKGCONF_BUFFER_INITIALIZER;
216 		pkgconf_buffer_t new_bcbuf = PKGCONF_BUFFER_INITIALIZER;
217 
218 		/* preserve the old bytecode */
219 		pkgconf_buffer_copy(&v->bcbuf, &old_bcbuf);
220 
221 		/* splice the selfrefs, using the old bytecode instead of ${var} */
222 		if (!pkgconf_bytecode_rewrite_selfrefs(&new_bcbuf, &rhs_bcbuf, key, &old_bcbuf))
223 		{
224 			pkgconf_buffer_finalize(&old_bcbuf);
225 			pkgconf_buffer_finalize(&new_bcbuf);
226 			pkgconf_buffer_finalize(&rhs_bcbuf);
227 
228 			return NULL;
229 		}
230 
231 		/* copy the spliced bytecode back to &rhs_bcbuf, replacing its contents */
232 		pkgconf_buffer_copy(&new_bcbuf, &rhs_bcbuf);
233 
234 		pkgconf_buffer_finalize(&old_bcbuf);
235 		pkgconf_buffer_finalize(&new_bcbuf);
236 	}
237 
238 	pkgconf_buffer_copy(&rhs_bcbuf, &v->bcbuf);
239 	pkgconf_bytecode_from_buffer(&v->bc, &v->bcbuf);
240 	pkgconf_buffer_finalize(&rhs_bcbuf);
241 
242 	return (pkgconf_tuple_t *) v;
243 }
244 
245 /*
246  * !doc
247  *
248  * .. c:function:: char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key)
249  *
250  *    Look up a variable in a variable list.
251  *
252  *    :param pkgconf_client_t* client: The pkgconf client object to access.
253  *    :param pkgconf_list_t* list: The variable list to search.
254  *    :param char* key: The variable name to search for.
255  *    :return: the value of the variable or ``NULL``
256  *    :rtype: char *
257  */
258 const char *
259 pkgconf_tuple_find(pkgconf_client_t *client, pkgconf_list_t *list, const char *key)
260 {
261 	pkgconf_variable_t *v;
262 
263 	if (client == NULL || list == NULL || key == NULL)
264 		return NULL;
265 
266 	v = pkgconf_variable_find(list, key);
267 	if (v == NULL)
268 		v = pkgconf_variable_find(&client->global_vars, key);
269 
270 	pkgconf_buffer_reset(&client->_scratch_buffer);
271 
272 	(void) pkgconf_variable_eval(client, list, v, &client->_scratch_buffer, NULL);
273 
274 	return pkgconf_buffer_str_or_empty(&client->_scratch_buffer);
275 }
276 
277 /*
278  * !doc
279  *
280  * .. c:function:: void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list)
281  *
282  *    Deletes a variable object, removing it from any variable lists and releasing any memory associated
283  *    with it.
284  *
285  *    :param pkgconf_tuple_t* tuple: The variable object to release.
286  *    :param pkgconf_list_t* list: The variable list the variable object is attached to.
287  *    :return: nothing
288  */
289 void
290 pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list)
291 {
292 	pkgconf_node_delete(&tuple->iter, list);
293 	pkgconf_variable_free(tuple);
294 }
295 
296 /*
297  * !doc
298  *
299  * .. c:function:: void pkgconf_tuple_free(pkgconf_list_t *list)
300  *
301  *    Deletes a variable list and any variables attached to it.
302  *
303  *    :param pkgconf_list_t* list: The variable list to delete.
304  *    :return: nothing
305  */
306 void
307 pkgconf_tuple_free(pkgconf_list_t *list)
308 {
309 	pkgconf_node_t *node, *next;
310 
311 	PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
312 		pkgconf_tuple_free_entry(node->data, list);
313 
314 	pkgconf_list_zero(list);
315 }
316