1 /*
2  * fragment.c
3  * Management of fragment lists.
4  *
5  * Copyright (c) 2012, 2013, 2014 pkgconf authors (see AUTHORS).
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * This software is provided 'as is' and without any warranty, express or
12  * implied.  In no event shall the authors be liable for any damages arising
13  * from the use of this software.
14  */
15 
16 #include <libpkgconf/stdinc.h>
17 #include <libpkgconf/libpkgconf.h>
18 
19 /*
20  * !doc
21  *
22  * libpkgconf `fragment` module
23  * ============================
24  *
25  * The `fragment` module provides low-level management and rendering of fragment lists.  A
26  * `fragment list` contains various `fragments` of text (such as ``-I /usr/include``) in a matter
27  * which is composable, mergeable and reorderable.
28  */
29 
30 struct pkgconf_fragment_check {
31 	char *token;
32 	size_t len;
33 };
34 
35 static inline bool
pkgconf_fragment_is_unmergeable(const char * string)36 pkgconf_fragment_is_unmergeable(const char *string)
37 {
38 	static const struct pkgconf_fragment_check check_fragments[] = {
39 		{"-framework", 10},
40 		{"-isystem", 8},
41 		{"-idirafter", 10},
42 		{"-pthread", 8},
43 		{"-Wa,", 4},
44 		{"-Wl,", 4},
45 		{"-Wp,", 4},
46 		{"-trigraphs", 10},
47 		{"-pedantic", 9},
48 		{"-ansi", 5},
49 		{"-std=", 5},
50 		{"-stdlib=", 8},
51 		{"-include", 8},
52 		{"-nostdinc", 9},
53 		{"-nostdlibinc", 12},
54 		{"-nobuiltininc", 13},
55 	};
56 
57 	if (*string != '-')
58 		return true;
59 
60 	for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++)
61 		if (!strncmp(string, check_fragments[i].token, check_fragments[i].len))
62 			return true;
63 
64 	/* only one pair of {-flag, arg} may be merged together */
65 	if (strchr(string, ' ') != NULL)
66 		return false;
67 
68 	return false;
69 }
70 
71 static inline bool
pkgconf_fragment_should_munge(const char * string,const char * sysroot_dir)72 pkgconf_fragment_should_munge(const char *string, const char *sysroot_dir)
73 {
74 	if (*string != '/')
75 		return false;
76 
77 	if (sysroot_dir != NULL && strncmp(sysroot_dir, string, strlen(sysroot_dir)))
78 		return true;
79 
80 	return false;
81 }
82 
83 static inline bool
pkgconf_fragment_is_special(const char * string)84 pkgconf_fragment_is_special(const char *string)
85 {
86 	if (*string != '-')
87 		return true;
88 
89 	if (!strncmp(string, "-lib:", 5))
90 		return true;
91 
92 	return pkgconf_fragment_is_unmergeable(string);
93 }
94 
95 static inline void
pkgconf_fragment_munge(const pkgconf_client_t * client,char * buf,size_t buflen,const char * source,const char * sysroot_dir)96 pkgconf_fragment_munge(const pkgconf_client_t *client, char *buf, size_t buflen, const char *source, const char *sysroot_dir)
97 {
98 	*buf = '\0';
99 
100 	if (sysroot_dir == NULL)
101 		sysroot_dir = pkgconf_tuple_find_global(client, "pc_sysrootdir");
102 
103 	if (sysroot_dir != NULL && pkgconf_fragment_should_munge(source, sysroot_dir))
104 		pkgconf_strlcat(buf, sysroot_dir, buflen);
105 
106 	pkgconf_strlcat(buf, source, buflen);
107 
108 	if (*buf == '/' && !(client->flags & PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS))
109 		pkgconf_path_relocate(buf, buflen);
110 }
111 
112 static inline char *
pkgconf_fragment_copy_munged(const pkgconf_client_t * client,const char * source)113 pkgconf_fragment_copy_munged(const pkgconf_client_t *client, const char *source)
114 {
115 	char mungebuf[PKGCONF_ITEM_SIZE];
116 	pkgconf_fragment_munge(client, mungebuf, sizeof mungebuf, source, client->sysroot_dir);
117 	return strdup(mungebuf);
118 }
119 
120 /*
121  * !doc
122  *
123  * .. c:function:: void pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string)
124  *
125  *    Adds a `fragment` of text to a `fragment list`, possibly modifying the fragment if a sysroot is set.
126  *
127  *    :param pkgconf_client_t* client: The pkgconf client being accessed.
128  *    :param pkgconf_list_t* list: The fragment list.
129  *    :param char* string: The string of text to add as a fragment to the fragment list.
130  *    :return: nothing
131  */
132 void
pkgconf_fragment_add(const pkgconf_client_t * client,pkgconf_list_t * list,const char * string)133 pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string)
134 {
135 	pkgconf_fragment_t *frag;
136 
137 	if (*string == '\0')
138 		return;
139 
140 	if (strlen(string) > 1 && !pkgconf_fragment_is_special(string))
141 	{
142 		frag = calloc(sizeof(pkgconf_fragment_t), 1);
143 
144 		frag->type = *(string + 1);
145 		frag->data = pkgconf_fragment_copy_munged(client, string + 2);
146 
147 		PKGCONF_TRACE(client, "added fragment {%c, '%s'} to list @%p", frag->type, frag->data, list);
148 	}
149 	else
150 	{
151 		char mungebuf[PKGCONF_ITEM_SIZE];
152 
153 		if (list->tail != NULL && list->tail->data != NULL &&
154 		    !(client->flags & PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS))
155 		{
156 			pkgconf_fragment_t *parent = list->tail->data;
157 
158 			/* only attempt to merge 'special' fragments together */
159 			if (!parent->type && pkgconf_fragment_is_unmergeable(parent->data))
160 			{
161 				size_t len;
162 				char *newdata;
163 
164 				pkgconf_fragment_munge(client, mungebuf, sizeof mungebuf, string, NULL);
165 
166 				len = strlen(parent->data) + strlen(mungebuf) + 2;
167 				newdata = malloc(len);
168 
169 				pkgconf_strlcpy(newdata, parent->data, len);
170 				pkgconf_strlcat(newdata, " ", len);
171 				pkgconf_strlcat(newdata, mungebuf, len);
172 
173 				PKGCONF_TRACE(client, "merging '%s' to '%s' to form fragment {'%s'} in list @%p", mungebuf, parent->data, newdata, list);
174 
175 				free(parent->data);
176 				parent->data = newdata;
177 				parent->merged = true;
178 
179 				/* use a copy operation to force a dedup */
180 				pkgconf_node_delete(&parent->iter, list);
181 				pkgconf_fragment_copy(client, list, parent, false);
182 
183 				/* the fragment list now (maybe) has the copied node, so free the original */
184 				free(parent->data);
185 				free(parent);
186 
187 				return;
188 			}
189 		}
190 
191 		frag = calloc(sizeof(pkgconf_fragment_t), 1);
192 
193 		frag->type = 0;
194 		frag->data = strdup(string);
195 
196 		PKGCONF_TRACE(client, "created special fragment {'%s'} in list @%p", frag->data, list);
197 	}
198 
199 	pkgconf_node_insert_tail(&frag->iter, frag, list);
200 }
201 
202 static inline pkgconf_fragment_t *
pkgconf_fragment_lookup(pkgconf_list_t * list,const pkgconf_fragment_t * base)203 pkgconf_fragment_lookup(pkgconf_list_t *list, const pkgconf_fragment_t *base)
204 {
205 	pkgconf_node_t *node;
206 
207 	PKGCONF_FOREACH_LIST_ENTRY_REVERSE(list->tail, node)
208 	{
209 		pkgconf_fragment_t *frag = node->data;
210 
211 		if (base->type != frag->type)
212 			continue;
213 
214 		if (!strcmp(base->data, frag->data))
215 			return frag;
216 	}
217 
218 	return NULL;
219 }
220 
221 static inline bool
pkgconf_fragment_can_merge_back(const pkgconf_fragment_t * base,unsigned int flags,bool is_private)222 pkgconf_fragment_can_merge_back(const pkgconf_fragment_t *base, unsigned int flags, bool is_private)
223 {
224 	(void) flags;
225 
226 	if (base->type == 'l')
227 	{
228 		if (is_private)
229 			return false;
230 
231 		return true;
232 	}
233 
234 	if (base->type == 'F')
235 		return false;
236 	if (base->type == 'L')
237 		return false;
238 	if (base->type == 'I')
239 		return false;
240 
241 	return true;
242 }
243 
244 static inline bool
pkgconf_fragment_can_merge(const pkgconf_fragment_t * base,unsigned int flags,bool is_private)245 pkgconf_fragment_can_merge(const pkgconf_fragment_t *base, unsigned int flags, bool is_private)
246 {
247 	(void) flags;
248 
249 	if (is_private)
250 		return false;
251 
252 	return pkgconf_fragment_is_unmergeable(base->data);
253 }
254 
255 static inline pkgconf_fragment_t *
pkgconf_fragment_exists(pkgconf_list_t * list,const pkgconf_fragment_t * base,unsigned int flags,bool is_private)256 pkgconf_fragment_exists(pkgconf_list_t *list, const pkgconf_fragment_t *base, unsigned int flags, bool is_private)
257 {
258 	if (!pkgconf_fragment_can_merge_back(base, flags, is_private))
259 		return NULL;
260 
261 	if (!pkgconf_fragment_can_merge(base, flags, is_private))
262 		return NULL;
263 
264 	return pkgconf_fragment_lookup(list, base);
265 }
266 
267 static inline bool
pkgconf_fragment_should_merge(const pkgconf_fragment_t * base)268 pkgconf_fragment_should_merge(const pkgconf_fragment_t *base)
269 {
270 	const pkgconf_fragment_t *parent;
271 
272 	/* if we are the first fragment, that means the next fragment is the same, so it's always safe. */
273 	if (base->iter.prev == NULL)
274 		return true;
275 
276 	/* this really shouldn't ever happen, but handle it */
277 	parent = base->iter.prev->data;
278 	if (parent == NULL)
279 		return true;
280 
281 	switch (parent->type)
282 	{
283 	case 'l':
284 	case 'L':
285 	case 'I':
286 		return true;
287 	default:
288 		return !base->type || parent->type == base->type;
289 	}
290 }
291 
292 /*
293  * !doc
294  *
295  * .. c:function:: bool pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag)
296  *
297  *    Checks if a `fragment` contains a `system path`.  System paths are detected at compile time and optionally overridden by
298  *    the ``PKG_CONFIG_SYSTEM_INCLUDE_PATH`` and ``PKG_CONFIG_SYSTEM_LIBRARY_PATH`` environment variables.
299  *
300  *    :param pkgconf_client_t* client: The pkgconf client object the fragment belongs to.
301  *    :param pkgconf_fragment_t* frag: The fragment being checked.
302  *    :return: true if the fragment contains a system path, else false
303  *    :rtype: bool
304  */
305 bool
pkgconf_fragment_has_system_dir(const pkgconf_client_t * client,const pkgconf_fragment_t * frag)306 pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag)
307 {
308 	const pkgconf_list_t *check_paths = NULL;
309 
310 	switch (frag->type)
311 	{
312 	case 'L':
313 		check_paths = &client->filter_libdirs;
314 		break;
315 	case 'I':
316 		check_paths = &client->filter_includedirs;
317 		break;
318 	default:
319 		return false;
320 	}
321 
322 	return pkgconf_path_match_list(frag->data, check_paths);
323 }
324 
325 /*
326  * !doc
327  *
328  * .. c:function:: void pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private)
329  *
330  *    Copies a `fragment` to another `fragment list`, possibly removing a previous copy of the `fragment`
331  *    in a process known as `mergeback`.
332  *
333  *    :param pkgconf_client_t* client: The pkgconf client being accessed.
334  *    :param pkgconf_list_t* list: The list the fragment is being added to.
335  *    :param pkgconf_fragment_t* base: The fragment being copied.
336  *    :param bool is_private: Whether the fragment list is a `private` fragment list (static linking).
337  *    :return: nothing
338  */
339 void
pkgconf_fragment_copy(const pkgconf_client_t * client,pkgconf_list_t * list,const pkgconf_fragment_t * base,bool is_private)340 pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private)
341 {
342 	pkgconf_fragment_t *frag;
343 
344 	if ((frag = pkgconf_fragment_exists(list, base, client->flags, is_private)) != NULL)
345 	{
346 		if (pkgconf_fragment_should_merge(frag))
347 			pkgconf_fragment_delete(list, frag);
348 	}
349 	else if (!is_private && !pkgconf_fragment_can_merge_back(base, client->flags, is_private) && (pkgconf_fragment_lookup(list, base) != NULL))
350 		return;
351 
352 	frag = calloc(sizeof(pkgconf_fragment_t), 1);
353 
354 	frag->type = base->type;
355 	frag->merged = base->merged;
356 	if (base->data != NULL)
357 		frag->data = strdup(base->data);
358 
359 	pkgconf_node_insert_tail(&frag->iter, frag, list);
360 }
361 
362 /*
363  * !doc
364  *
365  * .. c:function:: void pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base)
366  *
367  *    Copies a `fragment list` to another `fragment list`, possibly removing a previous copy of the fragments
368  *    in a process known as `mergeback`.
369  *
370  *    :param pkgconf_client_t* client: The pkgconf client being accessed.
371  *    :param pkgconf_list_t* list: The list the fragments are being added to.
372  *    :param pkgconf_list_t* base: The list the fragments are being copied from.
373  *    :return: nothing
374  */
375 void
pkgconf_fragment_copy_list(const pkgconf_client_t * client,pkgconf_list_t * list,const pkgconf_list_t * base)376 pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base)
377 {
378 	pkgconf_node_t *node;
379 
380 	PKGCONF_FOREACH_LIST_ENTRY(base->head, node)
381 	{
382 		pkgconf_fragment_t *frag = node->data;
383 
384 		pkgconf_fragment_copy(client, list, frag, true);
385 	}
386 }
387 
388 /*
389  * !doc
390  *
391  * .. c:function:: void pkgconf_fragment_filter(const pkgconf_client_t *client, pkgconf_list_t *dest, pkgconf_list_t *src, pkgconf_fragment_filter_func_t filter_func)
392  *
393  *    Copies a `fragment list` to another `fragment list` which match a user-specified filtering function.
394  *
395  *    :param pkgconf_client_t* client: The pkgconf client being accessed.
396  *    :param pkgconf_list_t* dest: The destination list.
397  *    :param pkgconf_list_t* src: The source list.
398  *    :param pkgconf_fragment_filter_func_t filter_func: The filter function to use.
399  *    :param void* data: Optional data to pass to the filter function.
400  *    :return: nothing
401  */
402 void
pkgconf_fragment_filter(const pkgconf_client_t * client,pkgconf_list_t * dest,pkgconf_list_t * src,pkgconf_fragment_filter_func_t filter_func,void * data)403 pkgconf_fragment_filter(const pkgconf_client_t *client, pkgconf_list_t *dest, pkgconf_list_t *src, pkgconf_fragment_filter_func_t filter_func, void *data)
404 {
405 	pkgconf_node_t *node;
406 
407 	PKGCONF_FOREACH_LIST_ENTRY(src->head, node)
408 	{
409 		pkgconf_fragment_t *frag = node->data;
410 
411 		if (filter_func(client, frag, data))
412 			pkgconf_fragment_copy(client, dest, frag, true);
413 	}
414 }
415 
416 static inline char *
fragment_quote(const pkgconf_fragment_t * frag)417 fragment_quote(const pkgconf_fragment_t *frag)
418 {
419 	const char *src = frag->data;
420 	ssize_t outlen = strlen(src) + 10;
421 	char *out, *dst;
422 
423 	if (frag->data == NULL)
424 		return NULL;
425 
426 	out = dst = calloc(outlen, 1);
427 
428 	for (; *src; src++)
429 	{
430 		if (((*src < ' ') ||
431 		    (*src >= (' ' + (frag->merged ? 1 : 0)) && *src < '$') ||
432 		    (*src > '$' && *src < '(') ||
433 		    (*src > ')' && *src < '+') ||
434 		    (*src > ':' && *src < '=') ||
435 		    (*src > '=' && *src < '@') ||
436 		    (*src > 'Z' && *src < '\\') ||
437 #ifndef _WIN32
438 		    (*src == '\\') ||
439 #endif
440 		    (*src > '\\' && *src < '^') ||
441 		    (*src == '`') ||
442 		    (*src > 'z' && *src < '~') ||
443 		    (*src > '~')))
444 			*dst++ = '\\';
445 
446 		*dst++ = *src;
447 
448 		if ((ptrdiff_t)(dst - out) + 2 > outlen)
449 		{
450 			ptrdiff_t offset = dst - out;
451 			outlen *= 2;
452 			out = realloc(out, outlen);
453 			dst = out + offset;
454 		}
455 	}
456 
457 	*dst = 0;
458 	return out;
459 }
460 
461 static inline size_t
pkgconf_fragment_len(const pkgconf_fragment_t * frag)462 pkgconf_fragment_len(const pkgconf_fragment_t *frag)
463 {
464 	size_t len = 1;
465 
466 	if (frag->type)
467 		len += 2;
468 
469 	if (frag->data != NULL)
470 	{
471 		char *quoted = fragment_quote(frag);
472 		len += strlen(quoted);
473 		free(quoted);
474 	}
475 
476 	return len;
477 }
478 
479 static size_t
fragment_render_len(const pkgconf_list_t * list,bool escape)480 fragment_render_len(const pkgconf_list_t *list, bool escape)
481 {
482 	(void) escape;
483 
484 	size_t out = 1;		/* trailing nul */
485 	pkgconf_node_t *node;
486 
487 	PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
488 	{
489 		const pkgconf_fragment_t *frag = node->data;
490 		out += pkgconf_fragment_len(frag);
491 	}
492 
493 	return out;
494 }
495 
496 static void
fragment_render_buf(const pkgconf_list_t * list,char * buf,size_t buflen,bool escape)497 fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape)
498 {
499 	(void) escape;
500 
501 	pkgconf_node_t *node;
502 	char *bptr = buf;
503 
504 	memset(buf, 0, buflen);
505 
506 	PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
507 	{
508 		const pkgconf_fragment_t *frag = node->data;
509 		size_t buf_remaining = buflen - (bptr - buf);
510 		char *quoted = fragment_quote(frag);
511 
512 		if (strlen(quoted) > buf_remaining)
513 		{
514 			free(quoted);
515 			break;
516 		}
517 
518 		if (frag->type)
519 		{
520 			*bptr++ = '-';
521 			*bptr++ = frag->type;
522 		}
523 
524 		if (quoted != NULL)
525 		{
526 			bptr += pkgconf_strlcpy(bptr, quoted, buf_remaining);
527 			free(quoted);
528 		}
529 
530 		*bptr++ = ' ';
531 	}
532 
533 	*bptr = '\0';
534 }
535 
536 static const pkgconf_fragment_render_ops_t default_render_ops = {
537 	.render_len = fragment_render_len,
538 	.render_buf = fragment_render_buf
539 };
540 
541 /*
542  * !doc
543  *
544  * .. c:function:: size_t pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops)
545  *
546  *    Calculates the required memory to store a `fragment list` when rendered as a string.
547  *
548  *    :param pkgconf_list_t* list: The `fragment list` being rendered.
549  *    :param bool escape: Whether or not to escape special shell characters (deprecated).
550  *    :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
551  *    :return: the amount of bytes required to represent the `fragment list` when rendered
552  *    :rtype: size_t
553  */
554 size_t
pkgconf_fragment_render_len(const pkgconf_list_t * list,bool escape,const pkgconf_fragment_render_ops_t * ops)555 pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops)
556 {
557 	(void) escape;
558 
559 	ops = ops != NULL ? ops : &default_render_ops;
560 	return ops->render_len(list, true);
561 }
562 
563 /*
564  * !doc
565  *
566  * .. c:function:: void pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape, const pkgconf_fragment_render_ops_t *ops)
567  *
568  *    Renders a `fragment list` into a buffer.
569  *
570  *    :param pkgconf_list_t* list: The `fragment list` being rendered.
571  *    :param char* buf: The buffer to render the fragment list into.
572  *    :param size_t buflen: The length of the buffer.
573  *    :param bool escape: Whether or not to escape special shell characters (deprecated).
574  *    :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
575  *    :return: nothing
576  */
577 void
pkgconf_fragment_render_buf(const pkgconf_list_t * list,char * buf,size_t buflen,bool escape,const pkgconf_fragment_render_ops_t * ops)578 pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape, const pkgconf_fragment_render_ops_t *ops)
579 {
580 	(void) escape;
581 
582 	ops = ops != NULL ? ops : &default_render_ops;
583 	ops->render_buf(list, buf, buflen, true);
584 }
585 
586 /*
587  * !doc
588  *
589  * .. c:function:: char *pkgconf_fragment_render(const pkgconf_list_t *list)
590  *
591  *    Allocate memory and render a `fragment list` into it.
592  *
593  *    :param pkgconf_list_t* list: The `fragment list` being rendered.
594  *    :param bool escape: Whether or not to escape special shell characters (deprecated).
595  *    :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
596  *    :return: An allocated string containing the rendered `fragment list`.
597  *    :rtype: char *
598  */
599 char *
pkgconf_fragment_render(const pkgconf_list_t * list,bool escape,const pkgconf_fragment_render_ops_t * ops)600 pkgconf_fragment_render(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops)
601 {
602 	(void) escape;
603 
604 	size_t buflen = pkgconf_fragment_render_len(list, true, ops);
605 	char *buf = calloc(1, buflen);
606 
607 	pkgconf_fragment_render_buf(list, buf, buflen, true, ops);
608 
609 	return buf;
610 }
611 
612 /*
613  * !doc
614  *
615  * .. c:function:: void pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node)
616  *
617  *    Delete a `fragment node` from a `fragment list`.
618  *
619  *    :param pkgconf_list_t* list: The `fragment list` to delete from.
620  *    :param pkgconf_fragment_t* node: The `fragment node` to delete.
621  *    :return: nothing
622  */
623 void
pkgconf_fragment_delete(pkgconf_list_t * list,pkgconf_fragment_t * node)624 pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node)
625 {
626 	pkgconf_node_delete(&node->iter, list);
627 
628 	free(node->data);
629 	free(node);
630 }
631 
632 /*
633  * !doc
634  *
635  * .. c:function:: void pkgconf_fragment_free(pkgconf_list_t *list)
636  *
637  *    Delete an entire `fragment list`.
638  *
639  *    :param pkgconf_list_t* list: The `fragment list` to delete.
640  *    :return: nothing
641  */
642 void
pkgconf_fragment_free(pkgconf_list_t * list)643 pkgconf_fragment_free(pkgconf_list_t *list)
644 {
645 	pkgconf_node_t *node, *next;
646 
647 	PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
648 	{
649 		pkgconf_fragment_t *frag = node->data;
650 
651 		free(frag->data);
652 		free(frag);
653 	}
654 }
655 
656 /*
657  * !doc
658  *
659  * .. c:function:: bool pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value)
660  *
661  *    Parse a string into a `fragment list`.
662  *
663  *    :param pkgconf_client_t* client: The pkgconf client being accessed.
664  *    :param pkgconf_list_t* list: The `fragment list` to add the fragment entries to.
665  *    :param pkgconf_list_t* vars: A list of variables to use for variable substitution.
666  *    :param char* value: The string to parse into fragments.
667  *    :return: true on success, false on parse error
668  */
669 bool
pkgconf_fragment_parse(const pkgconf_client_t * client,pkgconf_list_t * list,pkgconf_list_t * vars,const char * value)670 pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value)
671 {
672 	int i, ret, argc;
673 	char **argv;
674 	char *repstr = pkgconf_tuple_parse(client, vars, value);
675 
676 	PKGCONF_TRACE(client, "post-subst: [%s] -> [%s]", value, repstr);
677 
678 	ret = pkgconf_argv_split(repstr, &argc, &argv);
679 	if (ret < 0)
680 	{
681 		PKGCONF_TRACE(client, "unable to parse fragment string [%s]", repstr);
682 		free(repstr);
683 		return false;
684 	}
685 
686 	for (i = 0; i < argc; i++)
687 	{
688 		if (argv[i] == NULL)
689 		{
690 			PKGCONF_TRACE(client, "parsed fragment string is inconsistent: argc = %d while argv[%d] == NULL", argc, i);
691 			pkgconf_argv_free(argv);
692 			free(repstr);
693 			return false;
694 		}
695 
696 		pkgconf_fragment_add(client, list, argv[i]);
697 	}
698 
699 	pkgconf_argv_free(argv);
700 	free(repstr);
701 
702 	return true;
703 }
704