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
pkgconf_tuple_add_global(pkgconf_client_t * client,const char * key,const char * value)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 *
pkgconf_tuple_find_global(pkgconf_client_t * client,const char * key)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
pkgconf_tuple_free_global(pkgconf_client_t * client)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
pkgconf_tuple_define_global(pkgconf_client_t * client,const char * kv)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 *
dequote(const char * value)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 *
pkgconf_tuple_add(const pkgconf_client_t * client,pkgconf_list_t * list,const char * key,const char * value,bool parse,unsigned int flags)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 *
pkgconf_tuple_find(pkgconf_client_t * client,pkgconf_list_t * list,const char * key)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
pkgconf_tuple_free_entry(pkgconf_tuple_t * tuple,pkgconf_list_t * list)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
pkgconf_tuple_free(pkgconf_list_t * list)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