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