xref: /freebsd/contrib/pkgconf/libpkgconf/tuple.c (revision a3cefe7f2b4df0f70ff92d4570ce18e517af43ec)
1*a3cefe7fSPierre Pronchery /*
2*a3cefe7fSPierre Pronchery  * tuple.c
3*a3cefe7fSPierre Pronchery  * management of key->value tuples
4*a3cefe7fSPierre Pronchery  *
5*a3cefe7fSPierre Pronchery  * Copyright (c) 2011, 2012 pkgconf authors (see AUTHORS).
6*a3cefe7fSPierre Pronchery  *
7*a3cefe7fSPierre Pronchery  * Permission to use, copy, modify, and/or distribute this software for any
8*a3cefe7fSPierre Pronchery  * purpose with or without fee is hereby granted, provided that the above
9*a3cefe7fSPierre Pronchery  * copyright notice and this permission notice appear in all copies.
10*a3cefe7fSPierre Pronchery  *
11*a3cefe7fSPierre Pronchery  * This software is provided 'as is' and without any warranty, express or
12*a3cefe7fSPierre Pronchery  * implied.  In no event shall the authors be liable for any damages arising
13*a3cefe7fSPierre Pronchery  * from the use of this software.
14*a3cefe7fSPierre Pronchery  */
15*a3cefe7fSPierre Pronchery 
16*a3cefe7fSPierre Pronchery #include <libpkgconf/stdinc.h>
17*a3cefe7fSPierre Pronchery #include <libpkgconf/libpkgconf.h>
18*a3cefe7fSPierre Pronchery 
19*a3cefe7fSPierre Pronchery /*
20*a3cefe7fSPierre Pronchery  * !doc
21*a3cefe7fSPierre Pronchery  *
22*a3cefe7fSPierre Pronchery  * libpkgconf `tuple` module
23*a3cefe7fSPierre Pronchery  * =========================
24*a3cefe7fSPierre Pronchery  *
25*a3cefe7fSPierre Pronchery  * The `tuple` module provides key-value mappings backed by a linked list.  The key-value
26*a3cefe7fSPierre Pronchery  * mapping is mainly used for variable substitution when parsing .pc files.
27*a3cefe7fSPierre Pronchery  *
28*a3cefe7fSPierre Pronchery  * There are two sets of mappings: a ``pkgconf_pkg_t`` specific mapping, and a `global` mapping.
29*a3cefe7fSPierre Pronchery  * The `tuple` module provides convenience wrappers for managing the `global` mapping, which is
30*a3cefe7fSPierre Pronchery  * attached to a given client object.
31*a3cefe7fSPierre Pronchery  */
32*a3cefe7fSPierre Pronchery 
33*a3cefe7fSPierre Pronchery /*
34*a3cefe7fSPierre Pronchery  * !doc
35*a3cefe7fSPierre Pronchery  *
36*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value)
37*a3cefe7fSPierre Pronchery  *
38*a3cefe7fSPierre Pronchery  *    Defines a global variable, replacing the previous declaration if one was set.
39*a3cefe7fSPierre Pronchery  *
40*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to modify.
41*a3cefe7fSPierre Pronchery  *    :param char* key: The key for the mapping (variable name).
42*a3cefe7fSPierre Pronchery  *    :param char* value: The value for the mapped entry.
43*a3cefe7fSPierre Pronchery  *    :return: nothing
44*a3cefe7fSPierre Pronchery  */
45*a3cefe7fSPierre Pronchery void
pkgconf_tuple_add_global(pkgconf_client_t * client,const char * key,const char * value)46*a3cefe7fSPierre Pronchery pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value)
47*a3cefe7fSPierre Pronchery {
48*a3cefe7fSPierre Pronchery 	pkgconf_tuple_add(client, &client->global_vars, key, value, false, 0);
49*a3cefe7fSPierre Pronchery }
50*a3cefe7fSPierre Pronchery 
51*a3cefe7fSPierre Pronchery static pkgconf_tuple_t *
lookup_global_tuple(const pkgconf_client_t * client,const char * key)52*a3cefe7fSPierre Pronchery lookup_global_tuple(const pkgconf_client_t *client, const char *key)
53*a3cefe7fSPierre Pronchery {
54*a3cefe7fSPierre Pronchery 	pkgconf_node_t *node;
55*a3cefe7fSPierre Pronchery 
56*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(client->global_vars.head, node)
57*a3cefe7fSPierre Pronchery 	{
58*a3cefe7fSPierre Pronchery 		pkgconf_tuple_t *tuple = node->data;
59*a3cefe7fSPierre Pronchery 
60*a3cefe7fSPierre Pronchery 		if (!strcmp(tuple->key, key))
61*a3cefe7fSPierre Pronchery 			return tuple;
62*a3cefe7fSPierre Pronchery 	}
63*a3cefe7fSPierre Pronchery 
64*a3cefe7fSPierre Pronchery 	return NULL;
65*a3cefe7fSPierre Pronchery }
66*a3cefe7fSPierre Pronchery 
67*a3cefe7fSPierre Pronchery /*
68*a3cefe7fSPierre Pronchery  * !doc
69*a3cefe7fSPierre Pronchery  *
70*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key)
71*a3cefe7fSPierre Pronchery  *
72*a3cefe7fSPierre Pronchery  *    Looks up a global variable.
73*a3cefe7fSPierre Pronchery  *
74*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to access.
75*a3cefe7fSPierre Pronchery  *    :param char* key: The key or variable name to look up.
76*a3cefe7fSPierre Pronchery  *    :return: the contents of the variable or ``NULL``
77*a3cefe7fSPierre Pronchery  *    :rtype: char *
78*a3cefe7fSPierre Pronchery  */
79*a3cefe7fSPierre Pronchery char *
pkgconf_tuple_find_global(const pkgconf_client_t * client,const char * key)80*a3cefe7fSPierre Pronchery pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key)
81*a3cefe7fSPierre Pronchery {
82*a3cefe7fSPierre Pronchery 	pkgconf_tuple_t *tuple;
83*a3cefe7fSPierre Pronchery 
84*a3cefe7fSPierre Pronchery 	tuple = lookup_global_tuple(client, key);
85*a3cefe7fSPierre Pronchery 	if (tuple == NULL)
86*a3cefe7fSPierre Pronchery 		return NULL;
87*a3cefe7fSPierre Pronchery 
88*a3cefe7fSPierre Pronchery 	return tuple->value;
89*a3cefe7fSPierre Pronchery }
90*a3cefe7fSPierre Pronchery 
91*a3cefe7fSPierre Pronchery /*
92*a3cefe7fSPierre Pronchery  * !doc
93*a3cefe7fSPierre Pronchery  *
94*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_tuple_free_global(pkgconf_client_t *client)
95*a3cefe7fSPierre Pronchery  *
96*a3cefe7fSPierre Pronchery  *    Delete all global variables associated with a pkgconf client object.
97*a3cefe7fSPierre Pronchery  *
98*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to modify.
99*a3cefe7fSPierre Pronchery  *    :return: nothing
100*a3cefe7fSPierre Pronchery  */
101*a3cefe7fSPierre Pronchery void
pkgconf_tuple_free_global(pkgconf_client_t * client)102*a3cefe7fSPierre Pronchery pkgconf_tuple_free_global(pkgconf_client_t *client)
103*a3cefe7fSPierre Pronchery {
104*a3cefe7fSPierre Pronchery 	pkgconf_tuple_free(&client->global_vars);
105*a3cefe7fSPierre Pronchery }
106*a3cefe7fSPierre Pronchery 
107*a3cefe7fSPierre Pronchery /*
108*a3cefe7fSPierre Pronchery  * !doc
109*a3cefe7fSPierre Pronchery  *
110*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv)
111*a3cefe7fSPierre Pronchery  *
112*a3cefe7fSPierre Pronchery  *    Parse and define a global variable.
113*a3cefe7fSPierre Pronchery  *
114*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to modify.
115*a3cefe7fSPierre Pronchery  *    :param char* kv: The variable in the form of ``key=value``.
116*a3cefe7fSPierre Pronchery  *    :return: nothing
117*a3cefe7fSPierre Pronchery  */
118*a3cefe7fSPierre Pronchery void
pkgconf_tuple_define_global(pkgconf_client_t * client,const char * kv)119*a3cefe7fSPierre Pronchery pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv)
120*a3cefe7fSPierre Pronchery {
121*a3cefe7fSPierre Pronchery 	char *workbuf = strdup(kv);
122*a3cefe7fSPierre Pronchery 	char *value;
123*a3cefe7fSPierre Pronchery 	pkgconf_tuple_t *tuple;
124*a3cefe7fSPierre Pronchery 
125*a3cefe7fSPierre Pronchery 	value = strchr(workbuf, '=');
126*a3cefe7fSPierre Pronchery 	if (value == NULL)
127*a3cefe7fSPierre Pronchery 		goto out;
128*a3cefe7fSPierre Pronchery 
129*a3cefe7fSPierre Pronchery 	*value++ = '\0';
130*a3cefe7fSPierre Pronchery 
131*a3cefe7fSPierre Pronchery 	tuple = pkgconf_tuple_add(client, &client->global_vars, workbuf, value, false, 0);
132*a3cefe7fSPierre Pronchery 	if (tuple != NULL)
133*a3cefe7fSPierre Pronchery 		tuple->flags = PKGCONF_PKG_TUPLEF_OVERRIDE;
134*a3cefe7fSPierre Pronchery 
135*a3cefe7fSPierre Pronchery out:
136*a3cefe7fSPierre Pronchery 	free(workbuf);
137*a3cefe7fSPierre Pronchery }
138*a3cefe7fSPierre Pronchery 
139*a3cefe7fSPierre Pronchery static void
pkgconf_tuple_find_delete(pkgconf_list_t * list,const char * key)140*a3cefe7fSPierre Pronchery pkgconf_tuple_find_delete(pkgconf_list_t *list, const char *key)
141*a3cefe7fSPierre Pronchery {
142*a3cefe7fSPierre Pronchery 	pkgconf_node_t *node, *next;
143*a3cefe7fSPierre Pronchery 
144*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
145*a3cefe7fSPierre Pronchery 	{
146*a3cefe7fSPierre Pronchery 		pkgconf_tuple_t *tuple = node->data;
147*a3cefe7fSPierre Pronchery 
148*a3cefe7fSPierre Pronchery 		if (!strcmp(tuple->key, key))
149*a3cefe7fSPierre Pronchery 		{
150*a3cefe7fSPierre Pronchery 			pkgconf_tuple_free_entry(tuple, list);
151*a3cefe7fSPierre Pronchery 			return;
152*a3cefe7fSPierre Pronchery 		}
153*a3cefe7fSPierre Pronchery 	}
154*a3cefe7fSPierre Pronchery }
155*a3cefe7fSPierre Pronchery 
156*a3cefe7fSPierre Pronchery static char *
dequote(const char * value)157*a3cefe7fSPierre Pronchery dequote(const char *value)
158*a3cefe7fSPierre Pronchery {
159*a3cefe7fSPierre Pronchery 	char *buf = calloc(1, (strlen(value) + 1) * 2);
160*a3cefe7fSPierre Pronchery 	char *bptr = buf;
161*a3cefe7fSPierre Pronchery 	const char *i;
162*a3cefe7fSPierre Pronchery 	char quote = 0;
163*a3cefe7fSPierre Pronchery 
164*a3cefe7fSPierre Pronchery 	if (*value == '\'' || *value == '"')
165*a3cefe7fSPierre Pronchery 		quote = *value;
166*a3cefe7fSPierre Pronchery 
167*a3cefe7fSPierre Pronchery 	for (i = value; *i != '\0'; i++)
168*a3cefe7fSPierre Pronchery 	{
169*a3cefe7fSPierre Pronchery 		if (*i == '\\' && quote && *(i + 1) == quote)
170*a3cefe7fSPierre Pronchery 		{
171*a3cefe7fSPierre Pronchery 			i++;
172*a3cefe7fSPierre Pronchery 			*bptr++ = *i;
173*a3cefe7fSPierre Pronchery 		}
174*a3cefe7fSPierre Pronchery 		else if (*i != quote)
175*a3cefe7fSPierre Pronchery 			*bptr++ = *i;
176*a3cefe7fSPierre Pronchery 	}
177*a3cefe7fSPierre Pronchery 
178*a3cefe7fSPierre Pronchery 	return buf;
179*a3cefe7fSPierre Pronchery }
180*a3cefe7fSPierre Pronchery 
181*a3cefe7fSPierre Pronchery static const char *
find_sysroot(const pkgconf_client_t * client,pkgconf_list_t * vars)182*a3cefe7fSPierre Pronchery find_sysroot(const pkgconf_client_t *client, pkgconf_list_t *vars)
183*a3cefe7fSPierre Pronchery {
184*a3cefe7fSPierre Pronchery 	const char *sysroot_dir;
185*a3cefe7fSPierre Pronchery 
186*a3cefe7fSPierre Pronchery 	sysroot_dir = pkgconf_tuple_find(client, vars, "pc_sysrootdir");
187*a3cefe7fSPierre Pronchery 	if (sysroot_dir == NULL)
188*a3cefe7fSPierre Pronchery 		sysroot_dir = client->sysroot_dir;
189*a3cefe7fSPierre Pronchery 
190*a3cefe7fSPierre Pronchery 	return sysroot_dir;
191*a3cefe7fSPierre Pronchery }
192*a3cefe7fSPierre Pronchery 
193*a3cefe7fSPierre Pronchery static bool
should_rewrite_sysroot(const pkgconf_client_t * client,pkgconf_list_t * vars,const char * buf,unsigned int flags)194*a3cefe7fSPierre Pronchery should_rewrite_sysroot(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *buf, unsigned int flags)
195*a3cefe7fSPierre Pronchery {
196*a3cefe7fSPierre Pronchery 	const char *sysroot_dir;
197*a3cefe7fSPierre Pronchery 
198*a3cefe7fSPierre Pronchery 	if (flags & PKGCONF_PKG_PROPF_UNINSTALLED && !(client->flags & PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES))
199*a3cefe7fSPierre Pronchery 		return false;
200*a3cefe7fSPierre Pronchery 
201*a3cefe7fSPierre Pronchery 	sysroot_dir = find_sysroot(client, vars);
202*a3cefe7fSPierre Pronchery 	if (sysroot_dir == NULL)
203*a3cefe7fSPierre Pronchery 		return false;
204*a3cefe7fSPierre Pronchery 
205*a3cefe7fSPierre Pronchery 	if (*buf != '/')
206*a3cefe7fSPierre Pronchery 		return false;
207*a3cefe7fSPierre Pronchery 
208*a3cefe7fSPierre Pronchery 	if (!strcmp(sysroot_dir, "/"))
209*a3cefe7fSPierre Pronchery 		return false;
210*a3cefe7fSPierre Pronchery 
211*a3cefe7fSPierre Pronchery 	if (strlen(buf) <= strlen(sysroot_dir))
212*a3cefe7fSPierre Pronchery 		return false;
213*a3cefe7fSPierre Pronchery 
214*a3cefe7fSPierre Pronchery 	if (strstr(buf + strlen(sysroot_dir), sysroot_dir) == NULL)
215*a3cefe7fSPierre Pronchery 		return false;
216*a3cefe7fSPierre Pronchery 
217*a3cefe7fSPierre Pronchery 	return true;
218*a3cefe7fSPierre Pronchery }
219*a3cefe7fSPierre Pronchery 
220*a3cefe7fSPierre Pronchery /*
221*a3cefe7fSPierre Pronchery  * !doc
222*a3cefe7fSPierre Pronchery  *
223*a3cefe7fSPierre Pronchery  * .. 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)
224*a3cefe7fSPierre Pronchery  *
225*a3cefe7fSPierre Pronchery  *    Optionally parse and then define a variable.
226*a3cefe7fSPierre Pronchery  *
227*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to access.
228*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* list: The variable list to add the new variable to.
229*a3cefe7fSPierre Pronchery  *    :param char* key: The name of the variable being added.
230*a3cefe7fSPierre Pronchery  *    :param char* value: The value of the variable being added.
231*a3cefe7fSPierre Pronchery  *    :param bool parse: Whether or not to parse the value for variable substitution.
232*a3cefe7fSPierre Pronchery  *    :return: a variable object
233*a3cefe7fSPierre Pronchery  *    :rtype: pkgconf_tuple_t *
234*a3cefe7fSPierre Pronchery  */
235*a3cefe7fSPierre Pronchery 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)236*a3cefe7fSPierre Pronchery pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse, unsigned int flags)
237*a3cefe7fSPierre Pronchery {
238*a3cefe7fSPierre Pronchery 	char *dequote_value;
239*a3cefe7fSPierre Pronchery 	pkgconf_tuple_t *tuple = calloc(1, sizeof(pkgconf_tuple_t));
240*a3cefe7fSPierre Pronchery 
241*a3cefe7fSPierre Pronchery 	pkgconf_tuple_find_delete(list, key);
242*a3cefe7fSPierre Pronchery 
243*a3cefe7fSPierre Pronchery 	dequote_value = dequote(value);
244*a3cefe7fSPierre Pronchery 
245*a3cefe7fSPierre Pronchery 	tuple->key = strdup(key);
246*a3cefe7fSPierre Pronchery 	if (parse)
247*a3cefe7fSPierre Pronchery 		tuple->value = pkgconf_tuple_parse(client, list, dequote_value, flags);
248*a3cefe7fSPierre Pronchery 	else
249*a3cefe7fSPierre Pronchery 		tuple->value = strdup(dequote_value);
250*a3cefe7fSPierre Pronchery 
251*a3cefe7fSPierre Pronchery 	PKGCONF_TRACE(client, "adding tuple to @%p: %s => %s (parsed? %d)", list, key, tuple->value, parse);
252*a3cefe7fSPierre Pronchery 
253*a3cefe7fSPierre Pronchery 	pkgconf_node_insert(&tuple->iter, tuple, list);
254*a3cefe7fSPierre Pronchery 
255*a3cefe7fSPierre Pronchery 	free(dequote_value);
256*a3cefe7fSPierre Pronchery 
257*a3cefe7fSPierre Pronchery 	return tuple;
258*a3cefe7fSPierre Pronchery }
259*a3cefe7fSPierre Pronchery 
260*a3cefe7fSPierre Pronchery /*
261*a3cefe7fSPierre Pronchery  * !doc
262*a3cefe7fSPierre Pronchery  *
263*a3cefe7fSPierre Pronchery  * .. c:function:: char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key)
264*a3cefe7fSPierre Pronchery  *
265*a3cefe7fSPierre Pronchery  *    Look up a variable in a variable list.
266*a3cefe7fSPierre Pronchery  *
267*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to access.
268*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* list: The variable list to search.
269*a3cefe7fSPierre Pronchery  *    :param char* key: The variable name to search for.
270*a3cefe7fSPierre Pronchery  *    :return: the value of the variable or ``NULL``
271*a3cefe7fSPierre Pronchery  *    :rtype: char *
272*a3cefe7fSPierre Pronchery  */
273*a3cefe7fSPierre Pronchery char *
pkgconf_tuple_find(const pkgconf_client_t * client,pkgconf_list_t * list,const char * key)274*a3cefe7fSPierre Pronchery pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key)
275*a3cefe7fSPierre Pronchery {
276*a3cefe7fSPierre Pronchery 	pkgconf_node_t *node;
277*a3cefe7fSPierre Pronchery 	pkgconf_tuple_t *global_tuple;
278*a3cefe7fSPierre Pronchery 
279*a3cefe7fSPierre Pronchery 	global_tuple = lookup_global_tuple(client, key);
280*a3cefe7fSPierre Pronchery 	if (global_tuple != NULL && global_tuple->flags & PKGCONF_PKG_TUPLEF_OVERRIDE)
281*a3cefe7fSPierre Pronchery 		return global_tuple->value;
282*a3cefe7fSPierre Pronchery 
283*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
284*a3cefe7fSPierre Pronchery 	{
285*a3cefe7fSPierre Pronchery 		pkgconf_tuple_t *tuple = node->data;
286*a3cefe7fSPierre Pronchery 
287*a3cefe7fSPierre Pronchery 		if (!strcmp(tuple->key, key))
288*a3cefe7fSPierre Pronchery 			return tuple->value;
289*a3cefe7fSPierre Pronchery 	}
290*a3cefe7fSPierre Pronchery 
291*a3cefe7fSPierre Pronchery 	if (global_tuple != NULL)
292*a3cefe7fSPierre Pronchery 		return global_tuple->value;
293*a3cefe7fSPierre Pronchery 
294*a3cefe7fSPierre Pronchery 	return NULL;
295*a3cefe7fSPierre Pronchery }
296*a3cefe7fSPierre Pronchery 
297*a3cefe7fSPierre Pronchery /*
298*a3cefe7fSPierre Pronchery  * !doc
299*a3cefe7fSPierre Pronchery  *
300*a3cefe7fSPierre Pronchery  * .. c:function:: char *pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value, unsigned int flags)
301*a3cefe7fSPierre Pronchery  *
302*a3cefe7fSPierre Pronchery  *    Parse an expression for variable substitution.
303*a3cefe7fSPierre Pronchery  *
304*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to access.
305*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* list: The variable list to search for variables (along side the global variable list).
306*a3cefe7fSPierre Pronchery  *    :param char* value: The ``key=value`` string to parse.
307*a3cefe7fSPierre Pronchery  *    :param uint flags: Any flags to consider while parsing.
308*a3cefe7fSPierre Pronchery  *    :return: the variable data with any variables substituted
309*a3cefe7fSPierre Pronchery  *    :rtype: char *
310*a3cefe7fSPierre Pronchery  */
311*a3cefe7fSPierre Pronchery char *
pkgconf_tuple_parse(const pkgconf_client_t * client,pkgconf_list_t * vars,const char * value,unsigned int flags)312*a3cefe7fSPierre Pronchery pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value, unsigned int flags)
313*a3cefe7fSPierre Pronchery {
314*a3cefe7fSPierre Pronchery 	char buf[PKGCONF_BUFSIZE];
315*a3cefe7fSPierre Pronchery 	const char *ptr;
316*a3cefe7fSPierre Pronchery 	char *bptr = buf;
317*a3cefe7fSPierre Pronchery 
318*a3cefe7fSPierre Pronchery 	if (!(client->flags & PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES) &&
319*a3cefe7fSPierre Pronchery 		(!(flags & PKGCONF_PKG_PROPF_UNINSTALLED) || (client->flags & PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES)))
320*a3cefe7fSPierre Pronchery 	{
321*a3cefe7fSPierre Pronchery 		if (*value == '/' && client->sysroot_dir != NULL && strncmp(value, client->sysroot_dir, strlen(client->sysroot_dir)))
322*a3cefe7fSPierre Pronchery 			bptr += pkgconf_strlcpy(buf, client->sysroot_dir, sizeof buf);
323*a3cefe7fSPierre Pronchery 	}
324*a3cefe7fSPierre Pronchery 
325*a3cefe7fSPierre Pronchery 	for (ptr = value; *ptr != '\0' && bptr - buf < PKGCONF_BUFSIZE; ptr++)
326*a3cefe7fSPierre Pronchery 	{
327*a3cefe7fSPierre Pronchery 		if (*ptr != '$' || (*ptr == '$' && *(ptr + 1) != '{'))
328*a3cefe7fSPierre Pronchery 			*bptr++ = *ptr;
329*a3cefe7fSPierre Pronchery 		else if (*(ptr + 1) == '{')
330*a3cefe7fSPierre Pronchery 		{
331*a3cefe7fSPierre Pronchery 			char varname[PKGCONF_ITEM_SIZE];
332*a3cefe7fSPierre Pronchery 			char *vend = varname + PKGCONF_ITEM_SIZE - 1;
333*a3cefe7fSPierre Pronchery 			char *vptr = varname;
334*a3cefe7fSPierre Pronchery 			const char *pptr;
335*a3cefe7fSPierre Pronchery 			char *kv, *parsekv;
336*a3cefe7fSPierre Pronchery 
337*a3cefe7fSPierre Pronchery 			*vptr = '\0';
338*a3cefe7fSPierre Pronchery 
339*a3cefe7fSPierre Pronchery 			for (pptr = ptr + 2; *pptr != '\0'; pptr++)
340*a3cefe7fSPierre Pronchery 			{
341*a3cefe7fSPierre Pronchery 				if (*pptr != '}')
342*a3cefe7fSPierre Pronchery 				{
343*a3cefe7fSPierre Pronchery 					if (vptr < vend)
344*a3cefe7fSPierre Pronchery 						*vptr++ = *pptr;
345*a3cefe7fSPierre Pronchery 					else
346*a3cefe7fSPierre Pronchery 					{
347*a3cefe7fSPierre Pronchery 						*vptr = '\0';
348*a3cefe7fSPierre Pronchery 						break;
349*a3cefe7fSPierre Pronchery 					}
350*a3cefe7fSPierre Pronchery 				}
351*a3cefe7fSPierre Pronchery 				else
352*a3cefe7fSPierre Pronchery 				{
353*a3cefe7fSPierre Pronchery 					*vptr = '\0';
354*a3cefe7fSPierre Pronchery 					break;
355*a3cefe7fSPierre Pronchery 				}
356*a3cefe7fSPierre Pronchery 			}
357*a3cefe7fSPierre Pronchery 
358*a3cefe7fSPierre Pronchery 			PKGCONF_TRACE(client, "lookup tuple %s", varname);
359*a3cefe7fSPierre Pronchery 
360*a3cefe7fSPierre Pronchery 			size_t remain = PKGCONF_BUFSIZE - (bptr - buf);
361*a3cefe7fSPierre Pronchery 			ptr += (pptr - ptr);
362*a3cefe7fSPierre Pronchery 			kv = pkgconf_tuple_find_global(client, varname);
363*a3cefe7fSPierre Pronchery 			if (kv != NULL)
364*a3cefe7fSPierre Pronchery 			{
365*a3cefe7fSPierre Pronchery 				size_t nlen = pkgconf_strlcpy(bptr, kv, remain);
366*a3cefe7fSPierre Pronchery 				if (nlen > remain)
367*a3cefe7fSPierre Pronchery 				{
368*a3cefe7fSPierre Pronchery 					pkgconf_warn(client, "warning: truncating very long variable to 64KB\n");
369*a3cefe7fSPierre Pronchery 
370*a3cefe7fSPierre Pronchery 					bptr = buf + (PKGCONF_BUFSIZE - 1);
371*a3cefe7fSPierre Pronchery 					break;
372*a3cefe7fSPierre Pronchery 				}
373*a3cefe7fSPierre Pronchery 
374*a3cefe7fSPierre Pronchery 				bptr += nlen;
375*a3cefe7fSPierre Pronchery 			}
376*a3cefe7fSPierre Pronchery 			else
377*a3cefe7fSPierre Pronchery 			{
378*a3cefe7fSPierre Pronchery 				kv = pkgconf_tuple_find(client, vars, varname);
379*a3cefe7fSPierre Pronchery 
380*a3cefe7fSPierre Pronchery 				if (kv != NULL)
381*a3cefe7fSPierre Pronchery 				{
382*a3cefe7fSPierre Pronchery 					size_t nlen;
383*a3cefe7fSPierre Pronchery 
384*a3cefe7fSPierre Pronchery 					parsekv = pkgconf_tuple_parse(client, vars, kv, flags);
385*a3cefe7fSPierre Pronchery 					nlen = pkgconf_strlcpy(bptr, parsekv, remain);
386*a3cefe7fSPierre Pronchery 					free(parsekv);
387*a3cefe7fSPierre Pronchery 
388*a3cefe7fSPierre Pronchery 					if (nlen > remain)
389*a3cefe7fSPierre Pronchery 					{
390*a3cefe7fSPierre Pronchery 						pkgconf_warn(client, "warning: truncating very long variable to 64KB\n");
391*a3cefe7fSPierre Pronchery 
392*a3cefe7fSPierre Pronchery 						bptr = buf + (PKGCONF_BUFSIZE - 1);
393*a3cefe7fSPierre Pronchery 						break;
394*a3cefe7fSPierre Pronchery 					}
395*a3cefe7fSPierre Pronchery 
396*a3cefe7fSPierre Pronchery 					bptr += nlen;
397*a3cefe7fSPierre Pronchery 				}
398*a3cefe7fSPierre Pronchery 			}
399*a3cefe7fSPierre Pronchery 		}
400*a3cefe7fSPierre Pronchery 	}
401*a3cefe7fSPierre Pronchery 
402*a3cefe7fSPierre Pronchery 	*bptr = '\0';
403*a3cefe7fSPierre Pronchery 
404*a3cefe7fSPierre Pronchery 	/*
405*a3cefe7fSPierre Pronchery 	 * Sigh.  Somebody actually attempted to use freedesktop.org pkg-config's broken sysroot support,
406*a3cefe7fSPierre Pronchery 	 * which was written by somebody who did not understand how sysroots are supposed to work.  This
407*a3cefe7fSPierre Pronchery 	 * results in an incorrect path being built as the sysroot will be prepended twice, once explicitly,
408*a3cefe7fSPierre Pronchery 	 * and once by variable expansion (the pkgconf approach).  We could simply make ${pc_sysrootdir} blank,
409*a3cefe7fSPierre Pronchery 	 * but sometimes it is necessary to know the explicit sysroot path for other reasons, so we can't really
410*a3cefe7fSPierre Pronchery 	 * do that.
411*a3cefe7fSPierre Pronchery 	 *
412*a3cefe7fSPierre Pronchery 	 * As a result, we check to see if ${pc_sysrootdir} is prepended as a duplicate, and if so, remove the
413*a3cefe7fSPierre Pronchery 	 * prepend.  This allows us to handle both our approach and the broken freedesktop.org implementation's
414*a3cefe7fSPierre Pronchery 	 * approach.  Because a path can be shorter than ${pc_sysrootdir}, we do some checks first to ensure it's
415*a3cefe7fSPierre Pronchery 	 * safe to skip ahead in the string to scan for our sysroot dir.
416*a3cefe7fSPierre Pronchery 	 *
417*a3cefe7fSPierre Pronchery 	 * Finally, we call pkgconf_path_relocate() to clean the path of spurious elements.
418*a3cefe7fSPierre Pronchery 	 *
419*a3cefe7fSPierre Pronchery 	 * New in 1.9: Only attempt to rewrite the sysroot if we are not processing an uninstalled package.
420*a3cefe7fSPierre Pronchery 	 */
421*a3cefe7fSPierre Pronchery 	if (should_rewrite_sysroot(client, vars, buf, flags))
422*a3cefe7fSPierre Pronchery 	{
423*a3cefe7fSPierre Pronchery 		char cleanpath[PKGCONF_ITEM_SIZE];
424*a3cefe7fSPierre Pronchery 		const char *sysroot_dir = find_sysroot(client, vars);
425*a3cefe7fSPierre Pronchery 
426*a3cefe7fSPierre Pronchery 		pkgconf_strlcpy(cleanpath, buf + strlen(sysroot_dir), sizeof cleanpath);
427*a3cefe7fSPierre Pronchery 		pkgconf_path_relocate(cleanpath, sizeof cleanpath);
428*a3cefe7fSPierre Pronchery 
429*a3cefe7fSPierre Pronchery 		return strdup(cleanpath);
430*a3cefe7fSPierre Pronchery 	}
431*a3cefe7fSPierre Pronchery 
432*a3cefe7fSPierre Pronchery 	return strdup(buf);
433*a3cefe7fSPierre Pronchery }
434*a3cefe7fSPierre Pronchery 
435*a3cefe7fSPierre Pronchery /*
436*a3cefe7fSPierre Pronchery  * !doc
437*a3cefe7fSPierre Pronchery  *
438*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list)
439*a3cefe7fSPierre Pronchery  *
440*a3cefe7fSPierre Pronchery  *    Deletes a variable object, removing it from any variable lists and releasing any memory associated
441*a3cefe7fSPierre Pronchery  *    with it.
442*a3cefe7fSPierre Pronchery  *
443*a3cefe7fSPierre Pronchery  *    :param pkgconf_tuple_t* tuple: The variable object to release.
444*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* list: The variable list the variable object is attached to.
445*a3cefe7fSPierre Pronchery  *    :return: nothing
446*a3cefe7fSPierre Pronchery  */
447*a3cefe7fSPierre Pronchery void
pkgconf_tuple_free_entry(pkgconf_tuple_t * tuple,pkgconf_list_t * list)448*a3cefe7fSPierre Pronchery pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list)
449*a3cefe7fSPierre Pronchery {
450*a3cefe7fSPierre Pronchery 	pkgconf_node_delete(&tuple->iter, list);
451*a3cefe7fSPierre Pronchery 
452*a3cefe7fSPierre Pronchery 	free(tuple->key);
453*a3cefe7fSPierre Pronchery 	free(tuple->value);
454*a3cefe7fSPierre Pronchery 	free(tuple);
455*a3cefe7fSPierre Pronchery }
456*a3cefe7fSPierre Pronchery 
457*a3cefe7fSPierre Pronchery /*
458*a3cefe7fSPierre Pronchery  * !doc
459*a3cefe7fSPierre Pronchery  *
460*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_tuple_free(pkgconf_list_t *list)
461*a3cefe7fSPierre Pronchery  *
462*a3cefe7fSPierre Pronchery  *    Deletes a variable list and any variables attached to it.
463*a3cefe7fSPierre Pronchery  *
464*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* list: The variable list to delete.
465*a3cefe7fSPierre Pronchery  *    :return: nothing
466*a3cefe7fSPierre Pronchery  */
467*a3cefe7fSPierre Pronchery void
pkgconf_tuple_free(pkgconf_list_t * list)468*a3cefe7fSPierre Pronchery pkgconf_tuple_free(pkgconf_list_t *list)
469*a3cefe7fSPierre Pronchery {
470*a3cefe7fSPierre Pronchery 	pkgconf_node_t *node, *next;
471*a3cefe7fSPierre Pronchery 
472*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
473*a3cefe7fSPierre Pronchery 		pkgconf_tuple_free_entry(node->data, list);
474*a3cefe7fSPierre Pronchery 
475*a3cefe7fSPierre Pronchery 	pkgconf_list_zero(list);
476*a3cefe7fSPierre Pronchery }
477