~mmach/netext73/pkgconf

« back to all changes in this revision

Viewing changes to .pc/sysroot_dir-logic.patch/libpkgconf/pkg.c

  • Committer: mmach
  • Date: 2024-02-21 19:22:23 UTC
  • Revision ID: netbit73@gmail.com-20240221192223-5l809fiqh21udwrd
1.8.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * pkg.c
 
3
 * higher-level dependency graph compilation, management and manipulation
 
4
 *
 
5
 * Copyright (c) 2011, 2012, 2013 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/config.h>
 
17
#include <libpkgconf/stdinc.h>
 
18
#include <libpkgconf/libpkgconf.h>
 
19
 
 
20
/*
 
21
 * !doc
 
22
 *
 
23
 * libpkgconf `pkg` module
 
24
 * =======================
 
25
 *
 
26
 * The `pkg` module provides dependency resolution services and the overall `.pc` file parsing
 
27
 * routines.
 
28
 */
 
29
 
 
30
#ifdef _WIN32
 
31
#       define PKG_CONFIG_REG_KEY "Software\\pkgconfig\\PKG_CONFIG_PATH"
 
32
#       undef PKG_DEFAULT_PATH
 
33
#       define PKG_DEFAULT_PATH "../lib/pkgconfig;../share/pkgconfig"
 
34
#       define strncasecmp _strnicmp
 
35
#       define strcasecmp _stricmp
 
36
#endif
 
37
 
 
38
#define PKG_CONFIG_EXT ".pc"
 
39
 
 
40
static inline bool
 
41
str_has_suffix(const char *str, const char *suffix)
 
42
{
 
43
        size_t str_len = strlen(str);
 
44
        size_t suf_len = strlen(suffix);
 
45
 
 
46
        if (str_len < suf_len)
 
47
                return false;
 
48
 
 
49
        return !strncasecmp(str + str_len - suf_len, suffix, suf_len);
 
50
}
 
51
 
 
52
static char *
 
53
pkg_get_parent_dir(pkgconf_pkg_t *pkg)
 
54
{
 
55
        char buf[PKGCONF_ITEM_SIZE], *pathbuf;
 
56
 
 
57
        pkgconf_strlcpy(buf, pkg->filename, sizeof buf);
 
58
        pathbuf = strrchr(buf, PKG_DIR_SEP_S);
 
59
        if (pathbuf == NULL)
 
60
                pathbuf = strrchr(buf, '/');
 
61
        if (pathbuf != NULL)
 
62
                pathbuf[0] = '\0';
 
63
 
 
64
        return strdup(buf);
 
65
}
 
66
 
 
67
typedef void (*pkgconf_pkg_parser_keyword_func_t)(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value);
 
68
typedef struct {
 
69
        const char *keyword;
 
70
        const pkgconf_pkg_parser_keyword_func_t func;
 
71
        const ptrdiff_t offset;
 
72
} pkgconf_pkg_parser_keyword_pair_t;
 
73
 
 
74
static int pkgconf_pkg_parser_keyword_pair_cmp(const void *key, const void *ptr)
 
75
{
 
76
        const pkgconf_pkg_parser_keyword_pair_t *pair = ptr;
 
77
        return strcasecmp(key, pair->keyword);
 
78
}
 
79
 
 
80
static void
 
81
pkgconf_pkg_parser_tuple_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
 
82
{
 
83
        (void) keyword;
 
84
        (void) lineno;
 
85
 
 
86
        char **dest = (char **)((char *) pkg + offset);
 
87
        *dest = pkgconf_tuple_parse(client, &pkg->vars, value);
 
88
}
 
89
 
 
90
static void
 
91
pkgconf_pkg_parser_version_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
 
92
{
 
93
        (void) keyword;
 
94
        (void) lineno;
 
95
        char *p, *i;
 
96
        size_t len;
 
97
        char **dest = (char **)((char *) pkg + offset);
 
98
 
 
99
        /* cut at any detected whitespace */
 
100
        p = pkgconf_tuple_parse(client, &pkg->vars, value);
 
101
 
 
102
        len = strcspn(p, " \t");
 
103
        if (len != strlen(p))
 
104
        {
 
105
                i = p + (ptrdiff_t) len;
 
106
                *i = '\0';
 
107
 
 
108
                pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: malformed version field with whitespace, trimming to [%s]\n", pkg->filename,
 
109
                             lineno, p);
 
110
        }
 
111
 
 
112
        *dest = p;
 
113
}
 
114
 
 
115
static void
 
116
pkgconf_pkg_parser_fragment_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
 
117
{
 
118
        pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
 
119
        bool ret = pkgconf_fragment_parse(client, dest, &pkg->vars, value);
 
120
 
 
121
        if (!ret)
 
122
        {
 
123
                pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: unable to parse field '%s' into an argument vector, value [%s]\n", pkg->filename,
 
124
                             lineno, keyword, value);
 
125
        }
 
126
}
 
127
 
 
128
static void
 
129
pkgconf_pkg_parser_dependency_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
 
130
{
 
131
        (void) keyword;
 
132
        (void) lineno;
 
133
 
 
134
        pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
 
135
        pkgconf_dependency_parse(client, pkg, dest, value, 0);
 
136
}
 
137
 
 
138
/* a variant of pkgconf_pkg_parser_dependency_func which colors the dependency node as an "internal" dependency. */
 
139
static void
 
140
pkgconf_pkg_parser_internal_dependency_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
 
141
{
 
142
        (void) keyword;
 
143
        (void) lineno;
 
144
 
 
145
        pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
 
146
        pkgconf_dependency_parse(client, pkg, dest, value, PKGCONF_PKG_DEPF_INTERNAL);
 
147
}
 
148
 
 
149
/* keep this in alphabetical order */
 
150
static const pkgconf_pkg_parser_keyword_pair_t pkgconf_pkg_parser_keyword_funcs[] = {
 
151
        {"CFLAGS", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, cflags)},
 
152
        {"CFLAGS.private", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, cflags_private)},
 
153
        {"Conflicts", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, conflicts)},
 
154
        {"Description", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, description)},
 
155
        {"LIBS", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, libs)},
 
156
        {"LIBS.private", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, libs_private)},
 
157
        {"Name", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, realname)},
 
158
        {"Provides", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, provides)},
 
159
        {"Requires", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, required)},
 
160
        {"Requires.internal", pkgconf_pkg_parser_internal_dependency_func, offsetof(pkgconf_pkg_t, requires_private)},
 
161
        {"Requires.private", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, requires_private)},
 
162
        {"Version", pkgconf_pkg_parser_version_func, offsetof(pkgconf_pkg_t, version)},
 
163
};
 
164
 
 
165
static void
 
166
pkgconf_pkg_parser_keyword_set(void *opaque, const size_t lineno, const char *keyword, const char *value)
 
167
{
 
168
        pkgconf_pkg_t *pkg = opaque;
 
169
 
 
170
        const pkgconf_pkg_parser_keyword_pair_t *pair = bsearch(keyword,
 
171
                pkgconf_pkg_parser_keyword_funcs, PKGCONF_ARRAY_SIZE(pkgconf_pkg_parser_keyword_funcs),
 
172
                sizeof(pkgconf_pkg_parser_keyword_pair_t), pkgconf_pkg_parser_keyword_pair_cmp);
 
173
 
 
174
        if (pair == NULL || pair->func == NULL)
 
175
                return;
 
176
 
 
177
        pair->func(pkg->owner, pkg, keyword, lineno, pair->offset, value);
 
178
}
 
179
 
 
180
static const char *
 
181
determine_prefix(const pkgconf_pkg_t *pkg, char *buf, size_t buflen)
 
182
{
 
183
        char *pathiter;
 
184
 
 
185
        pkgconf_strlcpy(buf, pkg->filename, buflen);
 
186
        pkgconf_path_relocate(buf, buflen);
 
187
 
 
188
        pathiter = strrchr(buf, PKG_DIR_SEP_S);
 
189
        if (pathiter == NULL)
 
190
                pathiter = strrchr(buf, '/');
 
191
        if (pathiter != NULL)
 
192
                pathiter[0] = '\0';
 
193
 
 
194
        pathiter = strrchr(buf, PKG_DIR_SEP_S);
 
195
        if (pathiter == NULL)
 
196
                pathiter = strrchr(buf, '/');
 
197
        if (pathiter == NULL)
 
198
                return NULL;
 
199
 
 
200
        /* parent dir is not pkgconfig, can't relocate then */
 
201
        if (strcmp(pathiter + 1, "pkgconfig"))
 
202
                return NULL;
 
203
 
 
204
        /* okay, work backwards and do it again. */
 
205
        pathiter[0] = '\0';
 
206
        pathiter = strrchr(buf, PKG_DIR_SEP_S);
 
207
        if (pathiter == NULL)
 
208
                pathiter = strrchr(buf, '/');
 
209
        if (pathiter == NULL)
 
210
                return NULL;
 
211
 
 
212
        pathiter[0] = '\0';
 
213
 
 
214
        return buf;
 
215
}
 
216
 
 
217
/*
 
218
 * Takes a real path and converts it to a pkgconf value. This means normalizing
 
219
 * directory separators and escaping things (only spaces covered atm).
 
220
 *
 
221
 * This is useful for things like prefix/pcfiledir which might get injected
 
222
 * at runtime and are not sourced from the .pc file.
 
223
 *
 
224
 * "C:\foo bar\baz" -> "C:/foo\ bar/baz"
 
225
 * "/foo bar/baz" -> "/foo\ bar/baz"
 
226
 */
 
227
static char *
 
228
convert_path_to_value(const char *path)
 
229
{
 
230
        char *buf = calloc((strlen(path) + 1) * 2, 1);
 
231
        char *bptr = buf;
 
232
        const char *i;
 
233
 
 
234
        for (i = path; *i != '\0'; i++)
 
235
        {
 
236
                if (*i == PKG_DIR_SEP_S)
 
237
                        *bptr++ = '/';
 
238
                else if (*i == ' ') {
 
239
                        *bptr++ = '\\';
 
240
                        *bptr++ = *i;
 
241
                } else
 
242
                        *bptr++ = *i;
 
243
        }
 
244
 
 
245
        return buf;
 
246
}
 
247
 
 
248
static void
 
249
remove_additional_separators(char *buf)
 
250
{
 
251
        char *p = buf;
 
252
 
 
253
        while (*p) {
 
254
                if (*p == '/') {
 
255
                        char *q;
 
256
 
 
257
                        q = ++p;
 
258
                        while (*q && *q == '/')
 
259
                                q++;
 
260
 
 
261
                        if (p != q)
 
262
                                memmove (p, q, strlen (q) + 1);
 
263
                } else {
 
264
                        p++;
 
265
                }
 
266
        }
 
267
}
 
268
 
 
269
static void
 
270
canonicalize_path(char *buf)
 
271
{
 
272
        remove_additional_separators(buf);
 
273
}
 
274
 
 
275
static bool
 
276
is_path_prefix_equal(const char *path1, const char *path2, size_t path2_len)
 
277
{
 
278
#ifdef _WIN32
 
279
        return !_strnicmp(path1, path2, path2_len);
 
280
#else
 
281
        return !strncmp(path1, path2, path2_len);
 
282
#endif
 
283
}
 
284
 
 
285
static void
 
286
pkgconf_pkg_parser_value_set(void *opaque, const size_t lineno, const char *keyword, const char *value)
 
287
{
 
288
        char canonicalized_value[PKGCONF_ITEM_SIZE];
 
289
        pkgconf_pkg_t *pkg = opaque;
 
290
 
 
291
        (void) lineno;
 
292
 
 
293
        pkgconf_strlcpy(canonicalized_value, value, sizeof canonicalized_value);
 
294
        canonicalize_path(canonicalized_value);
 
295
 
 
296
        /* Some pc files will use absolute paths for all of their directories
 
297
         * which is broken when redefining the prefix. We try to outsmart the
 
298
         * file and rewrite any directory that starts with the same prefix.
 
299
         */
 
300
        if (strcmp(keyword, pkg->owner->prefix_varname) || !(pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX))
 
301
        {
 
302
                pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true);
 
303
        }
 
304
        else if (pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX && pkg->orig_prefix
 
305
            && is_path_prefix_equal(canonicalized_value, pkg->orig_prefix->value, strlen(pkg->orig_prefix->value)))
 
306
        {
 
307
                char newvalue[PKGCONF_ITEM_SIZE];
 
308
 
 
309
                pkgconf_strlcpy(newvalue, pkg->prefix->value, sizeof newvalue);
 
310
                pkgconf_strlcat(newvalue, canonicalized_value + strlen(pkg->orig_prefix->value), sizeof newvalue);
 
311
                pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, newvalue, false);
 
312
        }
 
313
        else
 
314
        {
 
315
                char pathbuf[PKGCONF_ITEM_SIZE];
 
316
                const char *relvalue = determine_prefix(pkg, pathbuf, sizeof pathbuf);
 
317
 
 
318
                if (relvalue != NULL)
 
319
                {
 
320
                        char *prefix_value = convert_path_to_value(relvalue);
 
321
                        pkg->orig_prefix = pkgconf_tuple_add(pkg->owner, &pkg->vars, "orig_prefix", canonicalized_value, true);
 
322
                        pkgconf_tuple_add_global(pkg->owner, keyword, prefix_value);
 
323
                        free(prefix_value);
 
324
                }
 
325
                else
 
326
                        pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true);
 
327
        }
 
328
}
 
329
 
 
330
typedef struct {
 
331
        const char *field;
 
332
        const ptrdiff_t offset;
 
333
} pkgconf_pkg_validity_check_t;
 
334
 
 
335
static const pkgconf_pkg_validity_check_t pkgconf_pkg_validations[] = {
 
336
        {"Name", offsetof(pkgconf_pkg_t, realname)},
 
337
        {"Description", offsetof(pkgconf_pkg_t, description)},
 
338
        {"Version", offsetof(pkgconf_pkg_t, version)},
 
339
};
 
340
 
 
341
static const pkgconf_parser_operand_func_t pkg_parser_funcs[256] = {
 
342
        [':'] = pkgconf_pkg_parser_keyword_set,
 
343
        ['='] = pkgconf_pkg_parser_value_set
 
344
};
 
345
 
 
346
static void pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...) PRINTFLIKE(2, 3);
 
347
 
 
348
static void
 
349
pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...)
 
350
{
 
351
        char buf[PKGCONF_ITEM_SIZE];
 
352
        va_list va;
 
353
 
 
354
        va_start(va, fmt);
 
355
        vsnprintf(buf, sizeof buf, fmt, va);
 
356
        va_end(va);
 
357
 
 
358
        pkgconf_warn(pkg->owner, "%s", buf);
 
359
}
 
360
 
 
361
static bool
 
362
pkgconf_pkg_validate(const pkgconf_client_t *client, const pkgconf_pkg_t *pkg)
 
363
{
 
364
        size_t i;
 
365
        bool valid = true;
 
366
 
 
367
        for (i = 0; i < PKGCONF_ARRAY_SIZE(pkgconf_pkg_validations); i++)
 
368
        {
 
369
                char **p = (char **)((char *) pkg + pkgconf_pkg_validations[i].offset);
 
370
 
 
371
                if (*p != NULL)
 
372
                        continue;
 
373
 
 
374
                pkgconf_warn(client, "%s: warning: file does not declare a `%s' field\n", pkg->filename, pkgconf_pkg_validations[i].field);
 
375
                valid = false;
 
376
        }
 
377
 
 
378
        return valid;
 
379
}
 
380
 
 
381
/*
 
382
 * !doc
 
383
 *
 
384
 * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_new_from_file(const pkgconf_client_t *client, const char *filename, FILE *f)
 
385
 *
 
386
 *    Parse a .pc file into a pkgconf_pkg_t object structure.
 
387
 *
 
388
 *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
 
389
 *    :param char* filename: The filename of the package file (including full path).
 
390
 *    :param FILE* f: The file object to read from.
 
391
 *    :returns: A ``pkgconf_pkg_t`` object which contains the package data.
 
392
 *    :rtype: pkgconf_pkg_t *
 
393
 */
 
394
pkgconf_pkg_t *
 
395
pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE *f)
 
396
{
 
397
        pkgconf_pkg_t *pkg;
 
398
        char *idptr;
 
399
 
 
400
        pkg = calloc(sizeof(pkgconf_pkg_t), 1);
 
401
        pkg->owner = client;
 
402
        pkg->filename = strdup(filename);
 
403
        pkg->pc_filedir = pkg_get_parent_dir(pkg);
 
404
 
 
405
        char *pc_filedir_value = convert_path_to_value(pkg->pc_filedir);
 
406
        pkgconf_tuple_add(client, &pkg->vars, "pcfiledir", pc_filedir_value, true);
 
407
        free(pc_filedir_value);
 
408
 
 
409
        /* If pc_filedir is outside of sysroot_dir, clear pc_filedir
 
410
         * See https://github.com/pkgconf/pkgconf/issues/213
 
411
         */
 
412
        if (client->sysroot_dir && strncmp(pkg->pc_filedir, client->sysroot_dir, strlen(client->sysroot_dir)))
 
413
        {
 
414
                free(client->sysroot_dir);
 
415
                client->sysroot_dir = NULL;
 
416
                pkgconf_client_set_sysroot_dir(client, NULL);
 
417
        }
 
418
 
 
419
        /* make module id */
 
420
        if ((idptr = strrchr(pkg->filename, PKG_DIR_SEP_S)) != NULL)
 
421
                idptr++;
 
422
        else
 
423
                idptr = pkg->filename;
 
424
 
 
425
#ifdef _WIN32
 
426
        /* On Windows, both \ and / are allowed in paths, so we have to chop both.
 
427
         * strrchr() took us to the last \ in that case, so we just have to see if
 
428
         * it is followed by a /.  If so, lop it off.
 
429
         */
 
430
        char *mungeptr;
 
431
        if ((mungeptr = strrchr(idptr, '/')) != NULL)
 
432
                idptr = ++mungeptr;
 
433
#endif
 
434
 
 
435
        pkg->id = strdup(idptr);
 
436
        idptr = strrchr(pkg->id, '.');
 
437
        if (idptr)
 
438
                *idptr = '\0';
 
439
 
 
440
        pkgconf_parser_parse(f, pkg, pkg_parser_funcs, (pkgconf_parser_warn_func_t) pkg_warn_func, pkg->filename);
 
441
 
 
442
        if (!pkgconf_pkg_validate(client, pkg))
 
443
        {
 
444
                pkgconf_warn(client, "%s: warning: skipping invalid file\n", pkg->filename);
 
445
                pkgconf_pkg_free(client, pkg);
 
446
                return NULL;
 
447
        }
 
448
 
 
449
        pkgconf_dependency_add(client, &pkg->provides, pkg->id, pkg->version, PKGCONF_CMP_EQUAL, 0);
 
450
 
 
451
        return pkgconf_pkg_ref(client, pkg);
 
452
}
 
453
 
 
454
/*
 
455
 * !doc
 
456
 *
 
457
 * .. c:function:: void pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
 
458
 *
 
459
 *    Releases all releases for a given ``pkgconf_pkg_t`` object.
 
460
 *
 
461
 *    :param pkgconf_client_t* client: The client which owns the ``pkgconf_pkg_t`` object, `pkg`.
 
462
 *    :param pkgconf_pkg_t* pkg: The package to free.
 
463
 *    :return: nothing
 
464
 */
 
465
void
 
466
pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
 
467
{
 
468
        if (pkg == NULL)
 
469
                return;
 
470
 
 
471
        if (pkg->flags & PKGCONF_PKG_PROPF_STATIC && !(pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL))
 
472
                return;
 
473
 
 
474
        pkgconf_cache_remove(client, pkg);
 
475
 
 
476
        pkgconf_dependency_free(&pkg->required);
 
477
        pkgconf_dependency_free(&pkg->requires_private);
 
478
        pkgconf_dependency_free(&pkg->conflicts);
 
479
        pkgconf_dependency_free(&pkg->provides);
 
480
 
 
481
        pkgconf_fragment_free(&pkg->cflags);
 
482
        pkgconf_fragment_free(&pkg->cflags_private);
 
483
        pkgconf_fragment_free(&pkg->libs);
 
484
        pkgconf_fragment_free(&pkg->libs_private);
 
485
 
 
486
        pkgconf_tuple_free(&pkg->vars);
 
487
 
 
488
        if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
 
489
                return;
 
490
 
 
491
        if (pkg->id != NULL)
 
492
                free(pkg->id);
 
493
 
 
494
        if (pkg->filename != NULL)
 
495
                free(pkg->filename);
 
496
 
 
497
        if (pkg->realname != NULL)
 
498
                free(pkg->realname);
 
499
 
 
500
        if (pkg->version != NULL)
 
501
                free(pkg->version);
 
502
 
 
503
        if (pkg->description != NULL)
 
504
                free(pkg->description);
 
505
 
 
506
        if (pkg->url != NULL)
 
507
                free(pkg->url);
 
508
 
 
509
        if (pkg->pc_filedir != NULL)
 
510
                free(pkg->pc_filedir);
 
511
 
 
512
        free(pkg);
 
513
}
 
514
 
 
515
/*
 
516
 * !doc
 
517
 *
 
518
 * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_ref(const pkgconf_client_t *client, pkgconf_pkg_t *pkg)
 
519
 *
 
520
 *    Adds an additional reference to the package object.
 
521
 *
 
522
 *    :param pkgconf_client_t* client: The pkgconf client object which owns the package being referenced.
 
523
 *    :param pkgconf_pkg_t* pkg: The package object being referenced.
 
524
 *    :return: The package itself with an incremented reference count.
 
525
 *    :rtype: pkgconf_pkg_t *
 
526
 */
 
527
pkgconf_pkg_t *
 
528
pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
 
529
{
 
530
        if (pkg->owner != NULL && pkg->owner != client)
 
531
                PKGCONF_TRACE(client, "WTF: client %p refers to package %p owned by other client %p", client, pkg, pkg->owner);
 
532
 
 
533
        pkg->refcount++;
 
534
        PKGCONF_TRACE(client, "refcount@%p: %d", pkg, pkg->refcount);
 
535
 
 
536
        return pkg;
 
537
}
 
538
 
 
539
/*
 
540
 * !doc
 
541
 *
 
542
 * .. c:function:: void pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
 
543
 *
 
544
 *    Releases a reference on the package object.  If the reference count is 0, then also free the package.
 
545
 *
 
546
 *    :param pkgconf_client_t* client: The pkgconf client object which owns the package being dereferenced.
 
547
 *    :param pkgconf_pkg_t* pkg: The package object being dereferenced.
 
548
 *    :return: nothing
 
549
 */
 
550
void
 
551
pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
 
552
{
 
553
        if (pkg->owner != NULL && pkg->owner != client)
 
554
                PKGCONF_TRACE(client, "WTF: client %p unrefs package %p owned by other client %p", client, pkg, pkg->owner);
 
555
 
 
556
        pkg->refcount--;
 
557
        PKGCONF_TRACE(pkg->owner, "refcount@%p: %d", pkg, pkg->refcount);
 
558
 
 
559
        if (pkg->refcount <= 0)
 
560
                pkgconf_pkg_free(pkg->owner, pkg);
 
561
}
 
562
 
 
563
static inline pkgconf_pkg_t *
 
564
pkgconf_pkg_try_specific_path(pkgconf_client_t *client, const char *path, const char *name)
 
565
{
 
566
        pkgconf_pkg_t *pkg = NULL;
 
567
        FILE *f;
 
568
        char locbuf[PKGCONF_ITEM_SIZE];
 
569
        char uninst_locbuf[PKGCONF_ITEM_SIZE];
 
570
 
 
571
        PKGCONF_TRACE(client, "trying path: %s for %s", path, name);
 
572
 
 
573
        snprintf(locbuf, sizeof locbuf, "%s%c%s" PKG_CONFIG_EXT, path, PKG_DIR_SEP_S, name);
 
574
        snprintf(uninst_locbuf, sizeof uninst_locbuf, "%s%c%s-uninstalled" PKG_CONFIG_EXT, path, PKG_DIR_SEP_S, name);
 
575
 
 
576
        if (!(client->flags & PKGCONF_PKG_PKGF_NO_UNINSTALLED) && (f = fopen(uninst_locbuf, "r")) != NULL)
 
577
        {
 
578
                PKGCONF_TRACE(client, "found (uninstalled): %s", uninst_locbuf);
 
579
                pkg = pkgconf_pkg_new_from_file(client, uninst_locbuf, f);
 
580
                if (pkg != NULL)
 
581
                        pkg->flags |= PKGCONF_PKG_PROPF_UNINSTALLED;
 
582
        }
 
583
        else if ((f = fopen(locbuf, "r")) != NULL)
 
584
        {
 
585
                PKGCONF_TRACE(client, "found: %s", locbuf);
 
586
                pkg = pkgconf_pkg_new_from_file(client, locbuf, f);
 
587
        }
 
588
 
 
589
        return pkg;
 
590
}
 
591
 
 
592
static pkgconf_pkg_t *
 
593
pkgconf_pkg_scan_dir(pkgconf_client_t *client, const char *path, void *data, pkgconf_pkg_iteration_func_t func)
 
594
{
 
595
        DIR *dir;
 
596
        struct dirent *dirent;
 
597
        pkgconf_pkg_t *outpkg = NULL;
 
598
 
 
599
        dir = opendir(path);
 
600
        if (dir == NULL)
 
601
                return NULL;
 
602
 
 
603
        PKGCONF_TRACE(client, "scanning dir [%s]", path);
 
604
 
 
605
        for (dirent = readdir(dir); dirent != NULL; dirent = readdir(dir))
 
606
        {
 
607
                char filebuf[PKGCONF_ITEM_SIZE];
 
608
                pkgconf_pkg_t *pkg;
 
609
                FILE *f;
 
610
 
 
611
                pkgconf_strlcpy(filebuf, path, sizeof filebuf);
 
612
                pkgconf_strlcat(filebuf, "/", sizeof filebuf);
 
613
                pkgconf_strlcat(filebuf, dirent->d_name, sizeof filebuf);
 
614
 
 
615
                if (!str_has_suffix(filebuf, PKG_CONFIG_EXT))
 
616
                        continue;
 
617
 
 
618
                PKGCONF_TRACE(client, "trying file [%s]", filebuf);
 
619
 
 
620
                f = fopen(filebuf, "r");
 
621
                if (f == NULL)
 
622
                        continue;
 
623
 
 
624
                pkg = pkgconf_pkg_new_from_file(client, filebuf, f);
 
625
                if (pkg != NULL)
 
626
                {
 
627
                        if (func(pkg, data))
 
628
                        {
 
629
                                outpkg = pkg;
 
630
                                goto out;
 
631
                        }
 
632
 
 
633
                        pkgconf_pkg_unref(client, pkg);
 
634
                }
 
635
        }
 
636
 
 
637
out:
 
638
        closedir(dir);
 
639
        return outpkg;
 
640
}
 
641
 
 
642
/*
 
643
 * !doc
 
644
 *
 
645
 * .. c:function:: pkgconf_pkg_t *pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func)
 
646
 *
 
647
 *    Iterates over all packages found in the `package directory list`, running ``func`` on them.  If ``func`` returns true,
 
648
 *    then stop iteration and return the last iterated package.
 
649
 *
 
650
 *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
 
651
 *    :param void* data: An opaque pointer to data to provide the iteration function with.
 
652
 *    :param pkgconf_pkg_iteration_func_t func: A function which is called for each package to determine if the package matches,
 
653
 *        always return ``false`` to iterate over all packages.
 
654
 *    :return: A package object reference if one is found by the scan function, else ``NULL``.
 
655
 *    :rtype: pkgconf_pkg_t *
 
656
 */
 
657
pkgconf_pkg_t *
 
658
pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func)
 
659
{
 
660
        pkgconf_node_t *n;
 
661
        pkgconf_pkg_t *pkg;
 
662
 
 
663
        PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n)
 
664
        {
 
665
                pkgconf_path_t *pnode = n->data;
 
666
 
 
667
                PKGCONF_TRACE(client, "scanning directory: %s", pnode->path);
 
668
 
 
669
                if ((pkg = pkgconf_pkg_scan_dir(client, pnode->path, data, func)) != NULL)
 
670
                        return pkg;
 
671
        }
 
672
 
 
673
        return NULL;
 
674
}
 
675
 
 
676
#ifdef _WIN32
 
677
static pkgconf_pkg_t *
 
678
pkgconf_pkg_find_in_registry_key(pkgconf_client_t *client, HKEY hkey, const char *name)
 
679
{
 
680
        pkgconf_pkg_t *pkg = NULL;
 
681
 
 
682
        HKEY key;
 
683
        int i = 0;
 
684
 
 
685
        char buf[16384]; /* per registry limits */
 
686
        DWORD bufsize = sizeof buf;
 
687
        if (RegOpenKeyEx(hkey, PKG_CONFIG_REG_KEY,
 
688
                                0, KEY_READ, &key) != ERROR_SUCCESS)
 
689
                return NULL;
 
690
 
 
691
        while (RegEnumValue(key, i++, buf, &bufsize, NULL, NULL, NULL, NULL)
 
692
                        == ERROR_SUCCESS)
 
693
        {
 
694
                char pathbuf[PKGCONF_ITEM_SIZE];
 
695
                DWORD type;
 
696
                DWORD pathbuflen = sizeof pathbuf;
 
697
 
 
698
                if (RegQueryValueEx(key, buf, NULL, &type, (LPBYTE) pathbuf, &pathbuflen)
 
699
                                == ERROR_SUCCESS && type == REG_SZ)
 
700
                {
 
701
                        pkg = pkgconf_pkg_try_specific_path(client, pathbuf, name);
 
702
                        if (pkg != NULL)
 
703
                                break;
 
704
                }
 
705
 
 
706
                bufsize = sizeof buf;
 
707
        }
 
708
 
 
709
        RegCloseKey(key);
 
710
        return pkg;
 
711
}
 
712
#endif
 
713
 
 
714
/*
 
715
 * !doc
 
716
 *
 
717
 * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
 
718
 *
 
719
 *    Search for a package.
 
720
 *
 
721
 *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
 
722
 *    :param char* name: The name of the package `atom` to use for searching.
 
723
 *    :return: A package object reference if the package was found, else ``NULL``.
 
724
 *    :rtype: pkgconf_pkg_t *
 
725
 */
 
726
pkgconf_pkg_t *
 
727
pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
 
728
{
 
729
        pkgconf_pkg_t *pkg = NULL;
 
730
        pkgconf_node_t *n;
 
731
        FILE *f;
 
732
 
 
733
        PKGCONF_TRACE(client, "looking for: %s", name);
 
734
 
 
735
        /* name might actually be a filename. */
 
736
        if (str_has_suffix(name, PKG_CONFIG_EXT))
 
737
        {
 
738
                if ((f = fopen(name, "r")) != NULL)
 
739
                {
 
740
                        pkgconf_pkg_t *pkg;
 
741
 
 
742
                        PKGCONF_TRACE(client, "%s is a file", name);
 
743
 
 
744
                        pkg = pkgconf_pkg_new_from_file(client, name, f);
 
745
                        if (pkg != NULL)
 
746
                        {
 
747
                                pkgconf_path_add(pkg->pc_filedir, &client->dir_list, true);
 
748
                                return pkg;
 
749
                        }
 
750
                }
 
751
        }
 
752
 
 
753
        /* check builtins */
 
754
        if ((pkg = pkgconf_builtin_pkg_get(name)) != NULL)
 
755
        {
 
756
                PKGCONF_TRACE(client, "%s is a builtin", name);
 
757
                return pkg;
 
758
        }
 
759
 
 
760
        /* check cache */
 
761
        if (!(client->flags & PKGCONF_PKG_PKGF_NO_CACHE))
 
762
        {
 
763
                if ((pkg = pkgconf_cache_lookup(client, name)) != NULL)
 
764
                {
 
765
                        PKGCONF_TRACE(client, "%s is cached", name);
 
766
                        return pkg;
 
767
                }
 
768
        }
 
769
 
 
770
        PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n)
 
771
        {
 
772
                pkgconf_path_t *pnode = n->data;
 
773
 
 
774
                pkg = pkgconf_pkg_try_specific_path(client, pnode->path, name);
 
775
                if (pkg != NULL)
 
776
                        goto out;
 
777
        }
 
778
 
 
779
#ifdef _WIN32
 
780
        /* support getting PKG_CONFIG_PATH from registry */
 
781
        pkg = pkgconf_pkg_find_in_registry_key(client, HKEY_CURRENT_USER, name);
 
782
        if (!pkg)
 
783
                pkg = pkgconf_pkg_find_in_registry_key(client, HKEY_LOCAL_MACHINE, name);
 
784
#endif
 
785
 
 
786
out:
 
787
        pkgconf_cache_add(client, pkg);
 
788
 
 
789
        return pkg;
 
790
}
 
791
 
 
792
/*
 
793
 * !doc
 
794
 *
 
795
 * .. c:function:: int pkgconf_compare_version(const char *a, const char *b)
 
796
 *
 
797
 *    Compare versions using RPM version comparison rules as described in the LSB.
 
798
 *
 
799
 *    :param char* a: The first version to compare in the pair.
 
800
 *    :param char* b: The second version to compare in the pair.
 
801
 *    :return: -1 if the first version is less than, 0 if both versions are equal, 1 if the second version is less than.
 
802
 *    :rtype: int
 
803
 */
 
804
int
 
805
pkgconf_compare_version(const char *a, const char *b)
 
806
{
 
807
        char oldch1, oldch2;
 
808
        char buf1[PKGCONF_ITEM_SIZE], buf2[PKGCONF_ITEM_SIZE];
 
809
        char *str1, *str2;
 
810
        char *one, *two;
 
811
        int ret;
 
812
        bool isnum;
 
813
 
 
814
        /* optimization: if version matches then it's the same version. */
 
815
        if (a == NULL)
 
816
                return -1;
 
817
 
 
818
        if (b == NULL)
 
819
                return 1;
 
820
 
 
821
        if (!strcasecmp(a, b))
 
822
                return 0;
 
823
 
 
824
        pkgconf_strlcpy(buf1, a, sizeof buf1);
 
825
        pkgconf_strlcpy(buf2, b, sizeof buf2);
 
826
 
 
827
        one = str1 = buf1;
 
828
        two = str2 = buf2;
 
829
 
 
830
        while (*one || *two)
 
831
        {
 
832
                while (*one && !isalnum((unsigned int)*one) && *one != '~')
 
833
                        one++;
 
834
                while (*two && !isalnum((unsigned int)*two) && *two != '~')
 
835
                        two++;
 
836
 
 
837
                if (*one == '~' || *two == '~')
 
838
                {
 
839
                        if (*one != '~')
 
840
                                return 1;
 
841
                        if (*two != '~')
 
842
                                return -1;
 
843
 
 
844
                        one++;
 
845
                        two++;
 
846
                        continue;
 
847
                }
 
848
 
 
849
                if (!(*one && *two))
 
850
                        break;
 
851
 
 
852
                str1 = one;
 
853
                str2 = two;
 
854
 
 
855
                if (isdigit((unsigned int)*str1))
 
856
                {
 
857
                        while (*str1 && isdigit((unsigned int)*str1))
 
858
                                str1++;
 
859
 
 
860
                        while (*str2 && isdigit((unsigned int)*str2))
 
861
                                str2++;
 
862
 
 
863
                        isnum = true;
 
864
                }
 
865
                else
 
866
                {
 
867
                        while (*str1 && isalpha((unsigned int)*str1))
 
868
                                str1++;
 
869
 
 
870
                        while (*str2 && isalpha((unsigned int)*str2))
 
871
                                str2++;
 
872
 
 
873
                        isnum = false;
 
874
                }
 
875
 
 
876
                oldch1 = *str1;
 
877
                oldch2 = *str2;
 
878
 
 
879
                *str1 = '\0';
 
880
                *str2 = '\0';
 
881
 
 
882
                if (one == str1)
 
883
                        return -1;
 
884
 
 
885
                if (two == str2)
 
886
                        return (isnum ? 1 : -1);
 
887
 
 
888
                if (isnum)
 
889
                {
 
890
                        int onelen, twolen;
 
891
 
 
892
                        while (*one == '0')
 
893
                                one++;
 
894
 
 
895
                        while (*two == '0')
 
896
                                two++;
 
897
 
 
898
                        onelen = strlen(one);
 
899
                        twolen = strlen(two);
 
900
 
 
901
                        if (onelen > twolen)
 
902
                                return 1;
 
903
                        else if (twolen > onelen)
 
904
                                return -1;
 
905
                }
 
906
 
 
907
                ret = strcmp(one, two);
 
908
                if (ret != 0)
 
909
                        return ret < 0 ? -1 : 1;
 
910
 
 
911
                *str1 = oldch1;
 
912
                *str2 = oldch2;
 
913
 
 
914
                one = str1;
 
915
                two = str2;
 
916
        }
 
917
 
 
918
        if ((!*one) && (!*two))
 
919
                return 0;
 
920
 
 
921
        if (!*one)
 
922
                return -1;
 
923
 
 
924
        return 1;
 
925
}
 
926
 
 
927
static pkgconf_pkg_t pkg_config_virtual = {
 
928
        .id = "pkg-config",
 
929
        .realname = "pkg-config",
 
930
        .description = "virtual package defining pkg-config API version supported",
 
931
        .url = PACKAGE_BUGREPORT,
 
932
        .version = PACKAGE_VERSION,
 
933
        .flags = PKGCONF_PKG_PROPF_STATIC,
 
934
        .vars = {
 
935
                .head = &(pkgconf_node_t){
 
936
                        .next = &(pkgconf_node_t){
 
937
                                .next = &(pkgconf_node_t){
 
938
                                        .data = &(pkgconf_tuple_t){
 
939
                                                .key = "pc_system_libdirs",
 
940
                                                .value = SYSTEM_LIBDIR,
 
941
                                        }
 
942
                                },
 
943
                                .data = &(pkgconf_tuple_t){
 
944
                                        .key = "pc_system_includedirs",
 
945
                                        .value = SYSTEM_INCLUDEDIR,
 
946
                                }
 
947
                        },
 
948
                        .data = &(pkgconf_tuple_t){
 
949
                                .key = "pc_path",
 
950
                                .value = PKG_DEFAULT_PATH,
 
951
                        },
 
952
                },
 
953
                .tail = NULL,
 
954
        }
 
955
};
 
956
 
 
957
static pkgconf_pkg_t pkgconf_virtual = {
 
958
        .id = "pkgconf",
 
959
        .realname = "pkgconf",
 
960
        .description = "virtual package defining pkgconf API version supported",
 
961
        .url = PACKAGE_BUGREPORT,
 
962
        .version = PACKAGE_VERSION,
 
963
        .flags = PKGCONF_PKG_PROPF_STATIC,
 
964
        .vars = {
 
965
                .head = &(pkgconf_node_t){
 
966
                        .next = &(pkgconf_node_t){
 
967
                                .next = &(pkgconf_node_t){
 
968
                                        .data = &(pkgconf_tuple_t){
 
969
                                                .key = "pc_system_libdirs",
 
970
                                                .value = SYSTEM_LIBDIR,
 
971
                                        }
 
972
                                },
 
973
                                .data = &(pkgconf_tuple_t){
 
974
                                        .key = "pc_system_includedirs",
 
975
                                        .value = SYSTEM_INCLUDEDIR,
 
976
                                }
 
977
                        },
 
978
                        .data = &(pkgconf_tuple_t){
 
979
                                .key = "pc_path",
 
980
                                .value = PKG_DEFAULT_PATH,
 
981
                        },
 
982
                },
 
983
                .tail = NULL,
 
984
        },
 
985
};
 
986
 
 
987
typedef struct {
 
988
        const char *name;
 
989
        pkgconf_pkg_t *pkg;
 
990
} pkgconf_builtin_pkg_pair_t;
 
991
 
 
992
/* keep these in alphabetical order */
 
993
static const pkgconf_builtin_pkg_pair_t pkgconf_builtin_pkg_pair_set[] = {
 
994
        {"pkg-config", &pkg_config_virtual},
 
995
        {"pkgconf", &pkgconf_virtual},
 
996
};
 
997
 
 
998
static int pkgconf_builtin_pkg_pair_cmp(const void *key, const void *ptr)
 
999
{
 
1000
        const pkgconf_builtin_pkg_pair_t *pair = ptr;
 
1001
        return strcasecmp(key, pair->name);
 
1002
}
 
1003
 
 
1004
/*
 
1005
 * !doc
 
1006
 *
 
1007
 * .. c:function:: pkgconf_pkg_t *pkgconf_builtin_pkg_get(const char *name)
 
1008
 *
 
1009
 *    Looks up a built-in package.  The package should not be freed or dereferenced.
 
1010
 *
 
1011
 *    :param char* name: An atom corresponding to a built-in package to search for.
 
1012
 *    :return: the built-in package if present, else ``NULL``.
 
1013
 *    :rtype: pkgconf_pkg_t *
 
1014
 */
 
1015
pkgconf_pkg_t *
 
1016
pkgconf_builtin_pkg_get(const char *name)
 
1017
{
 
1018
        const pkgconf_builtin_pkg_pair_t *pair = bsearch(name, pkgconf_builtin_pkg_pair_set,
 
1019
                PKGCONF_ARRAY_SIZE(pkgconf_builtin_pkg_pair_set), sizeof(pkgconf_builtin_pkg_pair_t),
 
1020
                pkgconf_builtin_pkg_pair_cmp);
 
1021
 
 
1022
        return (pair != NULL) ? pair->pkg : NULL;
 
1023
}
 
1024
 
 
1025
typedef bool (*pkgconf_vercmp_res_func_t)(const char *a, const char *b);
 
1026
 
 
1027
typedef struct {
 
1028
        const char *name;
 
1029
        pkgconf_pkg_comparator_t compare;
 
1030
} pkgconf_pkg_comparator_pair_t;
 
1031
 
 
1032
static const pkgconf_pkg_comparator_pair_t pkgconf_pkg_comparator_names[] = {
 
1033
        {"!=",          PKGCONF_CMP_NOT_EQUAL},
 
1034
        {"(any)",       PKGCONF_CMP_ANY},
 
1035
        {"<",           PKGCONF_CMP_LESS_THAN},
 
1036
        {"<=",          PKGCONF_CMP_LESS_THAN_EQUAL},
 
1037
        {"=",           PKGCONF_CMP_EQUAL},
 
1038
        {">",           PKGCONF_CMP_GREATER_THAN},
 
1039
        {">=",          PKGCONF_CMP_GREATER_THAN_EQUAL},
 
1040
};
 
1041
 
 
1042
static int pkgconf_pkg_comparator_pair_namecmp(const void *key, const void *ptr)
 
1043
{
 
1044
        const pkgconf_pkg_comparator_pair_t *pair = ptr;
 
1045
        return strcmp(key, pair->name);
 
1046
}
 
1047
 
 
1048
static bool pkgconf_pkg_comparator_lt(const char *a, const char *b)
 
1049
{
 
1050
        return (pkgconf_compare_version(a, b) < 0);
 
1051
}
 
1052
 
 
1053
static bool pkgconf_pkg_comparator_gt(const char *a, const char *b)
 
1054
{
 
1055
        return (pkgconf_compare_version(a, b) > 0);
 
1056
}
 
1057
 
 
1058
static bool pkgconf_pkg_comparator_lte(const char *a, const char *b)
 
1059
{
 
1060
        return (pkgconf_compare_version(a, b) <= 0);
 
1061
}
 
1062
 
 
1063
static bool pkgconf_pkg_comparator_gte(const char *a, const char *b)
 
1064
{
 
1065
        return (pkgconf_compare_version(a, b) >= 0);
 
1066
}
 
1067
 
 
1068
static bool pkgconf_pkg_comparator_eq(const char *a, const char *b)
 
1069
{
 
1070
        return (pkgconf_compare_version(a, b) == 0);
 
1071
}
 
1072
 
 
1073
static bool pkgconf_pkg_comparator_ne(const char *a, const char *b)
 
1074
{
 
1075
        return (pkgconf_compare_version(a, b) != 0);
 
1076
}
 
1077
 
 
1078
static bool pkgconf_pkg_comparator_any(const char *a, const char *b)
 
1079
{
 
1080
        (void) a;
 
1081
        (void) b;
 
1082
 
 
1083
        return true;
 
1084
}
 
1085
 
 
1086
static bool pkgconf_pkg_comparator_none(const char *a, const char *b)
 
1087
{
 
1088
        (void) a;
 
1089
        (void) b;
 
1090
 
 
1091
        return false;
 
1092
}
 
1093
 
 
1094
static const pkgconf_vercmp_res_func_t pkgconf_pkg_comparator_impls[] = {
 
1095
        [PKGCONF_CMP_ANY]                       = pkgconf_pkg_comparator_any,
 
1096
        [PKGCONF_CMP_LESS_THAN]                 = pkgconf_pkg_comparator_lt,
 
1097
        [PKGCONF_CMP_GREATER_THAN]              = pkgconf_pkg_comparator_gt,
 
1098
        [PKGCONF_CMP_LESS_THAN_EQUAL]           = pkgconf_pkg_comparator_lte,
 
1099
        [PKGCONF_CMP_GREATER_THAN_EQUAL]        = pkgconf_pkg_comparator_gte,
 
1100
        [PKGCONF_CMP_EQUAL]                     = pkgconf_pkg_comparator_eq,
 
1101
        [PKGCONF_CMP_NOT_EQUAL]                 = pkgconf_pkg_comparator_ne,
 
1102
};
 
1103
 
 
1104
/*
 
1105
 * !doc
 
1106
 *
 
1107
 * .. c:function:: const char *pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep)
 
1108
 *
 
1109
 *    Returns the comparator used in a depgraph dependency node as a string.
 
1110
 *
 
1111
 *    :param pkgconf_dependency_t* pkgdep: The depgraph dependency node to return the comparator for.
 
1112
 *    :return: A string matching the comparator or ``"???"``.
 
1113
 *    :rtype: char *
 
1114
 */
 
1115
const char *
 
1116
pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep)
 
1117
{
 
1118
        if (pkgdep->compare >= PKGCONF_ARRAY_SIZE(pkgconf_pkg_comparator_names))
 
1119
                return "???";
 
1120
 
 
1121
        return pkgconf_pkg_comparator_names[pkgdep->compare].name;
 
1122
}
 
1123
 
 
1124
/*
 
1125
 * !doc
 
1126
 *
 
1127
 * .. c:function:: pkgconf_pkg_comparator_t pkgconf_pkg_comparator_lookup_by_name(const char *name)
 
1128
 *
 
1129
 *    Look up the appropriate comparator bytecode in the comparator set (defined
 
1130
 *    in ``pkg.c``, see ``pkgconf_pkg_comparator_names`` and ``pkgconf_pkg_comparator_impls``).
 
1131
 *
 
1132
 *    :param char* name: The comparator to look up by `name`.
 
1133
 *    :return: The comparator bytecode if found, else ``PKGCONF_CMP_ANY``.
 
1134
 *    :rtype: pkgconf_pkg_comparator_t
 
1135
 */
 
1136
pkgconf_pkg_comparator_t
 
1137
pkgconf_pkg_comparator_lookup_by_name(const char *name)
 
1138
{
 
1139
        const pkgconf_pkg_comparator_pair_t *p = bsearch(name, pkgconf_pkg_comparator_names,
 
1140
                PKGCONF_ARRAY_SIZE(pkgconf_pkg_comparator_names), sizeof(pkgconf_pkg_comparator_pair_t),
 
1141
                pkgconf_pkg_comparator_pair_namecmp);
 
1142
 
 
1143
        return (p != NULL) ? p->compare : PKGCONF_CMP_ANY;
 
1144
}
 
1145
 
 
1146
typedef struct {
 
1147
        pkgconf_dependency_t *pkgdep;
 
1148
} pkgconf_pkg_scan_providers_ctx_t;
 
1149
 
 
1150
typedef struct {
 
1151
        const pkgconf_vercmp_res_func_t rulecmp[PKGCONF_CMP_COUNT];
 
1152
        const pkgconf_vercmp_res_func_t depcmp[PKGCONF_CMP_COUNT];
 
1153
} pkgconf_pkg_provides_vermatch_rule_t;
 
1154
 
 
1155
static const pkgconf_pkg_provides_vermatch_rule_t pkgconf_pkg_provides_vermatch_rules[] = {
 
1156
        [PKGCONF_CMP_ANY] = {
 
1157
                .rulecmp = {
 
1158
                        [PKGCONF_CMP_ANY]                       = pkgconf_pkg_comparator_none,
 
1159
                },
 
1160
                .depcmp = {
 
1161
                        [PKGCONF_CMP_ANY]                       = pkgconf_pkg_comparator_none,
 
1162
                },
 
1163
        },
 
1164
        [PKGCONF_CMP_LESS_THAN] = {
 
1165
                .rulecmp = {
 
1166
                        [PKGCONF_CMP_ANY]                       = pkgconf_pkg_comparator_none,
 
1167
                        [PKGCONF_CMP_LESS_THAN]                 = pkgconf_pkg_comparator_lt,
 
1168
                        [PKGCONF_CMP_GREATER_THAN]              = pkgconf_pkg_comparator_gt,
 
1169
                        [PKGCONF_CMP_LESS_THAN_EQUAL]           = pkgconf_pkg_comparator_lte,
 
1170
                        [PKGCONF_CMP_GREATER_THAN_EQUAL]        = pkgconf_pkg_comparator_gte,
 
1171
                },
 
1172
                .depcmp = {
 
1173
                        [PKGCONF_CMP_GREATER_THAN]              = pkgconf_pkg_comparator_lt,
 
1174
                        [PKGCONF_CMP_GREATER_THAN_EQUAL]        = pkgconf_pkg_comparator_lt,
 
1175
                        [PKGCONF_CMP_EQUAL]                     = pkgconf_pkg_comparator_lt,
 
1176
                        [PKGCONF_CMP_NOT_EQUAL]                 = pkgconf_pkg_comparator_gte,
 
1177
                },
 
1178
        },
 
1179
        [PKGCONF_CMP_GREATER_THAN] = {
 
1180
                .rulecmp = {
 
1181
                        [PKGCONF_CMP_ANY]                       = pkgconf_pkg_comparator_none,
 
1182
                        [PKGCONF_CMP_LESS_THAN]                 = pkgconf_pkg_comparator_lt,
 
1183
                        [PKGCONF_CMP_GREATER_THAN]              = pkgconf_pkg_comparator_gt,
 
1184
                        [PKGCONF_CMP_LESS_THAN_EQUAL]           = pkgconf_pkg_comparator_lte,
 
1185
                        [PKGCONF_CMP_GREATER_THAN_EQUAL]        = pkgconf_pkg_comparator_gte,
 
1186
                },
 
1187
                .depcmp = {
 
1188
                        [PKGCONF_CMP_LESS_THAN]                 = pkgconf_pkg_comparator_gt,
 
1189
                        [PKGCONF_CMP_LESS_THAN_EQUAL]           = pkgconf_pkg_comparator_gt,
 
1190
                        [PKGCONF_CMP_EQUAL]                     = pkgconf_pkg_comparator_gt,
 
1191
                        [PKGCONF_CMP_NOT_EQUAL]                 = pkgconf_pkg_comparator_lte,
 
1192
                },
 
1193
        },
 
1194
        [PKGCONF_CMP_LESS_THAN_EQUAL] = {
 
1195
                .rulecmp = {
 
1196
                        [PKGCONF_CMP_ANY]                       = pkgconf_pkg_comparator_none,
 
1197
                        [PKGCONF_CMP_LESS_THAN]                 = pkgconf_pkg_comparator_lt,
 
1198
                        [PKGCONF_CMP_GREATER_THAN]              = pkgconf_pkg_comparator_gt,
 
1199
                        [PKGCONF_CMP_LESS_THAN_EQUAL]           = pkgconf_pkg_comparator_lte,
 
1200
                        [PKGCONF_CMP_GREATER_THAN_EQUAL]        = pkgconf_pkg_comparator_gte,
 
1201
                },
 
1202
                .depcmp = {
 
1203
                        [PKGCONF_CMP_GREATER_THAN]              = pkgconf_pkg_comparator_lte,
 
1204
                        [PKGCONF_CMP_GREATER_THAN_EQUAL]        = pkgconf_pkg_comparator_lte,
 
1205
                        [PKGCONF_CMP_EQUAL]                     = pkgconf_pkg_comparator_lte,
 
1206
                        [PKGCONF_CMP_NOT_EQUAL]                 = pkgconf_pkg_comparator_gt,
 
1207
                },
 
1208
        },
 
1209
        [PKGCONF_CMP_GREATER_THAN_EQUAL] = {
 
1210
                .rulecmp = {
 
1211
                        [PKGCONF_CMP_ANY]                       = pkgconf_pkg_comparator_none,
 
1212
                        [PKGCONF_CMP_LESS_THAN]                 = pkgconf_pkg_comparator_lt,
 
1213
                        [PKGCONF_CMP_GREATER_THAN]              = pkgconf_pkg_comparator_gt,
 
1214
                        [PKGCONF_CMP_LESS_THAN_EQUAL]           = pkgconf_pkg_comparator_lte,
 
1215
                        [PKGCONF_CMP_GREATER_THAN_EQUAL]        = pkgconf_pkg_comparator_gte,
 
1216
                },
 
1217
                .depcmp = {
 
1218
                        [PKGCONF_CMP_LESS_THAN]                 = pkgconf_pkg_comparator_gte,
 
1219
                        [PKGCONF_CMP_LESS_THAN_EQUAL]           = pkgconf_pkg_comparator_gte,
 
1220
                        [PKGCONF_CMP_EQUAL]                     = pkgconf_pkg_comparator_gte,
 
1221
                        [PKGCONF_CMP_NOT_EQUAL]                 = pkgconf_pkg_comparator_lt,
 
1222
                },
 
1223
        },
 
1224
        [PKGCONF_CMP_EQUAL] = {
 
1225
                .rulecmp = {
 
1226
                        [PKGCONF_CMP_ANY]                       = pkgconf_pkg_comparator_none,
 
1227
                        [PKGCONF_CMP_LESS_THAN]                 = pkgconf_pkg_comparator_lt,
 
1228
                        [PKGCONF_CMP_GREATER_THAN]              = pkgconf_pkg_comparator_gt,
 
1229
                        [PKGCONF_CMP_LESS_THAN_EQUAL]           = pkgconf_pkg_comparator_lte,
 
1230
                        [PKGCONF_CMP_GREATER_THAN_EQUAL]        = pkgconf_pkg_comparator_gte,
 
1231
                        [PKGCONF_CMP_EQUAL]                     = pkgconf_pkg_comparator_eq,
 
1232
                        [PKGCONF_CMP_NOT_EQUAL]                 = pkgconf_pkg_comparator_ne
 
1233
                },
 
1234
                .depcmp = {
 
1235
                        [PKGCONF_CMP_ANY]                       = pkgconf_pkg_comparator_none,
 
1236
                },
 
1237
        },
 
1238
        [PKGCONF_CMP_NOT_EQUAL] = {
 
1239
                .rulecmp = {
 
1240
                        [PKGCONF_CMP_ANY]                       = pkgconf_pkg_comparator_none,
 
1241
                        [PKGCONF_CMP_LESS_THAN]                 = pkgconf_pkg_comparator_gte,
 
1242
                        [PKGCONF_CMP_GREATER_THAN]              = pkgconf_pkg_comparator_lte,
 
1243
                        [PKGCONF_CMP_LESS_THAN_EQUAL]           = pkgconf_pkg_comparator_gt,
 
1244
                        [PKGCONF_CMP_GREATER_THAN_EQUAL]        = pkgconf_pkg_comparator_lt,
 
1245
                        [PKGCONF_CMP_EQUAL]                     = pkgconf_pkg_comparator_ne,
 
1246
                        [PKGCONF_CMP_NOT_EQUAL]                 = pkgconf_pkg_comparator_eq
 
1247
                },
 
1248
                .depcmp = {
 
1249
                        [PKGCONF_CMP_ANY]                       = pkgconf_pkg_comparator_none,
 
1250
                },
 
1251
        },
 
1252
};
 
1253
 
 
1254
/*
 
1255
 * pkgconf_pkg_scan_provides_vercmp(pkgdep, provider)
 
1256
 *
 
1257
 * compare a provides node against the requested dependency node.
 
1258
 *
 
1259
 * XXX: maybe handle PKGCONF_CMP_ANY in a versioned comparison
 
1260
 */
 
1261
static bool
 
1262
pkgconf_pkg_scan_provides_vercmp(const pkgconf_dependency_t *pkgdep, const pkgconf_dependency_t *provider)
 
1263
{
 
1264
        const pkgconf_pkg_provides_vermatch_rule_t *rule = &pkgconf_pkg_provides_vermatch_rules[pkgdep->compare];
 
1265
 
 
1266
        if (rule->depcmp[provider->compare] != NULL &&
 
1267
            !rule->depcmp[provider->compare](provider->version, pkgdep->version))
 
1268
                return false;
 
1269
 
 
1270
        if (rule->rulecmp[provider->compare] != NULL &&
 
1271
            !rule->rulecmp[provider->compare](pkgdep->version, provider->version))
 
1272
                return false;
 
1273
 
 
1274
        return true;
 
1275
}
 
1276
 
 
1277
/*
 
1278
 * pkgconf_pkg_scan_provides_entry(pkg, ctx)
 
1279
 *
 
1280
 * attempt to match a single package's Provides rules against the requested dependency node.
 
1281
 */
 
1282
static bool
 
1283
pkgconf_pkg_scan_provides_entry(const pkgconf_pkg_t *pkg, const pkgconf_pkg_scan_providers_ctx_t *ctx)
 
1284
{
 
1285
        const pkgconf_dependency_t *pkgdep = ctx->pkgdep;
 
1286
        pkgconf_node_t *node;
 
1287
 
 
1288
        PKGCONF_FOREACH_LIST_ENTRY(pkg->provides.head, node)
 
1289
        {
 
1290
                const pkgconf_dependency_t *provider = node->data;
 
1291
                if (!strcmp(provider->package, pkgdep->package))
 
1292
                        return pkgconf_pkg_scan_provides_vercmp(pkgdep, provider);
 
1293
        }
 
1294
 
 
1295
        return false;
 
1296
}
 
1297
 
 
1298
/*
 
1299
 * pkgconf_pkg_scan_providers(client, pkgdep, eflags)
 
1300
 *
 
1301
 * scan all available packages to see if a Provides rule matches the pkgdep.
 
1302
 */
 
1303
static pkgconf_pkg_t *
 
1304
pkgconf_pkg_scan_providers(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags)
 
1305
{
 
1306
        pkgconf_pkg_t *pkg;
 
1307
        pkgconf_pkg_scan_providers_ctx_t ctx = {
 
1308
                .pkgdep = pkgdep,
 
1309
        };
 
1310
 
 
1311
        pkg = pkgconf_scan_all(client, &ctx, (pkgconf_pkg_iteration_func_t) pkgconf_pkg_scan_provides_entry);
 
1312
        if (pkg != NULL)
 
1313
        {
 
1314
                pkgdep->match = pkgconf_pkg_ref(client, pkg);
 
1315
                return pkg;
 
1316
        }
 
1317
 
 
1318
        if (eflags != NULL)
 
1319
                *eflags |= PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND;
 
1320
 
 
1321
        return NULL;
 
1322
}
 
1323
 
 
1324
/*
 
1325
 * !doc
 
1326
 *
 
1327
 * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags)
 
1328
 *
 
1329
 *    Verify a pkgconf_dependency_t node in the depgraph.  If the dependency is solvable,
 
1330
 *    return the appropriate ``pkgconf_pkg_t`` object, else ``NULL``.
 
1331
 *
 
1332
 *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
 
1333
 *    :param pkgconf_dependency_t* pkgdep: The dependency graph node to solve.
 
1334
 *    :param uint* eflags: An optional pointer that, if set, will be populated with an error code from the resolver.
 
1335
 *    :return: On success, the appropriate ``pkgconf_pkg_t`` object to solve the dependency, else ``NULL``.
 
1336
 *    :rtype: pkgconf_pkg_t *
 
1337
 */
 
1338
pkgconf_pkg_t *
 
1339
pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags)
 
1340
{
 
1341
        pkgconf_pkg_t *pkg = NULL;
 
1342
 
 
1343
        if (eflags != NULL)
 
1344
                *eflags = PKGCONF_PKG_ERRF_OK;
 
1345
 
 
1346
        PKGCONF_TRACE(client, "trying to verify dependency: %s", pkgdep->package);
 
1347
 
 
1348
        if (pkgdep->match != NULL)
 
1349
        {
 
1350
                PKGCONF_TRACE(client, "cached dependency: %s -> %s@%p", pkgdep->package, pkgdep->match->id, pkgdep->match);
 
1351
                return pkgconf_pkg_ref(client, pkgdep->match);
 
1352
        }
 
1353
 
 
1354
        pkg = pkgconf_pkg_find(client, pkgdep->package);
 
1355
        if (pkg == NULL)
 
1356
        {
 
1357
                if (client->flags & PKGCONF_PKG_PKGF_SKIP_PROVIDES)
 
1358
                {
 
1359
                        if (eflags != NULL)
 
1360
                                *eflags |= PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND;
 
1361
 
 
1362
                        return NULL;
 
1363
                }
 
1364
 
 
1365
                return pkgconf_pkg_scan_providers(client, pkgdep, eflags);
 
1366
        }
 
1367
 
 
1368
        if (pkg->id == NULL)
 
1369
                pkg->id = strdup(pkgdep->package);
 
1370
 
 
1371
        if (pkgconf_pkg_comparator_impls[pkgdep->compare](pkg->version, pkgdep->version) != true)
 
1372
        {
 
1373
                if (eflags != NULL)
 
1374
                        *eflags |= PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH;
 
1375
        }
 
1376
        else
 
1377
                pkgdep->match = pkgconf_pkg_ref(client, pkg);
 
1378
 
 
1379
        return pkg;
 
1380
}
 
1381
 
 
1382
/*
 
1383
 * !doc
 
1384
 *
 
1385
 * .. c:function:: unsigned int pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth)
 
1386
 *
 
1387
 *    Verify the graph dependency nodes are satisfiable by walking the tree using
 
1388
 *    ``pkgconf_pkg_traverse()``.
 
1389
 *
 
1390
 *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
 
1391
 *    :param pkgconf_pkg_t* root: The root entry in the package dependency graph which should contain the top-level dependencies to resolve.
 
1392
 *    :param int depth: The maximum allowed depth for dependency resolution.
 
1393
 *    :return: On success, ``PKGCONF_PKG_ERRF_OK`` (0), else an error code.
 
1394
 *    :rtype: unsigned int
 
1395
 */
 
1396
unsigned int
 
1397
pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth)
 
1398
{
 
1399
        return pkgconf_pkg_traverse(client, root, NULL, NULL, depth, 0);
 
1400
}
 
1401
 
 
1402
static unsigned int
 
1403
pkgconf_pkg_report_graph_error(pkgconf_client_t *client, pkgconf_pkg_t *parent, pkgconf_pkg_t *pkg, pkgconf_dependency_t *node, unsigned int eflags)
 
1404
{
 
1405
        if (eflags & PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND)
 
1406
        {
 
1407
                if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS) & !client->already_sent_notice)
 
1408
                {
 
1409
                        pkgconf_error(client, "Package %s was not found in the pkg-config search path.\n", node->package);
 
1410
                        pkgconf_error(client, "Perhaps you should add the directory containing `%s.pc'\n", node->package);
 
1411
                        pkgconf_error(client, "to the PKG_CONFIG_PATH environment variable\n");
 
1412
                        client->already_sent_notice = true;
 
1413
                }
 
1414
 
 
1415
                pkgconf_error(client, "Package '%s', required by '%s', not found\n", node->package, parent->id);
 
1416
                pkgconf_audit_log(client, "%s NOT-FOUND\n", node->package);
 
1417
        }
 
1418
        else if (eflags & PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH)
 
1419
        {
 
1420
                pkgconf_error(client, "Package dependency requirement '%s %s %s' could not be satisfied.\n",
 
1421
                        node->package, pkgconf_pkg_get_comparator(node), node->version);
 
1422
 
 
1423
                if (pkg != NULL)
 
1424
                        pkgconf_error(client, "Package '%s' has version '%s', required version is '%s %s'\n",
 
1425
                                node->package, pkg->version, pkgconf_pkg_get_comparator(node), node->version);
 
1426
        }
 
1427
 
 
1428
        if (pkg != NULL)
 
1429
                pkgconf_pkg_unref(client, pkg);
 
1430
 
 
1431
        return eflags;
 
1432
}
 
1433
 
 
1434
static inline unsigned int
 
1435
pkgconf_pkg_walk_list(pkgconf_client_t *client,
 
1436
        pkgconf_pkg_t *parent,
 
1437
        pkgconf_list_t *deplist,
 
1438
        pkgconf_pkg_traverse_func_t func,
 
1439
        void *data,
 
1440
        int depth,
 
1441
        unsigned int skip_flags)
 
1442
{
 
1443
        unsigned int eflags = PKGCONF_PKG_ERRF_OK;
 
1444
        pkgconf_node_t *node;
 
1445
 
 
1446
        PKGCONF_FOREACH_LIST_ENTRY(deplist->head, node)
 
1447
        {
 
1448
                unsigned int eflags_local = PKGCONF_PKG_ERRF_OK;
 
1449
                pkgconf_dependency_t *depnode = node->data;
 
1450
                pkgconf_pkg_t *pkgdep;
 
1451
 
 
1452
                if (*depnode->package == '\0')
 
1453
                        continue;
 
1454
 
 
1455
                pkgdep = pkgconf_pkg_verify_dependency(client, depnode, &eflags_local);
 
1456
 
 
1457
                eflags |= eflags_local;
 
1458
                if (eflags_local != PKGCONF_PKG_ERRF_OK && !(client->flags & PKGCONF_PKG_PKGF_SKIP_ERRORS))
 
1459
                {
 
1460
                        pkgconf_pkg_report_graph_error(client, parent, pkgdep, depnode, eflags_local);
 
1461
                        continue;
 
1462
                }
 
1463
                if (pkgdep == NULL)
 
1464
                        continue;
 
1465
 
 
1466
                if (pkgdep->flags & PKGCONF_PKG_PROPF_SEEN)
 
1467
                {
 
1468
                        pkgconf_pkg_unref(client, pkgdep);
 
1469
                        continue;
 
1470
                }
 
1471
 
 
1472
                if (skip_flags && (depnode->flags & skip_flags) == skip_flags)
 
1473
                {
 
1474
                        pkgconf_pkg_unref(client, pkgdep);
 
1475
                        continue;
 
1476
                }
 
1477
 
 
1478
                pkgconf_audit_log_dependency(client, pkgdep, depnode);
 
1479
 
 
1480
                pkgdep->flags |= PKGCONF_PKG_PROPF_SEEN;
 
1481
                eflags |= pkgconf_pkg_traverse(client, pkgdep, func, data, depth - 1, skip_flags);
 
1482
                pkgdep->flags &= ~PKGCONF_PKG_PROPF_SEEN;
 
1483
                pkgconf_pkg_unref(client, pkgdep);
 
1484
        }
 
1485
 
 
1486
        return eflags;
 
1487
}
 
1488
 
 
1489
static inline unsigned int
 
1490
pkgconf_pkg_walk_conflicts_list(pkgconf_client_t *client,
 
1491
        pkgconf_pkg_t *root, pkgconf_list_t *deplist)
 
1492
{
 
1493
        unsigned int eflags;
 
1494
        pkgconf_node_t *node, *childnode;
 
1495
 
 
1496
        PKGCONF_FOREACH_LIST_ENTRY(deplist->head, node)
 
1497
        {
 
1498
                pkgconf_dependency_t *parentnode = node->data;
 
1499
 
 
1500
                if (*parentnode->package == '\0')
 
1501
                        continue;
 
1502
 
 
1503
                PKGCONF_FOREACH_LIST_ENTRY(root->required.head, childnode)
 
1504
                {
 
1505
                        pkgconf_pkg_t *pkgdep;
 
1506
                        pkgconf_dependency_t *depnode = childnode->data;
 
1507
 
 
1508
                        if (*depnode->package == '\0' || strcmp(depnode->package, parentnode->package))
 
1509
                                continue;
 
1510
 
 
1511
                        pkgdep = pkgconf_pkg_verify_dependency(client, parentnode, &eflags);
 
1512
                        if (eflags == PKGCONF_PKG_ERRF_OK)
 
1513
                        {
 
1514
                                pkgconf_error(client, "Version '%s' of '%s' conflicts with '%s' due to satisfying conflict rule '%s %s%s%s'.\n",
 
1515
                                        pkgdep->version, pkgdep->realname, root->realname, parentnode->package, pkgconf_pkg_get_comparator(parentnode),
 
1516
                                        parentnode->version != NULL ? " " : "", parentnode->version != NULL ? parentnode->version : "");
 
1517
 
 
1518
                                if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS))
 
1519
                                {
 
1520
                                        pkgconf_error(client, "It may be possible to ignore this conflict and continue, try the\n");
 
1521
                                        pkgconf_error(client, "PKG_CONFIG_IGNORE_CONFLICTS environment variable.\n");
 
1522
                                }
 
1523
 
 
1524
                                pkgconf_pkg_unref(client, pkgdep);
 
1525
 
 
1526
                                return PKGCONF_PKG_ERRF_PACKAGE_CONFLICT;
 
1527
                        }
 
1528
 
 
1529
                        pkgconf_pkg_unref(client, pkgdep);
 
1530
                }
 
1531
        }
 
1532
 
 
1533
        return PKGCONF_PKG_ERRF_OK;
 
1534
}
 
1535
 
 
1536
/*
 
1537
 * !doc
 
1538
 *
 
1539
 * .. c:function:: unsigned int pkgconf_pkg_traverse(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_pkg_traverse_func_t func, void *data, int maxdepth, unsigned int skip_flags)
 
1540
 *
 
1541
 *    Walk and resolve the dependency graph up to `maxdepth` levels.
 
1542
 *
 
1543
 *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
 
1544
 *    :param pkgconf_pkg_t* root: The root of the dependency graph.
 
1545
 *    :param pkgconf_pkg_traverse_func_t func: A traversal function to call for each resolved node in the dependency graph.
 
1546
 *    :param void* data: An opaque pointer to data to be passed to the traversal function.
 
1547
 *    :param int maxdepth: The maximum depth to walk the dependency graph for.  -1 means infinite recursion.
 
1548
 *    :param uint skip_flags: Skip over dependency nodes containing the specified flags.  A setting of 0 skips no dependency nodes.
 
1549
 *    :return: ``PKGCONF_PKG_ERRF_OK`` on success, else an error code.
 
1550
 *    :rtype: unsigned int
 
1551
 */
 
1552
unsigned int
 
1553
pkgconf_pkg_traverse(pkgconf_client_t *client,
 
1554
        pkgconf_pkg_t *root,
 
1555
        pkgconf_pkg_traverse_func_t func,
 
1556
        void *data,
 
1557
        int maxdepth,
 
1558
        unsigned int skip_flags)
 
1559
{
 
1560
        unsigned int eflags = PKGCONF_PKG_ERRF_OK;
 
1561
 
 
1562
        if (maxdepth == 0)
 
1563
                return eflags;
 
1564
 
 
1565
        PKGCONF_TRACE(client, "%s: level %d", root->id, maxdepth);
 
1566
 
 
1567
        if ((root->flags & PKGCONF_PKG_PROPF_VIRTUAL) != PKGCONF_PKG_PROPF_VIRTUAL || (client->flags & PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL) != PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL)
 
1568
        {
 
1569
                if (func != NULL)
 
1570
                        func(client, root, data);
 
1571
        }
 
1572
 
 
1573
        if (!(client->flags & PKGCONF_PKG_PKGF_SKIP_CONFLICTS))
 
1574
        {
 
1575
                eflags = pkgconf_pkg_walk_conflicts_list(client, root, &root->conflicts);
 
1576
                if (eflags != PKGCONF_PKG_ERRF_OK)
 
1577
                        return eflags;
 
1578
        }
 
1579
 
 
1580
        PKGCONF_TRACE(client, "%s: walking requires list", root->id);
 
1581
        eflags = pkgconf_pkg_walk_list(client, root, &root->required, func, data, maxdepth, skip_flags);
 
1582
        if (eflags != PKGCONF_PKG_ERRF_OK)
 
1583
                return eflags;
 
1584
 
 
1585
        if (client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE)
 
1586
        {
 
1587
                PKGCONF_TRACE(client, "%s: walking requires.private list", root->id);
 
1588
 
 
1589
                /* XXX: ugly */
 
1590
                client->flags |= PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE;
 
1591
                eflags = pkgconf_pkg_walk_list(client, root, &root->requires_private, func, data, maxdepth, skip_flags);
 
1592
                client->flags &= ~PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE;
 
1593
 
 
1594
                if (eflags != PKGCONF_PKG_ERRF_OK)
 
1595
                        return eflags;
 
1596
        }
 
1597
 
 
1598
        return eflags;
 
1599
}
 
1600
 
 
1601
static void
 
1602
pkgconf_pkg_cflags_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
 
1603
{
 
1604
        pkgconf_list_t *list = data;
 
1605
        pkgconf_node_t *node;
 
1606
 
 
1607
        PKGCONF_FOREACH_LIST_ENTRY(pkg->cflags.head, node)
 
1608
        {
 
1609
                pkgconf_fragment_t *frag = node->data;
 
1610
                pkgconf_fragment_copy(client, list, frag, false);
 
1611
        }
 
1612
}
 
1613
 
 
1614
static void
 
1615
pkgconf_pkg_cflags_private_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
 
1616
{
 
1617
        pkgconf_list_t *list = data;
 
1618
        pkgconf_node_t *node;
 
1619
 
 
1620
        PKGCONF_FOREACH_LIST_ENTRY(pkg->cflags_private.head, node)
 
1621
        {
 
1622
                pkgconf_fragment_t *frag = node->data;
 
1623
                pkgconf_fragment_copy(client, list, frag, true);
 
1624
        }
 
1625
}
 
1626
 
 
1627
/*
 
1628
 * !doc
 
1629
 *
 
1630
 * .. c:function:: int pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
 
1631
 *
 
1632
 *    Walks a dependency graph and extracts relevant ``CFLAGS`` fragments.
 
1633
 *
 
1634
 *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
 
1635
 *    :param pkgconf_pkg_t* root: The root of the dependency graph.
 
1636
 *    :param pkgconf_list_t* list: The fragment list to add the extracted ``CFLAGS`` fragments to.
 
1637
 *    :param int maxdepth: The maximum allowed depth for dependency resolution.  -1 means infinite recursion.
 
1638
 *    :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code.
 
1639
 *    :rtype: unsigned int
 
1640
 */
 
1641
unsigned int
 
1642
pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
 
1643
{
 
1644
        unsigned int eflag;
 
1645
        unsigned int skip_flags = (client->flags & PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS) == 0 ? PKGCONF_PKG_DEPF_INTERNAL : 0;
 
1646
        pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
 
1647
 
 
1648
        eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_collect, &frags, maxdepth, skip_flags);
 
1649
 
 
1650
        if (eflag == PKGCONF_PKG_ERRF_OK && client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS)
 
1651
                eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_private_collect, &frags, maxdepth, skip_flags);
 
1652
 
 
1653
        if (eflag != PKGCONF_PKG_ERRF_OK)
 
1654
        {
 
1655
                pkgconf_fragment_free(&frags);
 
1656
                return eflag;
 
1657
        }
 
1658
 
 
1659
        pkgconf_fragment_copy_list(client, list, &frags);
 
1660
        pkgconf_fragment_free(&frags);
 
1661
 
 
1662
        return eflag;
 
1663
}
 
1664
 
 
1665
static void
 
1666
pkgconf_pkg_libs_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
 
1667
{
 
1668
        pkgconf_list_t *list = data;
 
1669
        pkgconf_node_t *node;
 
1670
 
 
1671
        PKGCONF_FOREACH_LIST_ENTRY(pkg->libs.head, node)
 
1672
        {
 
1673
                pkgconf_fragment_t *frag = node->data;
 
1674
                pkgconf_fragment_copy(client, list, frag, (client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) != 0);
 
1675
        }
 
1676
 
 
1677
        if (client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS)
 
1678
        {
 
1679
                PKGCONF_FOREACH_LIST_ENTRY(pkg->libs_private.head, node)
 
1680
                {
 
1681
                        pkgconf_fragment_t *frag = node->data;
 
1682
                        pkgconf_fragment_copy(client, list, frag, true);
 
1683
                }
 
1684
        }
 
1685
}
 
1686
 
 
1687
/*
 
1688
 * !doc
 
1689
 *
 
1690
 * .. c:function:: int pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
 
1691
 *
 
1692
 *    Walks a dependency graph and extracts relevant ``LIBS`` fragments.
 
1693
 *
 
1694
 *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
 
1695
 *    :param pkgconf_pkg_t* root: The root of the dependency graph.
 
1696
 *    :param pkgconf_list_t* list: The fragment list to add the extracted ``LIBS`` fragments to.
 
1697
 *    :param int maxdepth: The maximum allowed depth for dependency resolution.  -1 means infinite recursion.
 
1698
 *    :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code.
 
1699
 *    :rtype: unsigned int
 
1700
 */
 
1701
unsigned int
 
1702
pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
 
1703
{
 
1704
        unsigned int eflag;
 
1705
 
 
1706
        eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_libs_collect, list, maxdepth, 0);
 
1707
 
 
1708
        if (eflag != PKGCONF_PKG_ERRF_OK)
 
1709
        {
 
1710
                pkgconf_fragment_free(list);
 
1711
                return eflag;
 
1712
        }
 
1713
 
 
1714
        return eflag;
 
1715
}