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 {"-nodefaultlibs", 14},
56 };
57
58 if (*string != '-')
59 return true;
60
61 for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++)
62 if (!strncmp(string, check_fragments[i].token, check_fragments[i].len))
63 return true;
64
65 /* only one pair of {-flag, arg} may be merged together */
66 if (strchr(string, ' ') != NULL)
67 return false;
68
69 return false;
70 }
71
72 static inline bool
pkgconf_fragment_should_munge(const char * string,const char * sysroot_dir)73 pkgconf_fragment_should_munge(const char *string, const char *sysroot_dir)
74 {
75 if (*string != '/')
76 return false;
77
78 if (sysroot_dir != NULL && strncmp(sysroot_dir, string, strlen(sysroot_dir)))
79 return true;
80
81 return false;
82 }
83
84 static inline bool
pkgconf_fragment_is_groupable(const char * string)85 pkgconf_fragment_is_groupable(const char *string)
86 {
87 static const struct pkgconf_fragment_check check_fragments[] = {
88 {"-Wl,--start-group", 17},
89 {"-framework", 10},
90 {"-isystem", 8},
91 {"-idirafter", 10},
92 {"-include", 8},
93 };
94
95 for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++)
96 if (!strncmp(string, check_fragments[i].token, check_fragments[i].len))
97 return true;
98
99 return false;
100 }
101
102 static inline bool
pkgconf_fragment_is_terminus(const char * string)103 pkgconf_fragment_is_terminus(const char *string)
104 {
105 static const struct pkgconf_fragment_check check_fragments[] = {
106 {"-Wl,--end-group", 15},
107 };
108
109 for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++)
110 if (!strncmp(string, check_fragments[i].token, check_fragments[i].len))
111 return true;
112
113 return false;
114 }
115
116 static inline bool
pkgconf_fragment_is_special(const char * string)117 pkgconf_fragment_is_special(const char *string)
118 {
119 if (*string != '-')
120 return true;
121
122 if (!strncmp(string, "-lib:", 5))
123 return true;
124
125 return pkgconf_fragment_is_unmergeable(string);
126 }
127
128 static inline void
pkgconf_fragment_munge(const pkgconf_client_t * client,char * buf,size_t buflen,const char * source,const char * sysroot_dir,unsigned int flags)129 pkgconf_fragment_munge(const pkgconf_client_t *client, char *buf, size_t buflen, const char *source, const char *sysroot_dir, unsigned int flags)
130 {
131 *buf = '\0';
132
133 if (!(flags & PKGCONF_PKG_PROPF_UNINSTALLED) || (client->flags & PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES))
134 {
135 if (sysroot_dir == NULL)
136 sysroot_dir = pkgconf_tuple_find_global(client, "pc_sysrootdir");
137
138 if (sysroot_dir != NULL && pkgconf_fragment_should_munge(source, sysroot_dir))
139 pkgconf_strlcat(buf, sysroot_dir, buflen);
140 }
141
142 pkgconf_strlcat(buf, source, buflen);
143
144 if (*buf == '/' && !(client->flags & PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS))
145 pkgconf_path_relocate(buf, buflen);
146 }
147
148 static inline char *
pkgconf_fragment_copy_munged(const pkgconf_client_t * client,const char * source,unsigned int flags)149 pkgconf_fragment_copy_munged(const pkgconf_client_t *client, const char *source, unsigned int flags)
150 {
151 char mungebuf[PKGCONF_ITEM_SIZE];
152 pkgconf_fragment_munge(client, mungebuf, sizeof mungebuf, source, client->sysroot_dir, flags);
153 return strdup(mungebuf);
154 }
155
156 /*
157 * !doc
158 *
159 * .. c:function:: void pkgconf_fragment_insert(const pkgconf_client_t *client, pkgconf_list_t *list, char type, const char *data, bool tail)
160 *
161 * Adds a `fragment` of text to a `fragment list` directly without interpreting it.
162 *
163 * :param pkgconf_client_t* client: The pkgconf client being accessed.
164 * :param pkgconf_list_t* list: The fragment list.
165 * :param char type: The type of the fragment.
166 * :param char* data: The data of the fragment.
167 * :param bool tail: Whether to place the fragment at the beginning of the list or the end.
168 * :return: nothing
169 */
170 void
pkgconf_fragment_insert(const pkgconf_client_t * client,pkgconf_list_t * list,char type,const char * data,bool tail)171 pkgconf_fragment_insert(const pkgconf_client_t *client, pkgconf_list_t *list, char type, const char *data, bool tail)
172 {
173 pkgconf_fragment_t *frag;
174
175 frag = calloc(1, sizeof(pkgconf_fragment_t));
176 frag->type = type;
177 frag->data = pkgconf_fragment_copy_munged(client, data, 0);
178
179 if (tail)
180 {
181 pkgconf_node_insert_tail(&frag->iter, frag, list);
182 return;
183 }
184
185 pkgconf_node_insert(&frag->iter, frag, list);
186 }
187
188 /*
189 * !doc
190 *
191 * .. c:function:: void pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string, unsigned int flags)
192 *
193 * Adds a `fragment` of text to a `fragment list`, possibly modifying the fragment if a sysroot is set.
194 *
195 * :param pkgconf_client_t* client: The pkgconf client being accessed.
196 * :param pkgconf_list_t* list: The fragment list.
197 * :param char* string: The string of text to add as a fragment to the fragment list.
198 * :param uint flags: Parsing-related flags for the package.
199 * :return: nothing
200 */
201 void
pkgconf_fragment_add(const pkgconf_client_t * client,pkgconf_list_t * list,const char * string,unsigned int flags)202 pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string, unsigned int flags)
203 {
204 pkgconf_list_t *target = list;
205 pkgconf_fragment_t *frag;
206
207 if (*string == '\0')
208 return;
209
210 if (list->tail != NULL && list->tail->data != NULL &&
211 !(client->flags & PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS))
212 {
213 pkgconf_fragment_t *parent = list->tail->data;
214
215 /* only attempt to merge 'special' fragments together */
216 if (!parent->type && parent->data != NULL &&
217 pkgconf_fragment_is_unmergeable(parent->data) &&
218 !(parent->flags & PKGCONF_PKG_FRAGF_TERMINATED))
219 {
220 if (pkgconf_fragment_is_groupable(parent->data))
221 target = &parent->children;
222
223 if (pkgconf_fragment_is_terminus(string))
224 parent->flags |= PKGCONF_PKG_FRAGF_TERMINATED;
225
226 PKGCONF_TRACE(client, "adding fragment as child to list @%p", target);
227 }
228 }
229
230 frag = calloc(1, sizeof(pkgconf_fragment_t));
231 if (frag == NULL)
232 {
233 PKGCONF_TRACE(client, "failed to add new fragment due to allocation failure to list @%p", target);
234 return;
235 }
236
237 if (strlen(string) > 1 && !pkgconf_fragment_is_special(string))
238 {
239 frag->type = *(string + 1);
240 frag->data = pkgconf_fragment_copy_munged(client, string + 2, flags);
241
242 PKGCONF_TRACE(client, "added fragment {%c, '%s'} to list @%p", frag->type, frag->data, list);
243 }
244 else
245 {
246 frag->type = 0;
247 frag->data = pkgconf_fragment_copy_munged(client, string, flags);
248
249 PKGCONF_TRACE(client, "created special fragment {'%s'} in list @%p", frag->data, target);
250 }
251
252 pkgconf_node_insert_tail(&frag->iter, frag, target);
253 }
254
255 static inline pkgconf_fragment_t *
pkgconf_fragment_lookup(pkgconf_list_t * list,const pkgconf_fragment_t * base)256 pkgconf_fragment_lookup(pkgconf_list_t *list, const pkgconf_fragment_t *base)
257 {
258 pkgconf_node_t *node;
259
260 PKGCONF_FOREACH_LIST_ENTRY_REVERSE(list->tail, node)
261 {
262 pkgconf_fragment_t *frag = node->data;
263
264 if (base->type != frag->type)
265 continue;
266
267 if (!strcmp(base->data, frag->data))
268 return frag;
269 }
270
271 return NULL;
272 }
273
274 static inline bool
pkgconf_fragment_can_merge_back(const pkgconf_fragment_t * base,unsigned int flags,bool is_private)275 pkgconf_fragment_can_merge_back(const pkgconf_fragment_t *base, unsigned int flags, bool is_private)
276 {
277 (void) flags;
278
279 if (base->type == 'l')
280 {
281 if (is_private)
282 return false;
283
284 return true;
285 }
286
287 if (base->type == 'F')
288 return false;
289 if (base->type == 'L')
290 return false;
291 if (base->type == 'I')
292 return false;
293
294 return true;
295 }
296
297 static inline bool
pkgconf_fragment_can_merge(const pkgconf_fragment_t * base,unsigned int flags,bool is_private)298 pkgconf_fragment_can_merge(const pkgconf_fragment_t *base, unsigned int flags, bool is_private)
299 {
300 (void) flags;
301
302 if (is_private)
303 return false;
304
305 if (base->children.head != NULL)
306 return false;
307
308 return pkgconf_fragment_is_unmergeable(base->data);
309 }
310
311 static inline pkgconf_fragment_t *
pkgconf_fragment_exists(pkgconf_list_t * list,const pkgconf_fragment_t * base,unsigned int flags,bool is_private)312 pkgconf_fragment_exists(pkgconf_list_t *list, const pkgconf_fragment_t *base, unsigned int flags, bool is_private)
313 {
314 if (!pkgconf_fragment_can_merge_back(base, flags, is_private))
315 return NULL;
316
317 if (!pkgconf_fragment_can_merge(base, flags, is_private))
318 return NULL;
319
320 return pkgconf_fragment_lookup(list, base);
321 }
322
323 static inline bool
pkgconf_fragment_should_merge(const pkgconf_fragment_t * base)324 pkgconf_fragment_should_merge(const pkgconf_fragment_t *base)
325 {
326 const pkgconf_fragment_t *parent;
327
328 /* if we are the first fragment, that means the next fragment is the same, so it's always safe. */
329 if (base->iter.prev == NULL)
330 return true;
331
332 /* this really shouldn't ever happen, but handle it */
333 parent = base->iter.prev->data;
334 if (parent == NULL)
335 return true;
336
337 switch (parent->type)
338 {
339 case 'l':
340 case 'L':
341 case 'I':
342 return true;
343 default:
344 return !base->type || parent->type == base->type;
345 }
346 }
347
348 /*
349 * !doc
350 *
351 * .. c:function:: bool pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag)
352 *
353 * Checks if a `fragment` contains a `system path`. System paths are detected at compile time and optionally overridden by
354 * the ``PKG_CONFIG_SYSTEM_INCLUDE_PATH`` and ``PKG_CONFIG_SYSTEM_LIBRARY_PATH`` environment variables.
355 *
356 * :param pkgconf_client_t* client: The pkgconf client object the fragment belongs to.
357 * :param pkgconf_fragment_t* frag: The fragment being checked.
358 * :return: true if the fragment contains a system path, else false
359 * :rtype: bool
360 */
361 bool
pkgconf_fragment_has_system_dir(const pkgconf_client_t * client,const pkgconf_fragment_t * frag)362 pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag)
363 {
364 const pkgconf_list_t *check_paths = NULL;
365
366 switch (frag->type)
367 {
368 case 'L':
369 check_paths = &client->filter_libdirs;
370 break;
371 case 'I':
372 check_paths = &client->filter_includedirs;
373 break;
374 default:
375 return false;
376 }
377
378 return pkgconf_path_match_list(frag->data, check_paths);
379 }
380
381 /*
382 * !doc
383 *
384 * .. c:function:: void pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private)
385 *
386 * Copies a `fragment` to another `fragment list`, possibly removing a previous copy of the `fragment`
387 * in a process known as `mergeback`.
388 *
389 * :param pkgconf_client_t* client: The pkgconf client being accessed.
390 * :param pkgconf_list_t* list: The list the fragment is being added to.
391 * :param pkgconf_fragment_t* base: The fragment being copied.
392 * :param bool is_private: Whether the fragment list is a `private` fragment list (static linking).
393 * :return: nothing
394 */
395 void
pkgconf_fragment_copy(const pkgconf_client_t * client,pkgconf_list_t * list,const pkgconf_fragment_t * base,bool is_private)396 pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private)
397 {
398 pkgconf_fragment_t *frag;
399
400 if ((frag = pkgconf_fragment_exists(list, base, client->flags, is_private)) != NULL)
401 {
402 if (pkgconf_fragment_should_merge(frag))
403 pkgconf_fragment_delete(list, frag);
404 }
405 else if (!is_private && !pkgconf_fragment_can_merge_back(base, client->flags, is_private) && (pkgconf_fragment_lookup(list, base) != NULL))
406 return;
407
408 frag = calloc(1, sizeof(pkgconf_fragment_t));
409
410 frag->type = base->type;
411 pkgconf_fragment_copy_list(client, &frag->children, &base->children);
412 if (base->data != NULL)
413 frag->data = strdup(base->data);
414
415 pkgconf_node_insert_tail(&frag->iter, frag, list);
416 }
417
418 /*
419 * !doc
420 *
421 * .. c:function:: void pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base)
422 *
423 * Copies a `fragment list` to another `fragment list`, possibly removing a previous copy of the fragments
424 * in a process known as `mergeback`.
425 *
426 * :param pkgconf_client_t* client: The pkgconf client being accessed.
427 * :param pkgconf_list_t* list: The list the fragments are being added to.
428 * :param pkgconf_list_t* base: The list the fragments are being copied from.
429 * :return: nothing
430 */
431 void
pkgconf_fragment_copy_list(const pkgconf_client_t * client,pkgconf_list_t * list,const pkgconf_list_t * base)432 pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base)
433 {
434 pkgconf_node_t *node;
435
436 PKGCONF_FOREACH_LIST_ENTRY(base->head, node)
437 {
438 pkgconf_fragment_t *frag = node->data;
439
440 pkgconf_fragment_copy(client, list, frag, true);
441 }
442 }
443
444 /*
445 * !doc
446 *
447 * .. 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)
448 *
449 * Copies a `fragment list` to another `fragment list` which match a user-specified filtering function.
450 *
451 * :param pkgconf_client_t* client: The pkgconf client being accessed.
452 * :param pkgconf_list_t* dest: The destination list.
453 * :param pkgconf_list_t* src: The source list.
454 * :param pkgconf_fragment_filter_func_t filter_func: The filter function to use.
455 * :param void* data: Optional data to pass to the filter function.
456 * :return: nothing
457 */
458 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)459 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)
460 {
461 pkgconf_node_t *node;
462
463 PKGCONF_FOREACH_LIST_ENTRY(src->head, node)
464 {
465 pkgconf_fragment_t *frag = node->data;
466
467 if (filter_func(client, frag, data))
468 pkgconf_fragment_copy(client, dest, frag, true);
469 }
470 }
471
472 static inline char *
fragment_quote(const pkgconf_fragment_t * frag)473 fragment_quote(const pkgconf_fragment_t *frag)
474 {
475 const char *src = frag->data;
476 ssize_t outlen = strlen(src) + 10;
477 char *out, *dst;
478
479 if (frag->data == NULL)
480 return NULL;
481
482 out = dst = calloc(1, outlen);
483 if (out == NULL)
484 return NULL;
485
486 for (; *src; src++)
487 {
488 if (((*src < ' ') ||
489 (*src >= (' ' + (frag->children.head != NULL ? 1 : 0)) && *src < '$') ||
490 (*src > '$' && *src < '(') ||
491 (*src > ')' && *src < '+') ||
492 (*src > ':' && *src < '=') ||
493 (*src > '=' && *src < '@') ||
494 (*src > 'Z' && *src < '\\') ||
495 #ifndef _WIN32
496 (*src == '\\') ||
497 #endif
498 (*src > '\\' && *src < '^') ||
499 (*src == '`') ||
500 (*src > 'z' && *src < '~') ||
501 (*src > '~')))
502 *dst++ = '\\';
503
504 *dst++ = *src;
505
506 if ((ptrdiff_t)(dst - out) + 2 > outlen)
507 {
508 ptrdiff_t offset = dst - out;
509 outlen *= 2;
510
511 char *newout = realloc(out, outlen);
512 if (newout == NULL)
513 {
514 free(out);
515 return NULL;
516 }
517
518 out = newout;
519 dst = out + offset;
520 }
521 }
522
523 *dst = 0;
524 return out;
525 }
526
527 static inline size_t
pkgconf_fragment_len(const pkgconf_fragment_t * frag)528 pkgconf_fragment_len(const pkgconf_fragment_t *frag)
529 {
530 size_t len = 1;
531
532 if (frag->type)
533 len += 2;
534
535 if (frag->data != NULL)
536 {
537 pkgconf_node_t *iter;
538
539 char *quoted = fragment_quote(frag);
540 len += strlen(quoted);
541 free(quoted);
542
543 PKGCONF_FOREACH_LIST_ENTRY(frag->children.head, iter)
544 {
545 const pkgconf_fragment_t *child_frag = iter->data;
546 len += pkgconf_fragment_len(child_frag) + 1;
547 }
548 }
549
550 return len;
551 }
552
553 static size_t
fragment_render_len(const pkgconf_list_t * list,bool escape)554 fragment_render_len(const pkgconf_list_t *list, bool escape)
555 {
556 (void) escape;
557
558 size_t out = 1; /* trailing nul */
559 pkgconf_node_t *node;
560
561 PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
562 {
563 const pkgconf_fragment_t *frag = node->data;
564 out += pkgconf_fragment_len(frag);
565 }
566
567 return out;
568 }
569
570 static inline size_t
fragment_render_item(const pkgconf_fragment_t * frag,char * bptr,size_t bufremain)571 fragment_render_item(const pkgconf_fragment_t *frag, char *bptr, size_t bufremain)
572 {
573 const pkgconf_node_t *iter;
574 char *base = bptr;
575
576 char *quoted = fragment_quote(frag);
577 if (quoted == NULL)
578 return 0;
579
580 if (strlen(quoted) > bufremain)
581 {
582 free(quoted);
583 return 0;
584 }
585
586 if (frag->type)
587 {
588 *bptr++ = '-';
589 *bptr++ = frag->type;
590 }
591
592 if (quoted != NULL)
593 {
594 bptr += pkgconf_strlcpy(bptr, quoted, bufremain - (bptr - base));
595 free(quoted);
596 }
597
598 PKGCONF_FOREACH_LIST_ENTRY(frag->children.head, iter)
599 {
600 const pkgconf_fragment_t *child_frag = iter->data;
601
602 *bptr++ = ' ';
603 bptr += fragment_render_item(child_frag, bptr, bufremain - (bptr - base));
604 }
605
606 return bptr - base;
607 }
608
609 static void
fragment_render_buf(const pkgconf_list_t * list,char * buf,size_t buflen,bool escape)610 fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape)
611 {
612 (void) escape;
613
614 pkgconf_node_t *node;
615 char *bptr = buf;
616
617 memset(buf, 0, buflen);
618
619 PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
620 {
621 const pkgconf_fragment_t *frag = node->data;
622 size_t buf_remaining = buflen - (bptr - buf);
623 size_t written = fragment_render_item(frag, bptr, buf_remaining);
624
625 bptr += written;
626
627 if (node->next != NULL)
628 *bptr++ = ' ';
629 }
630 }
631
632 static const pkgconf_fragment_render_ops_t default_render_ops = {
633 .render_len = fragment_render_len,
634 .render_buf = fragment_render_buf
635 };
636
637 /*
638 * !doc
639 *
640 * .. c:function:: size_t pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops)
641 *
642 * Calculates the required memory to store a `fragment list` when rendered as a string.
643 *
644 * :param pkgconf_list_t* list: The `fragment list` being rendered.
645 * :param bool escape: Whether or not to escape special shell characters (deprecated).
646 * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
647 * :return: the amount of bytes required to represent the `fragment list` when rendered
648 * :rtype: size_t
649 */
650 size_t
pkgconf_fragment_render_len(const pkgconf_list_t * list,bool escape,const pkgconf_fragment_render_ops_t * ops)651 pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops)
652 {
653 (void) escape;
654
655 ops = ops != NULL ? ops : &default_render_ops;
656 return ops->render_len(list, true);
657 }
658
659 /*
660 * !doc
661 *
662 * .. 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)
663 *
664 * Renders a `fragment list` into a buffer.
665 *
666 * :param pkgconf_list_t* list: The `fragment list` being rendered.
667 * :param char* buf: The buffer to render the fragment list into.
668 * :param size_t buflen: The length of the buffer.
669 * :param bool escape: Whether or not to escape special shell characters (deprecated).
670 * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
671 * :return: nothing
672 */
673 void
pkgconf_fragment_render_buf(const pkgconf_list_t * list,char * buf,size_t buflen,bool escape,const pkgconf_fragment_render_ops_t * ops)674 pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape, const pkgconf_fragment_render_ops_t *ops)
675 {
676 (void) escape;
677
678 ops = ops != NULL ? ops : &default_render_ops;
679 ops->render_buf(list, buf, buflen, true);
680 }
681
682 /*
683 * !doc
684 *
685 * .. c:function:: char *pkgconf_fragment_render(const pkgconf_list_t *list)
686 *
687 * Allocate memory and render a `fragment list` into it.
688 *
689 * :param pkgconf_list_t* list: The `fragment list` being rendered.
690 * :param bool escape: Whether or not to escape special shell characters (deprecated).
691 * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
692 * :return: An allocated string containing the rendered `fragment list`.
693 * :rtype: char *
694 */
695 char *
pkgconf_fragment_render(const pkgconf_list_t * list,bool escape,const pkgconf_fragment_render_ops_t * ops)696 pkgconf_fragment_render(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops)
697 {
698 (void) escape;
699
700 size_t buflen = pkgconf_fragment_render_len(list, true, ops);
701 char *buf = calloc(1, buflen);
702
703 pkgconf_fragment_render_buf(list, buf, buflen, true, ops);
704
705 return buf;
706 }
707
708 /*
709 * !doc
710 *
711 * .. c:function:: void pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node)
712 *
713 * Delete a `fragment node` from a `fragment list`.
714 *
715 * :param pkgconf_list_t* list: The `fragment list` to delete from.
716 * :param pkgconf_fragment_t* node: The `fragment node` to delete.
717 * :return: nothing
718 */
719 void
pkgconf_fragment_delete(pkgconf_list_t * list,pkgconf_fragment_t * node)720 pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node)
721 {
722 pkgconf_node_delete(&node->iter, list);
723
724 free(node->data);
725 free(node);
726 }
727
728 /*
729 * !doc
730 *
731 * .. c:function:: void pkgconf_fragment_free(pkgconf_list_t *list)
732 *
733 * Delete an entire `fragment list`.
734 *
735 * :param pkgconf_list_t* list: The `fragment list` to delete.
736 * :return: nothing
737 */
738 void
pkgconf_fragment_free(pkgconf_list_t * list)739 pkgconf_fragment_free(pkgconf_list_t *list)
740 {
741 pkgconf_node_t *node, *next;
742
743 PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
744 {
745 pkgconf_fragment_t *frag = node->data;
746
747 pkgconf_fragment_free(&frag->children);
748 free(frag->data);
749 free(frag);
750 }
751 }
752
753 /*
754 * !doc
755 *
756 * .. c:function:: bool pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value)
757 *
758 * Parse a string into a `fragment list`.
759 *
760 * :param pkgconf_client_t* client: The pkgconf client being accessed.
761 * :param pkgconf_list_t* list: The `fragment list` to add the fragment entries to.
762 * :param pkgconf_list_t* vars: A list of variables to use for variable substitution.
763 * :param uint flags: Any parsing flags to be aware of.
764 * :param char* value: The string to parse into fragments.
765 * :return: true on success, false on parse error
766 */
767 bool
pkgconf_fragment_parse(const pkgconf_client_t * client,pkgconf_list_t * list,pkgconf_list_t * vars,const char * value,unsigned int flags)768 pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value, unsigned int flags)
769 {
770 int i, ret, argc;
771 char **argv;
772 char *repstr = pkgconf_tuple_parse(client, vars, value, flags);
773
774 PKGCONF_TRACE(client, "post-subst: [%s] -> [%s]", value, repstr);
775
776 ret = pkgconf_argv_split(repstr, &argc, &argv);
777 if (ret < 0)
778 {
779 PKGCONF_TRACE(client, "unable to parse fragment string [%s]", repstr);
780 free(repstr);
781 return false;
782 }
783
784 for (i = 0; i < argc; i++)
785 {
786 PKGCONF_TRACE(client, "processing %s", argv[i]);
787
788 if (argv[i] == NULL)
789 {
790 PKGCONF_TRACE(client, "parsed fragment string is inconsistent: argc = %d while argv[%d] == NULL", argc, i);
791 pkgconf_argv_free(argv);
792 free(repstr);
793 return false;
794 }
795
796 pkgconf_fragment_add(client, list, argv[i], flags);
797 }
798
799 pkgconf_argv_free(argv);
800 free(repstr);
801
802 return true;
803 }
804