summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Pronchery <khorben@FreeBSD.org>2026-04-22 13:13:41 +0000
committerPierre Pronchery <khorben@FreeBSD.org>2026-04-22 13:13:41 +0000
commit6294b6ab217a2d5f1d2bc23a64505a228294c508 (patch)
tree333ee95d0d49af6cfc4390c1ec90098e6493e332
-rw-r--r--AUTHORS41
-rw-r--r--CODE_OF_CONDUCT.md27
-rw-r--r--COPYING10
-rw-r--r--Kyuafile.in5
-rw-r--r--Makefile.am217
-rw-r--r--Makefile.lite77
-rw-r--r--NEWS943
-rw-r--r--README.md130
-rwxr-xr-xautogen.sh87
-rw-r--r--cli/bomtool/main.c368
-rw-r--r--cli/getopt_long.c643
-rw-r--r--cli/getopt_long.h70
-rw-r--r--cli/main.c1823
-rw-r--r--cli/renderer-msvc.c172
-rw-r--r--cli/renderer-msvc.h23
-rw-r--r--configure.ac66
-rw-r--r--doc/conf.py339
-rw-r--r--doc/extract.py149
-rw-r--r--doc/index.rst14
-rw-r--r--doc/libpkgconf-argvsplit.rst23
-rw-r--r--doc/libpkgconf-audit.rst35
-rw-r--r--doc/libpkgconf-cache.rst45
-rw-r--r--doc/libpkgconf-client.rst212
-rw-r--r--doc/libpkgconf-dependency.rst90
-rw-r--r--doc/libpkgconf-fragment.rst116
-rw-r--r--doc/libpkgconf-path.rst71
-rw-r--r--doc/libpkgconf-personality.rst27
-rw-r--r--doc/libpkgconf-pkg.rst154
-rw-r--r--doc/libpkgconf-queue.rst78
-rw-r--r--doc/libpkgconf-tuple.rst92
-rw-r--r--doc/libpkgconf.rst17
-rw-r--r--libpkgconf.pc.in12
-rw-r--r--libpkgconf/argvsplit.c161
-rw-r--r--libpkgconf/audit.c98
-rw-r--r--libpkgconf/bsdstubs.c197
-rw-r--r--libpkgconf/bsdstubs.h36
-rw-r--r--libpkgconf/buffer.c87
-rw-r--r--libpkgconf/cache.c231
-rw-r--r--libpkgconf/client.c817
-rw-r--r--libpkgconf/config.h.meson79
-rw-r--r--libpkgconf/dependency.c506
-rw-r--r--libpkgconf/fileio.c113
-rw-r--r--libpkgconf/fragment.c803
-rw-r--r--libpkgconf/iter.h113
-rw-r--r--libpkgconf/libpkgconf-api.h19
-rw-r--r--libpkgconf/libpkgconf.h493
-rw-r--r--libpkgconf/meson.build12
-rw-r--r--libpkgconf/parser.c115
-rw-r--r--libpkgconf/path.c459
-rw-r--r--libpkgconf/personality.c359
-rw-r--r--libpkgconf/pkg.c1935
-rw-r--r--libpkgconf/queue.c408
-rw-r--r--libpkgconf/stdinc.h75
-rw-r--r--libpkgconf/tuple.c476
-rw-r--r--libpkgconf/win-dirent.h1028
-rw-r--r--m4/ax_check_compile_flag.m453
-rw-r--r--man/bomtool.1100
-rw-r--r--man/pc.5178
-rw-r--r--man/pkg.m4.7143
-rw-r--r--man/pkgconf-personality.5100
-rw-r--r--man/pkgconf.1758
-rw-r--r--meson.build199
-rw-r--r--meson_options.txt19
-rw-r--r--pkg.m4350
-rw-r--r--pkgconf.wxs.in64
-rw-r--r--tests/Kyuafile.in14
-rwxr-xr-xtests/basic.sh391
-rwxr-xr-xtests/builtins.sh68
-rwxr-xr-xtests/conflicts.sh23
-rwxr-xr-xtests/framework.sh20
-rw-r--r--tests/lib-relocatable/lib/pkgconfig/foo.pc11
-rw-r--r--tests/lib1/argv-parse-2.pc10
-rw-r--r--tests/lib1/argv-parse-3.pc11
-rw-r--r--tests/lib1/argv-parse.pc10
-rw-r--r--tests/lib1/bar.pc10
-rw-r--r--tests/lib1/baz.pc11
-rw-r--r--tests/lib1/billion-laughs.pc13
-rw-r--r--tests/lib1/c-comment.pc17
-rw-r--r--tests/lib1/case-sensitivity.pc7
-rw-r--r--tests/lib1/cflags-internal.pc9
-rw-r--r--tests/lib1/cflags-libs-only.pc10
-rw-r--r--tests/lib1/cflags-libs-private-a.pc7
-rw-r--r--tests/lib1/cflags-libs-private-b.pc7
-rw-r--r--tests/lib1/cflags-libs-private-c.pc6
-rw-r--r--tests/lib1/cflags-whitespace-trailing.pc4
-rw-r--r--tests/lib1/cflags-whitespace.pc4
-rw-r--r--tests/lib1/child-prefix/pkgconfig/child-prefix-1.pc11
-rw-r--r--tests/lib1/circular-1.pc11
-rw-r--r--tests/lib1/circular-2.pc11
-rw-r--r--tests/lib1/circular-3.pc11
-rw-r--r--tests/lib1/comments-in-fields.pc9
-rw-r--r--tests/lib1/comments.pc6
-rw-r--r--tests/lib1/conflicts.pc11
-rw-r--r--tests/lib1/depgraph-break.pc12
-rw-r--r--tests/lib1/dos-lineendings.pc10
-rw-r--r--tests/lib1/empty-tuple.pc6
-rw-r--r--tests/lib1/escaped-backslash.pc4
-rw-r--r--tests/lib1/explicit-sysroot.pc7
-rw-r--r--tests/lib1/flag-order-1.pc10
-rw-r--r--tests/lib1/flag-order-3.pc10
-rw-r--r--tests/lib1/foo.pc12
-rw-r--r--tests/lib1/foobar.pc12
-rw-r--r--tests/lib1/fragment-collision-1.pc4
-rw-r--r--tests/lib1/fragment-collision-2.pc4
-rw-r--r--tests/lib1/fragment-collision-intermediary.pc5
-rw-r--r--tests/lib1/fragment-collision.pc6
-rw-r--r--tests/lib1/fragment-comment.pc4
-rw-r--r--tests/lib1/fragment-escaping-1.pc5
-rw-r--r--tests/lib1/fragment-escaping-2.pc4
-rw-r--r--tests/lib1/fragment-escaping-3.pc4
-rw-r--r--tests/lib1/fragment-group-a.pc4
-rw-r--r--tests/lib1/fragment-group-b.pc4
-rw-r--r--tests/lib1/fragment-group-c.pc4
-rw-r--r--tests/lib1/fragment-groups-2.pc4
-rw-r--r--tests/lib1/fragment-groups.pc4
-rw-r--r--tests/lib1/fragment-quoting-2.pc10
-rw-r--r--tests/lib1/fragment-quoting-3.pc10
-rw-r--r--tests/lib1/fragment-quoting-5.pc10
-rw-r--r--tests/lib1/fragment-quoting-7.pc4
-rw-r--r--tests/lib1/fragment-quoting.pc10
-rw-r--r--tests/lib1/framework-1.pc9
-rw-r--r--tests/lib1/framework-2.pc10
-rw-r--r--tests/lib1/idirafter-ordering.pc4
-rw-r--r--tests/lib1/idirafter.pc4
-rw-r--r--tests/lib1/incomplete.pc4
-rw-r--r--tests/lib1/intermediary-1.pc10
-rw-r--r--tests/lib1/intermediary-2.pc10
-rw-r--r--tests/lib1/isystem.pc4
-rw-r--r--tests/lib1/malformed-1.pc2
-rw-r--r--tests/lib1/malformed-quoting.pc4
-rw-r--r--tests/lib1/malformed-version.pc5
-rw-r--r--tests/lib1/metapackage-1.pc6
-rw-r--r--tests/lib1/metapackage-2.pc5
-rw-r--r--tests/lib1/metapackage-3.pc4
-rw-r--r--tests/lib1/metapackage.pc4
-rw-r--r--tests/lib1/missing-require.pc11
-rw-r--r--tests/lib1/multiline-bogus.pc9
-rw-r--r--tests/lib1/multiline.pc9
-rw-r--r--tests/lib1/no-trailing-newline.pc10
-rw-r--r--tests/lib1/nocflag.pc9
-rw-r--r--tests/lib1/nolib.pc9
-rw-r--r--tests/lib1/omg-sysroot-uninstalled.pc10
-rw-r--r--tests/lib1/omg-uninstalled.pc10
-rw-r--r--tests/lib1/orphaned-requires-private.pc8
-rw-r--r--tests/lib1/paren-quoting.pc5
-rw-r--r--tests/lib1/pcfiledir.pc8
-rw-r--r--tests/lib1/prefix-foo1.pc12
-rw-r--r--tests/lib1/prefix-foo2.pc12
-rw-r--r--tests/lib1/private-libs-duplication.pc7
-rw-r--r--tests/lib1/provides-request-simple.pc6
-rw-r--r--tests/lib1/provides.pc6
-rw-r--r--tests/lib1/quotes.pc10
-rw-r--r--tests/lib1/requires-internal-2.pc9
-rw-r--r--tests/lib1/requires-internal-collision.pc10
-rw-r--r--tests/lib1/requires-internal-missing.pc10
-rw-r--r--tests/lib1/requires-internal.pc10
-rw-r--r--tests/lib1/spaces-in-paths.pc7
-rw-r--r--tests/lib1/static-archive-libs.pc10
-rw-r--r--tests/lib1/static-libs.pc12
-rw-r--r--tests/lib1/sysroot-dir-2.pc7
-rw-r--r--tests/lib1/sysroot-dir-3.pc9
-rw-r--r--tests/lib1/sysroot-dir-4.pc9
-rw-r--r--tests/lib1/sysroot-dir-5.pc9
-rw-r--r--tests/lib1/sysroot-dir.pc5
-rw-r--r--tests/lib1/tilde-quoting.pc5
-rw-r--r--tests/lib1/tilde.pc11
-rw-r--r--tests/lib1/truncated.pc1
-rw-r--r--tests/lib1/tuple-quoting.pc7
-rw-r--r--tests/lib1/typelibdir.pc11
-rw-r--r--tests/lib1/unavailable-provider.pc11
-rw-r--r--tests/lib1/utf8.pc10
-rw-r--r--tests/lib1/variable-whitespace.pc11
-rw-r--r--tests/lib2/foo.pc10
-rw-r--r--tests/lib3/bar.pc10
-rw-r--r--tests/meson.build23
-rwxr-xr-xtests/parser.sh372
-rwxr-xr-xtests/provides.sh310
-rwxr-xr-xtests/regress.sh354
-rwxr-xr-xtests/requires.sh176
-rwxr-xr-xtests/symlink.sh122
-rwxr-xr-xtests/sysroot.sh110
-rw-r--r--tests/test_env.sh.in50
-rwxr-xr-xtests/version.sh38
-rw-r--r--txt2rtf.py35
184 files changed, 19696 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 000000000000..9ba1de45cb0b
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,41 @@
+A. Wilcox <AWilcox@Wilcox-Tech.com>
+Alexander Tsoy <alexander@tsoy.me>
+Alexpux <alexey.pawlow@gmail.com>
+Alon Bar-Lev <alon.barlev@gmail.com>
+Alyx <alyx@malkier.net>
+Ariadne Conill <ariadne@dereferenced.org>
+Baptiste Daroussin <bapt@FreeBSD.org>
+Baptiste Daroussin <bapt@gandi.net>
+Bryan Drewery <bryan@shatow.net>
+Dag-Erling Smørgrav <des@des.no>
+Dan Kegel <dank@kegel.com>
+Dan Kegel <dank@oblong.com>
+Dan Nicholson <dbn.lists@gmail.com>
+David Michael <fedora.dm0@gmail.com>
+Emil Renner Berthing <esmil@mailme.dk>
+Fabian Groffen <grobian@gentoo.org>
+Graham Ollis <plicease@cpan.org>
+Gregor Richards <Richards@codu.org>
+Ignacio Casal Quinteiro <qignacio@amazon.com>
+Igor Gnatenko <ignatenko@redhat.com>
+Issam Maghni <concatime@users.noreply.github.com>
+JD Horelick <jdhore1@gmail.com>
+Jason Dusek <jason.dusek@gmail.com>
+Javier Viguera <javier.viguera@digi.com>
+Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
+John Hein <jhgit@users.github.com>
+Jussi Pakkanen <jpakkane@gmail.com>
+Leorize <alaviss@users.noreply.github.com>
+Luca Barbato <lu_zero@gentoo.org>
+Marcin Wojdyr <wojdyr@gmail.com>
+Maxin B. John <maxinbjohn@users.noreply.github.com>
+Michał Górny <mgorny@gentoo.org>
+Mike Frysinger <vapier@gentoo.org>
+Seungha Yang <seungha.yang@navercorp.com>
+TingPing <tingping@tingping.se>
+Tobias Kortkamp <t6@users.noreply.github.com>
+Tony Theodore <tonyt@logyst.com>
+Volker Braun <vbraun.name@gmail.com>
+Yu Kobayashi <yukoba@accelart.jp>
+orbea <orbea@fredslev.dk>
+✈ Graham ✈ <plicease@cpan.org>
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000000..1dc77b3ef9af
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,27 @@
+# pkgconf Code of Conduct
+
+We invite and encourage everybody to express their opinions on relevant
+topics. All participants should at all times feel at ease to do so without
+fearing any form of attack, reprisal or harassment. We ask everybody to be
+respectful and considerate towards each other, especially when attempting
+to provide constructive criticism.
+
+To foster tolerance, respect and hospitality in our community, we agree not
+to engage in discriminatory, disparaging or offensive speech or actions,
+including as to (but not limited to) gender, sexuality, race, nationality,
+religion or profession. We are a community of many different nationalities
+and backgrounds, and we cherish our strength in diversity.
+
+
+## Reporting incidents
+
+Please report any incidents which may be perceived as violations to
+`ariadne+conduct@dereferenced.org`. Incidents will be investigated and,
+if warranted, acted upon.
+
+
+## Credits
+
+This CoC is derived from the [FSFE Code of Conduct][fsfe-coc].
+
+ [fsfe-coc]: https://fsfe.org/about/codeofconduct.en.html
diff --git a/COPYING b/COPYING
new file mode 100644
index 000000000000..35c7c16aa7e3
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,10 @@
+Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ pkgconf authors (see AUTHORS file in source directory).
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+This software is provided 'as is' and without any warranty, express or
+implied. In no event shall the authors be liable for any damages arising
+from the use of this software.
diff --git a/Kyuafile.in b/Kyuafile.in
new file mode 100644
index 000000000000..1490fef49c80
--- /dev/null
+++ b/Kyuafile.in
@@ -0,0 +1,5 @@
+syntax(2)
+
+test_suite('pkgconf')
+
+include('tests/Kyuafile')
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 000000000000..93486a17a0b7
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,217 @@
+libdir = @libdir@
+datarootdir = @datarootdir@
+datadir = @datadir@
+includedir = @includedir@
+system_includedir = @SYSTEM_INCLUDEDIR@
+system_libdir = @SYSTEM_LIBDIR@
+pkg_default_dir = @PKG_DEFAULT_PATH@
+personality_dir = @PERSONALITY_PATH@
+pkgconfigdir = $(libdir)/pkgconfig
+nodist_pkgconfig_DATA = libpkgconf.pc
+
+ACLOCAL_AMFLAGS = -I m4
+AM_CFLAGS = -DPERSONALITY_PATH=\"$(personality_dir)\" -DPKG_DEFAULT_PATH=\"$(pkg_default_dir)\" -DSYSTEM_INCLUDEDIR=\"$(system_includedir)\" -DSYSTEM_LIBDIR=\"$(system_libdir)\"
+
+bin_PROGRAMS = pkgconf bomtool
+lib_LTLIBRARIES = libpkgconf.la
+
+EXTRA_DIST = pkg.m4 \
+ meson.build \
+ meson_options.txt \
+ pkgconf.wxs.in \
+ txt2rtf.py \
+ libpkgconf/meson.build \
+ libpkgconf/config.h.meson \
+ libpkgconf/win-dirent.h \
+ tests/lib-relocatable/lib/pkgconfig/foo.pc \
+ tests/lib1/argv-parse-2.pc \
+ tests/lib1/billion-laughs.pc \
+ tests/lib1/dos-lineendings.pc \
+ tests/lib1/paren-quoting.pc \
+ tests/lib1/argv-parse-3.pc \
+ tests/lib1/foo.pc \
+ tests/lib1/foobar.pc \
+ tests/lib1/unavailable-provider.pc \
+ tests/lib1/prefix-foo1.pc \
+ tests/lib1/argv-parse.pc \
+ tests/lib1/framework-1.pc \
+ tests/lib1/prefix-foo2.pc \
+ tests/lib1/bar.pc \
+ tests/lib1/framework-2.pc \
+ tests/lib1/private-libs-duplication.pc \
+ tests/lib1/baz.pc \
+ tests/lib1/incomplete.pc \
+ tests/lib1/quotes.pc \
+ tests/lib1/case-sensitivity.pc \
+ tests/lib1/intermediary-1.pc \
+ tests/lib1/static-archive-libs.pc \
+ tests/lib1/cflags-libs-only.pc \
+ tests/lib1/intermediary-2.pc \
+ tests/lib1/static-libs.pc \
+ tests/lib1/circular-1.pc \
+ tests/lib1/missing-require.pc \
+ tests/lib1/sysroot-dir.pc \
+ tests/lib1/circular-2.pc \
+ tests/lib1/multiline.pc \
+ tests/lib1/multiline-bogus.pc \
+ tests/lib1/tilde-quoting.pc \
+ tests/lib1/circular-3.pc \
+ tests/lib1/no-trailing-newline.pc \
+ tests/lib1/tilde.pc \
+ tests/lib1/comments-in-fields.pc \
+ tests/lib1/nocflag.pc \
+ tests/lib1/typelibdir.pc \
+ tests/lib2/foo.pc \
+ tests/lib1/comments.pc \
+ tests/lib1/nolib.pc \
+ tests/lib3/bar.pc \
+ tests/lib1/conflicts.pc \
+ tests/lib1/omg-uninstalled.pc \
+ tests/lib1/omg-sysroot-uninstalled.pc \
+ tests/lib1/isystem.pc \
+ tests/lib1/idirafter.pc \
+ tests/lib1/idirafter-ordering.pc \
+ tests/lib1/depgraph-break.pc \
+ tests/lib1/cflags-whitespace.pc \
+ tests/lib1/cflags-whitespace-trailing.pc \
+ tests/lib1/provides.pc \
+ tests/lib1/provides-request-simple.pc \
+ tests/lib1/flag-order-1.pc \
+ tests/lib1/flag-order-3.pc \
+ tests/lib1/variable-whitespace.pc \
+ tests/lib1/fragment-collision.pc \
+ tests/lib1/fragment-collision-intermediary.pc \
+ tests/lib1/fragment-collision-1.pc \
+ tests/lib1/fragment-collision-2.pc \
+ tests/lib1/fragment-comment.pc \
+ tests/lib1/fragment-escaping-1.pc \
+ tests/lib1/fragment-escaping-2.pc \
+ tests/lib1/fragment-escaping-3.pc \
+ tests/lib1/fragment-quoting.pc \
+ tests/lib1/fragment-quoting-2.pc \
+ tests/lib1/fragment-quoting-3.pc \
+ tests/lib1/fragment-quoting-5.pc \
+ tests/lib1/fragment-quoting-7.pc \
+ tests/lib1/fragment-groups.pc \
+ tests/lib1/fragment-groups-2.pc \
+ tests/lib1/fragment-group-a.pc \
+ tests/lib1/fragment-group-b.pc \
+ tests/lib1/fragment-group-c.pc \
+ tests/lib1/malformed-1.pc \
+ tests/lib1/malformed-quoting.pc \
+ tests/lib1/malformed-version.pc \
+ tests/lib1/metapackage.pc \
+ tests/lib1/metapackage-1.pc \
+ tests/lib1/metapackage-2.pc \
+ tests/lib1/metapackage-3.pc \
+ tests/lib1/explicit-sysroot.pc \
+ tests/lib1/escaped-backslash.pc \
+ tests/lib1/cflags-internal.pc \
+ tests/lib1/requires-internal.pc \
+ tests/lib1/requires-internal-2.pc \
+ tests/lib1/requires-internal-missing.pc \
+ tests/lib1/requires-internal-collision.pc \
+ tests/lib1/tuple-quoting.pc \
+ tests/lib1/empty-tuple.pc \
+ tests/lib1/orphaned-requires-private.pc \
+ tests/lib1/pcfiledir.pc \
+ tests/lib1/sysroot-dir-2.pc \
+ tests/lib1/sysroot-dir-3.pc \
+ tests/lib1/sysroot-dir-4.pc \
+ tests/lib1/sysroot-dir-5.pc \
+ tests/lib1/child-prefix/pkgconfig/child-prefix-1.pc \
+ tests/lib1/cflags-libs-private-a.pc \
+ tests/lib1/cflags-libs-private-b.pc \
+ tests/lib1/cflags-libs-private-c.pc \
+ tests/lib1/truncated.pc \
+ tests/lib1/c-comment.pc \
+ tests/meson.build \
+ $(test_scripts) \
+ doc/conf.py \
+ doc/extract.py \
+ doc/index.rst \
+ doc/libpkgconf.rst \
+ doc/libpkgconf-argvsplit.rst \
+ doc/libpkgconf-audit.rst \
+ doc/libpkgconf-cache.rst \
+ doc/libpkgconf-client.rst \
+ doc/libpkgconf-dependency.rst \
+ doc/libpkgconf-fragment.rst \
+ doc/libpkgconf-path.rst \
+ doc/libpkgconf-pkg.rst \
+ doc/libpkgconf-queue.rst \
+ doc/libpkgconf-tuple.rst
+
+test_scripts= tests/basic.sh \
+ tests/builtins.sh \
+ tests/conflicts.sh \
+ tests/framework.sh \
+ tests/parser.sh \
+ tests/provides.sh \
+ tests/regress.sh \
+ tests/requires.sh \
+ tests/symlink.sh \
+ tests/sysroot.sh \
+ tests/version.sh
+
+test_sh = $(test_scripts)
+check_SCRIPTS = ${test_sh:.sh=}
+
+SUFFIXES= .sh
+
+nobase_pkginclude_HEADERS = libpkgconf/bsdstubs.h libpkgconf/iter.h libpkgconf/libpkgconf.h libpkgconf/stdinc.h libpkgconf/libpkgconf-api.h
+libpkgconf_la_SOURCES = \
+ libpkgconf/audit.c \
+ libpkgconf/buffer.c \
+ libpkgconf/cache.c \
+ libpkgconf/client.c \
+ libpkgconf/pkg.c \
+ libpkgconf/bsdstubs.c \
+ libpkgconf/fragment.c \
+ libpkgconf/argvsplit.c \
+ libpkgconf/fileio.c \
+ libpkgconf/tuple.c \
+ libpkgconf/dependency.c \
+ libpkgconf/queue.c \
+ libpkgconf/path.c \
+ libpkgconf/personality.c \
+ libpkgconf/parser.c
+libpkgconf_la_LDFLAGS = -no-undefined -version-info 7:0:0 -export-symbols-regex '^pkgconf_'
+
+dist_man_MANS = \
+ man/bomtool.1 \
+ man/pkgconf.1 \
+ man/pkg.m4.7 \
+ man/pc.5 \
+ man/pkgconf-personality.5
+
+pkgconf_LDADD = libpkgconf.la
+pkgconf_SOURCES = \
+ cli/main.c \
+ cli/getopt_long.c \
+ cli/renderer-msvc.c
+pkgconf_CPPFLAGS = -I$(top_srcdir)/libpkgconf -I$(top_srcdir)/cli
+noinst_HEADERS = \
+ cli/getopt_long.h \
+ cli/renderer-msvc.h
+
+bomtool_LDADD = libpkgconf.la
+bomtool_SOURCES = \
+ cli/bomtool/main.c \
+ cli/getopt_long.c
+bomtool_CPPFLAGS = -I$(top_srcdir)/libpkgconf -I$(top_srcdir)/cli -I$(top_srcdir)/cli/bomtool
+
+dist_doc_DATA = README.md AUTHORS
+
+m4datadir = $(datadir)/aclocal
+m4data_DATA = pkg.m4
+
+CLEANFILES = $(EXTRA_PROGRAMS) \
+ $(check_SCRIPTS)
+
+check: pkgconf $(check_SCRIPTS)
+ kyua --config=none test --kyuafile='$(top_builddir)/Kyuafile' \
+ --build-root='$(top_builddir)'
+
+.sh:
+ install -m 755 $< $@
diff --git a/Makefile.lite b/Makefile.lite
new file mode 100644
index 000000000000..6d1234331199
--- /dev/null
+++ b/Makefile.lite
@@ -0,0 +1,77 @@
+# Copyright (c) 2019 William Pitcock <nenolod@dereferenced.org>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# This software is provided 'as is' and without any warranty, express or
+# implied. In no event shall the authors be liable for any damages arising
+# from the use of this software.
+
+# pkgconf-lite is a staticly-linked version of pkgconf that does not include
+# all features, notably it does not include cross-compile support and MSVC
+# support. It does not include the libpkgconf library.
+
+SRCS = \
+ libpkgconf/argvsplit.c \
+ libpkgconf/audit.c \
+ libpkgconf/bsdstubs.c \
+ libpkgconf/cache.c \
+ libpkgconf/client.c \
+ libpkgconf/dependency.c \
+ libpkgconf/fileio.c \
+ libpkgconf/fragment.c \
+ libpkgconf/parser.c \
+ libpkgconf/path.c \
+ libpkgconf/personality.c \
+ libpkgconf/pkg.c \
+ libpkgconf/queue.c \
+ libpkgconf/tuple.c \
+ cli/getopt_long.c \
+ cli/main.c
+OBJS = ${SRCS:.c=.o}
+CFLAGS = ${STATIC} -DPKGCONF_LITE -I. -Ilibpkgconf -Icli -DSYSTEM_LIBDIR=\"${SYSTEM_LIBDIR}\" -DSYSTEM_INCLUDEDIR=\"${SYSTEM_INCLUDEDIR}\" -DPKG_DEFAULT_PATH=\"${PKG_DEFAULT_PATH}\"
+STATIC =
+STRIP = strip
+
+all: pkgconf-lite
+
+libpkgconf/config.h:
+ @echo '#define PACKAGE_NAME "pkgconf-lite"' >> $@
+ @echo '#define PACKAGE_BUGREPORT "https://git.dereferenced.org/pkgconf/pkgconf/issues"' >> $@
+ @echo '#define PACKAGE_VERSION "2.5.1"' >> $@
+ @echo '#define PACKAGE PACKAGE_NAME " " PACKAGE_VERSION' >> $@
+ @echo '#define HAVE_STRLCPY' >> $@
+ @echo '#define HAVE_STRLCAT' >> $@
+ @echo '#define HAVE_STRNDUP' >> $@
+
+pkgconf-lite: preflight libpkgconf/config.h ${OBJS}
+ ${CC} ${STATIC} -o $@ ${OBJS}
+ ${STRIP} $@
+
+clean:
+ rm -f libpkgconf/config.h
+ rm -f ${OBJS}
+ rm -f pkgconf-lite
+
+preflight: preflight-system-libdir preflight-system-includedir preflight-pkg-default-path
+
+preflight-system-libdir:
+ @if test -z "${SYSTEM_LIBDIR}"; then \
+ echo "SYSTEM_LIBDIR not set."; \
+ exit 1; \
+ fi
+
+preflight-system-includedir:
+ @if test -z "${SYSTEM_INCLUDEDIR}"; then \
+ echo "SYSTEM_INCLUDEDIR not set."; \
+ exit 1; \
+ fi
+
+preflight-pkg-default-path:
+ @if test -z "${PKG_DEFAULT_PATH}"; then \
+ echo "PKG_DEFAULT_PATH not set."; \
+ exit 1; \
+ fi
+
+.PHONY: preflight preflight-system-libdir preflight-system-includedir preflight-pkg-default-path clean
diff --git a/NEWS b/NEWS
new file mode 100644
index 000000000000..8b2947649e15
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,943 @@
+Changes from previous version of pkgconf
+========================================
+
+Changes from 2.5.0 to 2.5.1:
+----------------------------
+
+* Fix processing of empty dependency lists.
+
+Changes from 2.4.3 to 2.5.0:
+----------------------------
+
+* Added a manual page for bomtool.
+
+* Add support for preloaded packages.
+ These are modules which are preloaded into the package database
+ and preferred over searching the module search path when present.
+
+* Refactor Windows registry PKG_CONFIG_PATH support so that it
+ augments the main directory search list instead of being treated
+ as a special case.
+
+* Processing of `--with-path` arguments by the pkgconf CLI is
+ now deferred until libpkgconf is fully initialized, effectively
+ aligning behavior with PKG_CONFIG_PATH processing.
+
+* Fix several minor memory safety bugs which were identified by
+ the GCC 15 static analyzer.
+
+* Added support for pledge(2) and unveil(2) on systems where
+ this functionality is available.
+
+* Significant improvements to pkgconf's manual pages.
+ Patches by Ingo Schwarze and Jonathan Gray (OpenBSD).
+
+* Remove questionable default-static assumption on Windows that
+ was inherited from the original pkg-config. Most distributions
+ of pkgconf on Windows were already patching this out.
+ Patch by Kai Pastor.
+
+* Add -D_POSIX_C_SOURCE=200809L to the build definitions, which
+ is needed for readlinkat on glibc.
+ Patch by Filipe Laíns.
+
+Changes from 2.4.2 to 2.4.3:
+----------------------------
+
+* Fix additional logic errors relating to the new fragment trees
+ functionality.
+
+Changes from 2.4.1 to 2.4.2:
+----------------------------
+
+* Fix several logic errors in the pkg-config file parser that were
+ surfaced by recent refactoring work.
+
+* Fix BSD make compatibility so that it generates the test data
+ before running kyua on BSD make implementations.
+
+Changes from 2.4.0 to 2.4.1:
+----------------------------
+
+* Ensure the full DAG is solved for all query types.
+
+Changes from 2.3.0 to 2.4.0:
+----------------------------
+
+* Allow multiple package names in solution-based queries such as
+ `--print-requires`, `--print-requires-private` and `--print-provides`.
+
+* Use `_DEFAULT_SOURCE` where appropriate on Meson.
+
+* Add an abstract buffer type and use it when loading files from disk
+ instead of a 64KB buffer. This ensures large pkg-config files are
+ not truncated.
+
+* Disable graph recursion in `--variable` queries as it was generating
+ duplicate output.
+
+* Add infrastructure for tracking fragment group relations and convert
+ storage of fragments to use a tree-like structure instead of string
+ concatenation.
+
+* Add support for tracking linker groups, e.g.
+
+ -Wl,--start-group -la -lb -lc -Wl,--end-group
+
+ as fragment groups.
+
+* Properly contextualize the sysroot directory when processing package
+ information, ensuring packages where ${pc_sysrootdir} does not match
+ the default are properly processed.
+
+Changes from 2.2.0 to 2.3.0:
+----------------------------
+
+* Fix compile with Meson on Solaris by defining __EXTENSIONS__.
+
+* Add support for the PKG_CONFIG_RELOCATE_PATHS environmental variable.
+ When set, the program will act as if --define-prefix is always enabled.
+
+* Color solution nodes that were part of the original query, and use
+ that coloring to skip over dependencies when generating DocumentNames
+ in bomtool.
+
+* Enhance --env option to support variables with both --variable=varname
+ and --print-variables.
+
+* Add --exists-cflags option which creates synthetic preprocessor
+ definition flags for every queried dependency when found.
+
+* Document that Requires.private is always used for header paths.
+ Patch by Petr Písař.
+
+* Fix minor documentation typos.
+ Patch by Pierce.
+
+* Ensure string comparisons using <ctype.h> functions are done with
+ unsigned bytes to avoid undefined behavior.
+ Patch by Taylor R Campbell.
+
+* Fix parsing edge-case bugs with dependency versions.
+ Patch by Kai Pastor.
+
+* Change PKG_PROG_PKG_CONFIG autoconf macro to add a customizable
+ failure handler if pkg-config is not found.
+ Patch by Ismael Luceno.
+
+Changes from 2.1.1 to 2.2.0:
+----------------------------
+
+* libpkgconf SOVERSION is now 5.
+
+* Significant solver rework to flatten both requires and requires.private
+ dependencies in a single pass. Improves performance slightly and ensures
+ proper dependency order.
+ Patches by Kai Pastor.
+
+* Improve `--digraph` output to reflect more of the solver's state in the
+ rendered dependency graph.
+ Patches by Kai Pastor.
+
+* Do not reference the graph root by name when presenting error messages about
+ directly requested dependency nodes.
+ Patch by Kai Pastor.
+
+Changes from 2.1.0 to 2.1.1:
+----------------------------
+
+* Documentation fixes from Sam James and Stefan Weil.
+
+* Fix --modversion with constraints.
+ Patch by Kai Pastor.
+
+* Reintroduce an optimization to the dependency graph walker which avoids
+ revisiting already visited nodes.
+ Patch by Yi Chou with some modifications.
+
+* Add a regression test to check that the dependency flattener is working
+ as expected.
+ Patch by Kai Pastor.
+
+Changes from 2.0.3 to 2.1.0:
+----------------------------
+
+* Do not flatten the solver solution into the original world used as
+ input to the solver.
+ Patches by Kai Pastor.
+
+* Fix warnings with GCC 14 -Walloc-size.
+ Patch by Sam James.
+
+* Add --solution to the pkgconf CLI to dump the solver state.
+
+* Improve the --digraph output to clarify cancelled edges in a given
+ solution.
+
+* Demote requires dependencies to requires.private when a parent
+ dependency is pulled in via requires.private.
+
+* Trim trailing whitespace when processing package arguments.
+ Patch by Colin Gillespie.
+
+* Avoid strncmp() in --modversion version comparison.
+ Patch by Colin Gillespie.
+
+* Update autoconf compile flag checking macro.
+ Patch by Peter Kokot.
+
+* Add system default path configuration to Meson.
+ Patch by L. E. Segovia.
+
+* Fix order of PKG_CONFIG_LIBDIR and PKG_CONFIG_PATH element processing.
+
+Changes from 2.0.2 to 2.0.3:
+----------------------------
+
+* Fix some edge-cases with the new `--modversion` implementation
+ and add additional regression tests.
+ Patch by Colin Gillespie.
+
+* Fix some format specifiers to use PRIu64 in debug tracing.
+
+Changes from 2.0.1 to 2.0.2:
+----------------------------
+
+* Fix long-standing bug where package identifiers for "uninstalled"
+ packages incorrectly included the "-uninstalled" suffix.
+
+ This was exposed by the recent change to `--modversion` in 2.0.1.
+
+Changes from 2.0.0 to 2.0.1:
+----------------------------
+
+* The behavior of --modversion was largely reverted back to the traditional
+ pkg-config behavior, but still operates on a solved dependency graph.
+
+ The order of --modversion output is based on the dependency resolution
+ queue which is passed to the solver, which itself generally maps to the
+ order of the constrants provided on the command line.
+
+* A new flag, --verbose, has been added. When used with `--modversion`, it
+ is possible to disambiguate which version belongs to which module:
+
+ % pkgconf --modversion --verbose foo bar
+ foo: 1.2.3
+ bar: 1.3
+
+Changes from 1.9.5 to 2.0.0:
+----------------------------
+
+* When flattening the dependency graph, retain the latest seen edges
+ rather than the earliest.
+
+* Fix a long-standing bug where the dependency resolution queue was
+ evaluated in reverse. This bug masked the aforementioned dependency
+ flattening bug in many cases.
+
+* Fix handling of --with-path, which was appending paths to the search
+ list rather than prepending them as intended.
+
+* Error when --modversion is requested with more than one package, as
+ the output is ambiguous.
+
+Changes from 1.9.4 to 1.9.5:
+----------------------------
+
+* Fix incorrect assumptions involving the use of ctype(3) functions.
+ Patch by Taylor R Campbell.
+
+* Fix detection of provided functions on autoconf.
+ Patches by Harmen Stoppels.
+
+* Fix deletion of tests/meson.build by the autoconf build system.
+ Patch by h30032433.
+
+* Fix quoting rules in argvsplit.c.
+ Patch by huyubiao.
+
+* Update libpkgconf documentation and documentation building scripts.
+ Patches by Andrew Shadura.
+
+* Enforce maximum package count correctly for --modversion.
+
+Changes from 1.9.3 to 1.9.4:
+----------------------------
+
+* Fix a buffer overflow vulnerability involving very large variable expansions.
+ CVE-2023-24056
+
+* Fix a bunch of minor regressions with the solver.
+
+* Create separate solutions for `--cflags` and `--libs` when `--static` is not
+ used.
+
+* Remove final trailing whitespace in pkgconf_fragment_render_buf().
+
+* Revert broken pkg.m4 change involving querying module versions in
+ PKG_CHECK_MODULES.
+
+* Fix handling of tildes in version strings.
+
+* Various C99 formatting string fixes involving SIZE_FMT_SPECIFIER.
+
+Changes from 1.9.2 to 1.9.3:
+----------------------------
+
+* Fix a bunch of minor code issues pointed out using Clang static analyzer.
+
+* New API: pkgconf_solution_free(), which frees a compiled solution graph.
+
+* Fix behavior when overriding global variables with `--define-variable`.
+
+Changes from 1.9.1 to 1.9.2:
+----------------------------
+
+* Do not try to break dependency cycles across dependency lists. This causes
+ the solved graph to sometimes miss required dependency nodes because the
+ solver detected an incorrect dependency cycle.
+
+* New API: pkgconf_queue_solve(), which replaces pkgconf_queue_apply().
+ pkgconf_queue_apply is now deprecated and should not be used in new code.
+
+Changes from 1.9.0 to 1.9.1:
+----------------------------
+
+* Skip graph flattening and traversal for query types which only make sense
+ for a single pkg-config module.
+
+ The old solver walked these graphs with --maximum-traverse-depth=1 in
+ these cases, but this is no longer helpful because the graph is flattened
+ by the new solver.
+
+Changes from 1.8.0 to 1.9.0:
+----------------------------
+
+* pkgconf 1.9.0 is the first testing release in the pkgconf 2.0 development
+ series. While it is believed to be suitable for production, there may be
+ bugs due to the overall redesign of the solver and other initiatives.
+ Additionally, a future release of pkgconf plans will have additional ABI
+ breaks for the libpkgconf library before the pkgconf 2.0 release is cut.
+
+* There is now a new solver that is designed to provide higher performance
+ with complicated graphs, which works by flattening the dependency graph
+ into a smaller set of dependencies. This graph can then be evaluated
+ instead of the original dependency graph without having to visit every
+ edge in the graph.
+
+ NOTE: This solver, while providing significant performance improvements,
+ does so, at the cost of changed behavior for some edge cases (such as
+ circular dependencies).
+
+* Bug fixes:
+ - Resolved several memory leaks with edge cases when using libpkgconf
+ directly.
+ - pkgconf CLI now consistently frees libpkgconf resources under all
+ circumstances.
+ - SYSROOT rules are no longer applied to `-uninstalled` packages by
+ default. Use `PKG_CONFIG_PKGCONF1_SYSROOT_RULES` for legacy behavior.
+
+* A new `--license` selector has been added to the pkgconf CLI. This uses
+ SPDX expressions which can be set as the `License` field in `.pc` files.
+ See the `pc(5)` manpage for more information.
+
+* The canonical location for pkgconf maintenance going forward is
+ <https://gitea.treehouse.systems/ariadne/pkgconf>. This is presently
+ mirrored to GitHub for user convenience, but that mirroring will
+ be terminated at some point (due to GitHub Copilot).
+
+Changes from 1.7.4 to 1.8.0:
+----------------------------
+
+* This is the last planned maintenance branch. I see pkgconf as basically
+ a finished tool at this point, and very few people were ultimately interested
+ in libpkgconf. So, from here on out, it will just be bug fixes only and
+ very minor enhancements.
+
+* Bug fixes:
+ - Improved path handling on Windows to conform to what the MSYS2
+ and Cygwin teams were already modifying pkgconf to do.
+ Patches by Christoph Reiter.
+ - Fix a minor memory leak relating to cross-personalities.
+ Patch by Stone Tickle.
+ - Fix static builds for Windows on Meson.
+ Patch by Alexander Neumann.
+ - Fix some edge cases with --redefine-prefix.
+ Patch by midipix.
+ - Do not prepend sysroot_dir if the .pc file does not exist in the
+ sysroot.
+ Patch by Sandro Mani.
+ - Do not perform path filtering on default system include and library
+ path lists. This fixes consistency with other mechanisms that modify
+ these path lists.
+
+* Enhancements:
+ - Document the --validate option in the manpage.
+ Patch by orbea.
+
+Changes from 1.7.3 to 1.7.4:
+----------------------------
+
+* Bug fixes:
+ - Fix null-dereference crash when pulling a malformed 'uninstalled'
+ .pc file into a dependency tree. Patch by Tobias Stöckmann.
+ - Fix truncation of comment characters when quoted.
+ - Fix handling of .pc module names in --list-all on Windows.
+ Patch by Ryan Scott.
+ - Handle platforms where realpath(3) requires a pre-allocated buffer.
+ Patch by Fabian Groffen.
+ - Fix version whitespace warning.
+ Patch by Christoph Reiter.
+
+* Enhancements:
+ - Rewrite DOS paths on native Windows builds that don't use
+ Cygwin/MSYS.
+ - Add WantDefaultPure cross-compiler personality option.
+ - Prefer --static --pure linking on Windows.
+ - Add PKG_CONFIG_DONT_DEFINE_PREFIX environment variable.
+ Patch by Jeff Moguillansky.
+ - Many improvements when building pkgconf with Meson.
+ Patches by Christoph Reiter.
+
+Changes from 1.7.2 to 1.7.3:
+----------------------------
+
+* Bug fixes:
+ - Fix a possible out of boundary write when evaluating dependencies.
+ Patch by Tobias Stöckmann.
+ - Fix escaping logic on Windows. Patch by Vincent Torri.
+ - Fix out of boundary reads and writes with a malformed fragment.
+ Patches by Tobias Stöckmann.
+ - Fix a possible out of boundary write when evaluating tuples.
+ Patch by Tobias Stöckmann.
+
+Changes from 1.7.1 to 1.7.2:
+----------------------------
+
+* Bug fixes:
+ - Fix a windows-specific crash relating to path fixups.
+
+Changes from 1.7.0 to 1.7.1:
+----------------------------
+
+* Bug fixes:
+ - Fix a possible out of boundary access in the parser for the
+ cross-compile database. Patch by Tobias Stöckmann.
+ - Missing files for building with Meson are now included in the
+ tarball. Patch by Neal Gompa.
+ - Fix calculation of package atoms on Windows with paths that
+ use both directory separator characters.
+
+Changes from 1.6.3 to 1.7.0:
+----------------------------
+
+* Bug fixes:
+ - Fix a possible buffer overflow involving newline escaping.
+ Patch by Tobias Stöckmann.
+ - Fix an out of boundary access in the parser.
+ Patch by Tobias Stöckmann.
+ - Fix leakage of strcmp() result value in pkgconf_compare_version()
+ responses.
+ - Return the default personality if loading a cross-compile
+ personality file failed.
+ - Do not complain about newlines when validating package versions.
+ - Properly detect strndup() on Windows when building with Meson.
+
+* Enhancements:
+ - A new --shared option and WantDefaultStatic cross-compile
+ configuration option have been added. This allows for toolchains
+ to specify that static linking should be used by default.
+ - Support for the PKG_CONFIG_MSVC_SYNTAX environment variable has
+ been added. Patch by Dan Kegel.
+ - Support for the PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS
+ client flag which disables emulation of freedesktop.org pkg-config
+ fragment merging semantics has been added.
+ Patch by Karen Arutyunov.
+
+Changes from 1.6.2 to 1.6.3:
+----------------------------
+
+* Bug fixes:
+ - Properly tokenize versions. Versions cannot logically contain
+ whitespace, as dependency-lists would not properly tokenize if
+ they could. A diagnostic is generated for malformed version
+ strings containing whitespace when --validate is used.
+
+* Enhancements:
+ - CMake support has been dropped. Use Meson to build on Windows.
+
+Changes from 1.6.1 to 1.6.2:
+----------------------------
+
+* Bug fixes:
+ - Fixed a memory leak when deduplicating paths.
+ - Fixed strndup-related build regression on Windows.
+
+* Enhancements:
+ - Added pkgconf-lite variant. pkgconf-lite is a stripped down
+ variant of pkgconf that only includes pkg-config features.
+ - Added --modversion description to pkgconf(1) man page.
+
+Changes from 1.6.0 to 1.6.1:
+----------------------------
+
+* Bug fixes:
+ - Fixed an issue where a personality may not be properly selected
+ due to argv[0] containing a full path.
+ - Fixed a regression where having an empty PKG_CONFIG_LIBDIR
+ environment variable would not eliminate the default search
+ paths.
+ - Use POSIX realpath(3) instead of readlink() for deduplicating the
+ search path. Use _fullpath() on Windows for the same purpose.
+ - The dequoting logic for tuples has been improved to ensure that
+ quotes *inside* a value remain quoted when necessary.
+
+Changes from 1.5.4 to 1.6.0:
+----------------------------
+
+* Bug fixes:
+ - Fixed issue where packages which referenced missing packages in
+ Requires.private may have crashed due to memory corruption issues
+ in some circumstances.
+ - Fixed warnings reported by GCC 8 diagnostics.
+
+* Enhancements:
+ - Add LIBPKGCONF_VERSION and LIBPKGCONF_VERSION_STR macros for
+ determining libpkgconf version.
+ - Add pkgconf_fragment_copy_list() to copy a fragment list to
+ another fragment list.
+
+Changes from 1.5.3 to 1.5.4:
+----------------------------
+
+* Bug fixes:
+ - fix build on Windows with Meson
+ - fix edge cases for path canonicalization (especially on Windows)
+
+Changes from 1.5.2 to 1.5.3:
+----------------------------
+
+* Security fixes:
+ - Fix edge cases involving dequoting zero-length tuples that can lead to a
+ buffer overflow under the right circumstances. Thanks to A. Wilcox for
+ reporting and supplying a patch. (MR 3)
+
+Changes from 1.5.1 to 1.5.2:
+----------------------------
+
+* Bug fixes:
+ - Ensure environment variables override values learned from personality files
+ or built-in defaults.
+
+* Documentation enhancements:
+ - Add pkgconf-personality(5) manpage documenting the personality file format.
+
+Changes from 1.5.0 to 1.5.1:
+----------------------------
+
+* Bug fixes:
+ - fixed a crash with some invalid multi-line .pc files
+
+Changes from 1.4.2 to 1.5.0:
+---------------------------
+
+* Administrative:
+ - The git repository has moved to <https://git.dereferenced.org/pkgconf/pkgconf>,
+ due to the acquisition of GitHub by Microsoft.
+
+* Overall enhancements:
+ - pkgconf now supports the proposed Requires.internal pkg-config extension,
+ by merging it with the Requires.private list (there is no functional difference
+ between the two in our resolver implementation)
+ - Support for cross-compilation personalities have been added. To make use of this
+ functionality, create a file in the new personality.d directory that sits inside
+ the pkgconfig directory. The personality file format is described in
+ pc-personality(5). (github #166)
+ - Support for Haiku has been added, including interpretation of BELIBRARIES and
+ other toolchain specifics. (github #180)
+ - Testsuite support can be disabled when building with Meson. (github #175)
+
+* Bug fixes:
+ - tuples are now appropriately dequoted when added by the parser (github #186).
+
+* Various Windows enhancements:
+ - CMake supports building with GCC on Windows. (github #179)
+ - Prefix rewriting has been improved. (github #177)
+ - PKGCONF_API support has been implemented when building with Meson,
+ allowing Meson to be used to build pkgconf on Windows. (github #174)
+
+* Documentation fixes:
+ - The manpages have been linted and fixed. (github #181, #182, #183)
+ - The description of pkgconf --exists has been corrected. (github #173)
+
+Changes from 1.4.1 to 1.4.2:
+----------------------------
+
+* Bug fixes:
+ - ensure pkgconf_dependency_t nodes have a solution marked when satisfied
+ by an indirect provider (github #172)
+
+Changes from 1.4.0 to 1.4.1:
+----------------------------
+
+* Bug fixes:
+ - revert some quoting changes because they don't work well with certain
+ GCC edge cases (github #168)
+
+* Enhancements:
+ - add limited support for --cflags with --msvc-syntax
+
+Changes from 1.3.7 to 1.4.0:
+----------------------------
+
+* Notable libpkgconf API changes:
+ - pkgconf_pkg_t.requires has been renamed to pkgconf_pkg_t.required for
+ C++20 compatibility.
+
+* Enhancements:
+ - pkgconf and libpkgconf has been ported to Windows as native binaries.
+ - improved compatibility with freedesktop.org pkg-config's ${pc_sysrootdir}
+ usage pattern.
+ - do not mention PKG_CONFIG_SKIP_CONFLICTS environmental variable when
+ simplified errors are requested, as with PKG_CONFIG_PATH.
+ - the dependency solver now stores solutions to dependency graph elements
+ it visits, allowing for the dependency graph to be incrementally solved.
+ this improves dependency solving time by an order of magnitude in most
+ cases.
+ - new --env option allows for exporting cflags/libs fragments as export
+ variables
+ - new support for building pkgconf with CMake and Meson
+ - improved compiler warning flag detection on autoconf and CMake
+ - removed PKGCONF_BUFSIZE allocations from the stack where possible
+ - allow for customizing the way fragment lists are rendered using a callback API
+ - new support for --msvc-syntax output using the new fragment rendering callbacks
+ - fragments are now quoted according to POSIX literal rules
+ - new variables on the pkg-config builtin:
+ - ${pc_system_includedirs}: the system includedir search path known by pkgconf
+ - ${pc_system_libdirs}: the system libdir search path known by pkgconf
+ - new manpages:
+ - pc(5) describing pkgconf's interpretation of pkg-config .pc files
+ - pkg.m4(7) describing the autotools macros bundled with pkgconf
+
+* Bug fixes:
+ - fix pkgconf_pkg_t.id generation on native Windows where either \ or / are usable
+ as path separator.
+ - add missing --modversion to --help output
+ - do not evaluate module paths for modules that are not actually on disk
+ - ensure we work on a zeroed buffer prior to calling realpath(2) with it
+ - fix path deduplication edge case when cache-inodes feature is unavailable
+ - fix path rewriting regression with PKG_CONFIG_SYSROOT_DIR when
+ PKG_CONFIG_SYSROOT_DIR is set to /
+ - fix crash in edge case where a .pc file has misquoting in a fragment list.
+ - fix logic edge case when comparing relocated paths
+
+Changes from 1.3.6 to 1.3.7:
+----------------------------
+
+* Enhancements:
+ - improved diagnostics for malformed packages.
+
+* Bug fixes:
+ - reject packages which contain incomplete metadata in post-parse phase.
+
+Changes from 1.3.5 to 1.3.6:
+----------------------------
+
+* Enhancements:
+ - add many cflags to the protected set: -Wa, -Wl, -Wp, -ansi, -std=, -stdlib=,
+ -pedantic, -pthread, -trigraphs, -nostdinc, -nostdlibinc, -nobuiltininc.
+
+* Bug fixes:
+ - handle -include cflag fragments properly.
+
+Changes from 1.3.4 to 1.3.5:
+----------------------------
+
+* Bug fixes:
+ - fix --variable output for compatibility some broken configure scripts when they
+ request the same variable from multiple packages
+
+Changes from 1.3.3 to 1.3.4:
+----------------------------
+
+* Bug fixes:
+ - fix a quoting issue exposed by the Go testsuite
+
+Changes from 1.3.2 to 1.3.3:
+----------------------------
+
+* Bug fixes:
+ - back out disabling the dependency resolver for single-package queries, it caused
+ too many regressions.
+
+* Enhancements:
+ - allow explicitly disabling the dependency resolver via new environment variable,
+ PKG_CONFIG_MINIMUM_TRAVERSE_DEPTH=1. while pkgconf could already do this using
+ --minimum-traverse-depth=1, other pkg-config implementations do not have this
+ option, so adding an environment variable allows to make better use of this
+ feature (other implementations won't error due to unknown option this way)
+
+Changes from 1.3.1 to 1.3.2:
+----------------------------
+
+* Bug fixes:
+ - rewrite handling of --modversion, --print-variables and --variable to not require
+ the dependency resolver
+ - ensure we disable the dependency resolver in all cases where it is a single-package
+ query (1.3.1 did not go far enough)
+
+Changes from 1.3.0 to 1.3.1:
+----------------------------
+
+* Features:
+ - implement --short-errors
+
+* Bug fixes:
+ - only consider a single package at a time with --print-requires, --print-requires-private,
+ --print-provides, --modversion, --print-variable and --print-variables
+
+* Enhancements:
+ - synchronized latest freedesktop.org changes to pkg.m4
+ - improve error reporting with legacy --atleast-version and similar flags.
+
+Changes from 1.2.0 to 1.3.0:
+----------------------------
+
+* Features:
+ - pkgconf --debug now provides a facility for tracing most relevant libpkgconf operations
+ - libpkgconf: add warn and trace handlers for warnings
+ - replace realpath() with faster, lighter weight path normalization function (github #112)
+ - pkgconf CLI now emulates pkg-config quoting rules precisely, while allowing direct access
+ to the actual fragments via libpkgconf
+
+* Bug fixes:
+ - pkg: properly separate static and virtual packages so they are not inappropriately
+ optimized out of the dependency graph (github #108)
+ - argvsplit: do not consider ' and " to have similar rules to escape sequences (github #111)
+ - pkg: strip trailing whitespace when parsing .pc files
+
+* Enhancements:
+ - argvsplit basically rewritten from scratch
+ - many code fixes spotted by coverity
+ - add PKG_CONFIG_DONT_RELOCATE_PATHS and --dont-relocate-paths environment variables to
+ disable path relocation feature if needed
+ - remove extra whitespace that was present for compatibility with older pkg-config releases
+ (github #113)
+
+Changes from 1.1.0 to 1.2.0:
+----------------------------
+
+* Features:
+ - new --path option lists the .pc files which provided the requested dependencies
+ - new path relocation API: pkgconf_path_relocate(), which wraps functions such as
+ realpath() and cygwin_conv_path().
+ - new --with-path option adds a path to the search list
+ - new --define-prefix and --dont-define-prefix features enable automatic prefix
+ detection for relocatable SDKs. this is mostly useful on windows.
+
+* Bug fixes:
+ - fragments: fix even more edge cases involving token concatenation
+ - path lists: don't attempt to collect path inodes if the filter is disabled
+ - path lists: explicitly avoid uninitialised data for the path inode cache
+ - client: properly handle --keep-system-cflags and --keep-system-libs
+
+* Enhancements:
+ - windows: build libpkgconf as a DLL
+ - fragments: only munge fragments if sysroot_dir is actually set
+ - overall API: resolver flags have been moved to being a client-object setting
+ instead of used for every function invocation
+
+Changes from 1.0.1 to 1.1.0:
+----------------------------
+
+* Features:
+ - new Provides system allows alternate .pc files to provide a dependency
+ - stable library API (with documentation): http://pkgconf.readthedocs.io/
+
+* Enhancements:
+ - make it possible to programmatically declare dependencies instead of just using the parser
+ - testsuite migrated to run under kyua
+ - provide a libpkgconf.pc file for consumers to use
+ - pkgconf client: new --pure flag to enable dependency graph optimization in --static mode
+ - significant .pc parser speedups using bsearch(3).
+ - handle -idirafter in the same way as -isystem CFLAGS
+ - learn toolchain "system" paths from GCC environment variables, if present
+ - filter duplicate PKG_CONFIG_PATH (and other) entries by inode
+
+* Bug fixes:
+ - fragments: fix another edge case involving empty tokens being concatenated onto previous tokens
+ (github #99)
+ - libpkgconf: remove dependencies on config.h in public headers
+
+Changes from 1.0.0 to 1.0.1:
+----------------------------
+
+* Enhancements:
+ - new stub implementation of --print-provides (github #95)
+
+* Bug fixes:
+ - fragments: fix an edge case involving path-only fragments and PKG_CONFIG_SYSROOT_DIR (github #94)
+
+Changes from 0.9.12 to 1.0.0:
+-----------------------------
+
+* Features:
+ - new library: libpkgconf
+
+* Enhancements:
+ - testsuite: use an explicit prefix on all tests
+ - build: switch to automake
+
+* Bug fixes:
+ - cast all usage of ctype(3) functions
+ - do not expand variables passed via --define-variable for compatibility with pkg-config 0.29
+ - let the CFLAGS being user settable
+
+Changes from 0.9.11 to 0.9.12:
+------------------------------
+
+* Features:
+ - add --list-package-names
+
+* Enhancements:
+ - ensure -I and -L are never pushed back
+
+* Bug fixes:
+ - fix implicit conversion warnings with variables over 31bits
+
+Changes from 0.9.10 to 0.9.11:
+------------------------------
+
+* Features:
+ - add --validate
+
+* Enhancements:
+ - add large file support checks in autoconf
+
+* Bug fixes:
+ - fix private lib deduplication
+ - handle --static correctly in some more esoteric scenarios
+
+Changes from 0.9.9 to 0.9.10:
+-----------------------------
+
+* Features:
+
+* Enhancements:
+
+* Bug fixes:
+ - Fix parser when dealing with commented lines
+
+Changes from 0.9.8 to 0.9.9:
+----------------------------
+
+* Features:
+ - add a sub out --print-provides
+
+* Enhancements:
+
+* Bug fixes:
+ - Fix parser when dealing with comments in fields
+
+Changes from 0.9.7 to 0.9.8:
+----------------------------
+
+* Features:
+
+* Enhancements:
+ - Convert manpages to mdoc(7)
+
+* Bug fixes:
+ - Fix parsing multiline fields
+
+Changes from 0.9.6 to 0.9.7:
+----------------------------
+
+* Features:
+
+* Enhancements:
+ - Convert manpages to mdoc(7)
+
+* Bug fixes:
+ - Fix parsing multiline fields
+
+Changes from 0.9.5 to 0.9.6:
+----------------------------
+
+* Features:
+ - add a sub --debug
+
+* Enhancements:
+ - Do not hardcode non-posix install(1)
+
+* Bug fixes:
+ - fix --with-system-includedir and --with-system-libdir behaviour
+
+Changes from 0.9.4 to 0.9.5:
+----------------------------
+
+* Features:
+
+* Enhancements:
+ - Make all variables but CFLAGS and LIBS case sensitive
+
+* Bug fixes:
+
+Changes from 0.9.3 to 0.9.4:
+----------------------------
+
+* Features:
+ - Add a pkgconf(1) manpage
+
+* Enhancements:
+ - Improve support for MacOS -framework
+
+* Bug fixes:
+
+Changes from 0.9.2 to 0.9.3:
+----------------------------
+
+* Features:
+ - Add support for CFLAGS.private
+
+* Enhancements:
+ - Support out of source build
+ - Improved private libs deduplication
+
+* Bug fixes:
+
+Changes from 0.9.1 to 0.9.2:
+----------------------------
+
+* Features:
+
+* Enhancements:
+
+* Bug fixes:
+ - Fix PKG_CONFIG_PATH being ignored when a .pc is directly supplied from
+ command line
+
+Changes from 0.9.0 to 0.9.1:
+----------------------------
+
+* Features:
+
+* Enhancements:
+ - --simulate: print depgraph operations
+ - --simulate: print bytecode program as a human-readable AST
+
+* Bug fixes:
+ - reset parser state on new package atom
+
+Changes from 0.8.12 to 0.9.0:
+----------------------------
+
+* Features:
+
+* Enhancements:
+ - Rework the internal cache API
+ - Rework the internal code to use the new pkg_list_t framework
+ - Rework PKG_CONFIG_PATH handling code
+
+* Bug fixes:
+ - fix multi-recursion with -framework
diff --git a/README.md b/README.md
new file mode 100644
index 000000000000..257d686a62b6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,130 @@
+# pkgconf [![test](https://github.com/pkgconf/pkgconf/actions/workflows/test.yml/badge.svg)](https://github.com/pkgconf/pkgconf/actions/workflows/test.yml)
+
+`pkgconf` is a program which helps to configure compiler and linker flags for
+development libraries. It is a superset of the functionality provided by
+pkg-config from freedesktop.org, but does not provide bug-compatibility with
+the original pkg-config.
+
+`libpkgconf` is a library which provides access to most of `pkgconf`'s functionality,
+to allow other tooling such as compilers and IDEs to discover and use libraries
+configured by pkgconf.
+
+## release tarballs
+
+Release tarballs are available on [distfiles.ariadne.space][distfiles].
+
+ [distfiles]: https://distfiles.ariadne.space/pkgconf/
+
+## build system setup
+
+If you would like to use the git sources directly, or a snapshot of the
+sources from GitHub, you will need to regenerate the autotools build
+system artifacts yourself, or use Meson instead. For example, on Alpine:
+
+ $ apk add autoconf automake libtool build-base
+ $ sh ./autogen.sh
+
+## pkgconf-lite
+
+If you only need the original pkg-config functionality, there is also pkgconf-lite,
+which builds the `pkgconf` frontend and relevant portions of `libpkgconf` functionality
+into a single binary:
+
+ $ make -f Makefile.lite
+
+## why `pkgconf` over original `pkg-config`?
+
+pkgconf builds a flattened directed dependency graph, which allows for more insight
+into relationships between dependencies, allowing for some link-time dependency
+optimization, which allows for the user to more conservatively link their binaries,
+which may be helpful in some environments, such as when prelink(1) is being used.
+
+The solver is also optimized to handle large dependency graphs with hundreds of
+thousands of edges, which can be seen in any project using the Abseil frameworks
+for example.
+
+In addition, pkgconf has full support for virtual packages, while the original
+pkg-config does not, as well as fully supporting `Conflicts` at dependency
+resolution time, which is more efficient than checking for `Conflicts` while
+walking the dependency graph.
+
+## linker flags optimization
+
+pkgconf, when used effectively, can make optimizations to avoid overlinking binaries.
+
+This functionality depends on the pkg-config module properly declaring its dependency
+tree instead of using `Libs` and `Cflags` fields to directly link against other modules
+which have pkg-config metadata files installed.
+
+The practice of using `Libs` and `Cflags` to describe unrelated dependencies is
+not recommended in [Dan Nicholson's pkg-config tutorial][fd-tut] for this reason.
+
+ [fd-tut]: http://people.freedesktop.org/~dbn/pkg-config-guide.html
+
+## bug compatibility with original pkg-config
+
+In general, we do not provide bug-level compatibility with pkg-config.
+
+What that means is, if you feel that there is a legitimate regression versus pkg-config,
+do let us know, but also make sure that the .pc files are valid and follow the rules of
+the [pkg-config tutorial][fd-tut], as most likely fixing them to follow the specified
+rules will solve the problem.
+
+## debug output
+
+Please use only the stable interfaces to query pkg-config. Do not screen-scrape the
+output from `--debug`: this is sent to `stderr` for a reason, it is not intended to be
+scraped. The `--debug` output is **not** a stable interface, and should **never** be
+depended on as a source of information. If you need a stable interface to query pkg-config
+which is not covered, please get in touch.
+
+## compiling `pkgconf` and `libpkgconf` on UNIX
+
+pkgconf is basically compiled the same way any other autotools-based project is
+compiled:
+
+ $ ./configure
+ $ make
+ $ sudo make install
+
+If you are installing pkgconf into a custom prefix, such as `/opt/pkgconf`, you will
+likely want to define the default system includedir and libdir for your toolchain.
+To do this, use the `--with-system-includedir` and `--with-system-libdir` configure
+flags like so:
+
+ $ ./configure \
+ --prefix=/opt/pkgconf \
+ --with-system-libdir=/lib:/usr/lib \
+ --with-system-includedir=/usr/include
+ $ make
+ $ sudo make install
+
+## compiling `pkgconf` and `libpkgconf` with Meson (usually for Windows)
+
+pkgconf is compiled using [Meson](https://mesonbuild.com) on Windows. In theory, you could also use
+Meson to build on UNIX, but this is not recommended at this time as pkgconf is typically built
+much earlier than Meson.
+
+ $ meson setup build -Dtests=disabled
+ $ meson compile -C build
+ $ meson install -C build
+
+There are a few defines such as `SYSTEM_LIBDIR`, `PKGCONFIGDIR` and `SYSTEM_INCLUDEDIR`.
+However, on Windows, the default `PKGCONFIGDIR` value is usually overridden at runtime based
+on path relocation.
+
+## pkg-config symlink
+
+If you want pkgconf to be used when you invoke `pkg-config`, you should install a
+symlink for this. We do not do this for you, as we believe it is better for vendors
+to make this determination themselves.
+
+ $ ln -sf pkgconf /usr/bin/pkg-config
+
+## contacts
+
+You can report bugs at <https://github.com/pkgconf/pkgconf/issues>.
+
+There is a mailing list at <https://lists.sr.ht/~kaniini/pkgconf>.
+
+You can contact us via IRC at `#pkgconf` at `irc.oftc.net`.
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 000000000000..92bd5b678973
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,87 @@
+#! /bin/sh
+
+TOP_DIR=$(dirname $0)
+LAST_DIR=$PWD
+
+if test ! -f $TOP_DIR/configure.ac ; then
+ echo "You must execute this script from the top level directory."
+ exit 1
+fi
+
+AUTOCONF=${AUTOCONF:-autoconf}
+ACLOCAL=${ACLOCAL:-aclocal}
+AUTOHEADER=${AUTOHEADER:-autoheader}
+AUTOMAKE=${AUTOMAKE:-automake}
+LIBTOOLIZE=${LIBTOOLIZE:-libtoolize}
+
+dump_help_screen ()
+{
+ echo "Usage: $0 [options]"
+ echo
+ echo "options:"
+ echo " -n skip CVS changelog creation"
+ echo " -h,--help show this help screen"
+ echo
+ exit 0
+}
+
+parse_options ()
+{
+ while test "$1" != "" ; do
+ case $1 in
+ -h|--help)
+ dump_help_screen
+ ;;
+ -n)
+ SKIP_CVS_CHANGELOG=yes
+ ;;
+ *)
+ echo Invalid argument - $1
+ dump_help_screen
+ ;;
+ esac
+ shift
+ done
+}
+
+run_or_die ()
+{
+ COMMAND=$1
+
+ # check for empty commands
+ if test -z "$COMMAND" ; then
+ echo "*warning* no command specified"
+ return 1
+ fi
+
+ shift;
+
+ OPTIONS="$@"
+
+ # print a message
+ echo -n "*info* running $COMMAND"
+ if test -n "$OPTIONS" ; then
+ echo " ($OPTIONS)"
+ else
+ echo
+ fi
+
+ # run or die
+ $COMMAND $OPTIONS ; RESULT=$?
+ if test $RESULT -ne 0 ; then
+ echo "*error* $COMMAND failed. (exit code = $RESULT)"
+ exit 1
+ fi
+
+ return 0
+}
+
+parse_options "$@"
+
+cd $TOP_DIR
+
+run_or_die $ACLOCAL
+run_or_die $AUTOHEADER
+run_or_die $AUTOCONF
+run_or_die $LIBTOOLIZE --install
+run_or_die $AUTOMAKE --add-missing
diff --git a/cli/bomtool/main.c b/cli/bomtool/main.c
new file mode 100644
index 000000000000..1e8391aa5ae9
--- /dev/null
+++ b/cli/bomtool/main.c
@@ -0,0 +1,368 @@
+/*
+ * bomtool/main.c
+ * main() routine, printer functions
+ *
+ * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
+ * pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include "libpkgconf/config.h"
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+#include "getopt_long.h"
+
+#define PKG_VERSION (((uint64_t) 1) << 1)
+#define PKG_ABOUT (((uint64_t) 1) << 2)
+#define PKG_HELP (((uint64_t) 1) << 3)
+
+static const char *spdx_version = "SPDX-2.2";
+static const char *bom_license = "CC0-1.0";
+static const char *document_ref = "SPDXRef-DOCUMENT";
+
+static pkgconf_client_t pkg_client;
+static uint64_t want_flags;
+static size_t maximum_package_count = 0;
+static int maximum_traverse_depth = 2000;
+FILE *error_msgout = NULL;
+
+static bool
+error_handler(const char *msg, const pkgconf_client_t *client, void *data)
+{
+ (void) client;
+ (void) data;
+ fprintf(error_msgout, "%s", msg);
+ return true;
+}
+
+static const char *
+sbom_spdx_identity(pkgconf_pkg_t *pkg)
+{
+ static char buf[PKGCONF_ITEM_SIZE];
+
+ snprintf(buf, sizeof buf, "%sC64%s", pkg->id, pkg->version);
+
+ return buf;
+}
+
+static const char *
+sbom_name(pkgconf_pkg_t *world)
+{
+ static char buf[PKGCONF_BUFSIZE];
+ pkgconf_node_t *node;
+
+ pkgconf_strlcpy(buf, "SBOM-SPDX", sizeof buf);
+
+ PKGCONF_FOREACH_LIST_ENTRY(world->required.head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+ pkgconf_pkg_t *match = dep->match;
+
+ if ((dep->flags & PKGCONF_PKG_DEPF_QUERY) != PKGCONF_PKG_DEPF_QUERY)
+ continue;
+
+ if (!dep->match)
+ continue;
+
+ pkgconf_strlcat(buf, "-", sizeof buf);
+ pkgconf_strlcat(buf, sbom_spdx_identity(match), sizeof buf);
+ }
+
+ return buf;
+}
+
+static void
+write_sbom_header(pkgconf_client_t *client, pkgconf_pkg_t *world)
+{
+ (void) client;
+ (void) world;
+
+ printf("SPDXVersion: %s\n", spdx_version);
+ printf("DataLicense: %s\n", bom_license);
+ printf("SPDXID: %s\n", document_ref);
+ printf("DocumentName: %s\n", sbom_name(world));
+ printf("DocumentNamespace: https://spdx.org/spdxdocs/bomtool-%s\n", PACKAGE_VERSION);
+ printf("Creator: Tool: bomtool %s\n", PACKAGE_VERSION);
+
+ printf("\n\n");
+}
+
+static const char *
+sbom_identity(pkgconf_pkg_t *pkg)
+{
+ static char buf[PKGCONF_ITEM_SIZE];
+
+ snprintf(buf, sizeof buf, "%s@%s", pkg->id, pkg->version);
+
+ return buf;
+}
+
+static void
+write_sbom_package(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *unused)
+{
+ (void) client;
+ (void) unused;
+
+ if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
+ return;
+
+ printf("##### Package: %s\n\n", sbom_identity(pkg));
+
+ printf("PackageName: %s\n", sbom_identity(pkg));
+ printf("SPDXID: SPDXRef-Package-%s\n", sbom_spdx_identity(pkg));
+ printf("PackageVersion: %s\n", pkg->version);
+ printf("PackageDownloadLocation: NOASSERTION\n");
+ printf("PackageVerificationCode: NOASSERTION\n");
+
+ /* XXX: What about projects? */
+ if (pkg->maintainer != NULL)
+ printf("PackageSupplier: Person: %s\n", pkg->maintainer);
+
+ if (pkg->url != NULL)
+ printf("PackageHomePage: %s\n", pkg->url);
+
+ printf("PackageLicenseDeclared: %s\n", pkg->license != NULL ? pkg->license : "NOASSERTION");
+
+ if (pkg->copyright != NULL)
+ printf("PackageCopyrightText: <text>%s</text>\n", pkg->copyright);
+
+ if (pkg->description != NULL)
+ printf("PackageSummary: <text>%s</text>\n", pkg->description);
+
+ printf("\n\n");
+}
+
+static void
+write_sbom_relationships(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *unused)
+{
+ (void) client;
+ (void) unused;
+
+ char baseref[PKGCONF_ITEM_SIZE];
+ pkgconf_node_t *node;
+
+ if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
+ return;
+
+ snprintf(baseref, sizeof baseref, "SPDXRef-Package-%sC64%s", pkg->id, pkg->version);
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+ pkgconf_pkg_t *match = dep->match;
+
+ if (!dep->match)
+ continue;
+
+ printf("Relationship: %s DEPENDS_ON SPDXRef-Package-%s\n", baseref, sbom_spdx_identity(match));
+ printf("Relationship: SPDXRef-Package-%s DEPENDENCY_OF %s\n", sbom_spdx_identity(match), baseref);
+ }
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+ pkgconf_pkg_t *match = dep->match;
+
+ if (!dep->match)
+ continue;
+
+ printf("Relationship: %s DEPENDS_ON SPDXRef-Package-%s\n", baseref, sbom_spdx_identity(match));
+ printf("Relationship: SPDXRef-Package-%s DEV_DEPENDENCY_OF %s\n", sbom_spdx_identity(match), baseref);
+ }
+
+ if (pkg->required.head != NULL || pkg->requires_private.head != NULL)
+ printf("\n\n");
+}
+
+static bool
+generate_sbom_from_world(pkgconf_client_t *client, pkgconf_pkg_t *world)
+{
+ int eflag;
+ pkgconf_node_t *node;
+
+ write_sbom_header(client, world);
+
+ eflag = pkgconf_pkg_traverse(client, world, write_sbom_package, NULL, maximum_traverse_depth, 0);
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ return false;
+
+ eflag = pkgconf_pkg_traverse(client, world, write_sbom_relationships, NULL, maximum_traverse_depth, 0);
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ return false;
+
+ PKGCONF_FOREACH_LIST_ENTRY(world->required.head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+ pkgconf_pkg_t *match = dep->match;
+
+ if (!dep->match)
+ continue;
+
+ printf("Relationship: %s DESCRIBES SPDXRef-Package-%s\n", document_ref, sbom_spdx_identity(match));
+ }
+
+ return true;
+}
+
+static int
+version(void)
+{
+ printf("bomtool %s\n", PACKAGE_VERSION);
+ return EXIT_SUCCESS;
+}
+
+static int
+about(void)
+{
+ printf("bomtool (%s %s)\n", PACKAGE_NAME, PACKAGE_VERSION);
+ printf("Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021\n");
+ printf(" pkgconf authors (see AUTHORS in documentation directory).\n\n");
+ printf("Permission to use, copy, modify, and/or distribute this software for any\n");
+ printf("purpose with or without fee is hereby granted, provided that the above\n");
+ printf("copyright notice and this permission notice appear in all copies.\n\n");
+ printf("This software is provided 'as is' and without any warranty, express or\n");
+ printf("implied. In no event shall the authors be liable for any damages arising\n");
+ printf("from the use of this software.\n\n");
+ printf("Report bugs at <%s>.\n", PACKAGE_BUGREPORT);
+ return EXIT_SUCCESS;
+}
+
+static int
+usage(void)
+{
+ printf("usage: bomtool [--flags] [modules]\n");
+
+ printf("\nbasic options:\n\n");
+
+ printf(" --help this message\n");
+ printf(" --about print bomtool version and license to stdout\n");
+ printf(" --version print bomtool version to stdout\n");
+
+ return EXIT_SUCCESS;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ret = EXIT_SUCCESS;
+ pkgconf_list_t pkgq = PKGCONF_LIST_INITIALIZER;
+ unsigned int want_client_flags = PKGCONF_PKG_PKGF_SEARCH_PRIVATE;
+ pkgconf_cross_personality_t *personality = pkgconf_cross_personality_default();
+ pkgconf_pkg_t world = {
+ .id = "virtual:world",
+ .realname = "virtual world package",
+ .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL,
+ };
+
+ error_msgout = stderr;
+
+ struct pkg_option options[] = {
+ { "version", no_argument, &want_flags, PKG_VERSION, },
+ { "about", no_argument, &want_flags, PKG_ABOUT, },
+ { "help", no_argument, &want_flags, PKG_HELP, },
+ { NULL, 0, NULL, 0 }
+ };
+
+ while ((ret = pkg_getopt_long_only(argc, argv, "", options, NULL)) != -1)
+ {
+ switch (ret)
+ {
+ case '?':
+ case ':':
+ return EXIT_FAILURE;
+ default:
+ break;
+ }
+ }
+
+ pkgconf_client_init(&pkg_client, error_handler, NULL, personality);
+
+ /* we have determined what features we want most likely. in some cases, we override later. */
+ pkgconf_client_set_flags(&pkg_client, want_client_flags);
+
+ /* at this point, want_client_flags should be set, so build the dir list */
+ pkgconf_client_dir_list_build(&pkg_client, personality);
+
+ if ((want_flags & PKG_ABOUT) == PKG_ABOUT)
+ return about();
+
+ if ((want_flags & PKG_VERSION) == PKG_VERSION)
+ return version();
+
+ if ((want_flags & PKG_HELP) == PKG_HELP)
+ return usage();
+
+ while (1)
+ {
+ const char *package = argv[pkg_optind];
+
+ if (package == NULL)
+ break;
+
+ /* check if there is a limit to the number of packages allowed to be included, if so and we have hit
+ * the limit, stop adding packages to the queue.
+ */
+ if (maximum_package_count > 0 && pkgq.length > maximum_package_count)
+ break;
+
+ while (isspace((unsigned char)package[0]))
+ package++;
+
+ /* skip empty packages */
+ if (package[0] == '\0') {
+ pkg_optind++;
+ continue;
+ }
+
+ if (argv[pkg_optind + 1] == NULL || !PKGCONF_IS_OPERATOR_CHAR(*(argv[pkg_optind + 1])))
+ {
+ pkgconf_queue_push(&pkgq, package);
+ pkg_optind++;
+ }
+ else
+ {
+ char packagebuf[PKGCONF_BUFSIZE];
+
+ snprintf(packagebuf, sizeof packagebuf, "%s %s %s", package, argv[pkg_optind + 1], argv[pkg_optind + 2]);
+ pkg_optind += 3;
+
+ pkgconf_queue_push(&pkgq, packagebuf);
+ }
+ }
+
+ if (pkgq.head == NULL)
+ {
+ fprintf(stderr, "Please specify at least one package name on the command line.\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ ret = EXIT_SUCCESS;
+
+ if (!pkgconf_queue_solve(&pkg_client, &pkgq, &world, maximum_traverse_depth))
+ {
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ if (!generate_sbom_from_world(&pkg_client, &world))
+ {
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+out:
+ pkgconf_solution_free(&pkg_client, &world);
+ pkgconf_queue_free(&pkgq);
+ pkgconf_cross_personality_deinit(personality);
+ pkgconf_client_deinit(&pkg_client);
+
+ return ret;
+}
diff --git a/cli/getopt_long.c b/cli/getopt_long.c
new file mode 100644
index 000000000000..c47215fae4cf
--- /dev/null
+++ b/cli/getopt_long.c
@@ -0,0 +1,643 @@
+/* $OpenBSD: getopt_long.c,v 1.21 2006/09/22 17:22:05 millert Exp $ */
+/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
+
+/*
+ * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "getopt_long.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#define PKGCONF_HACK_LOGICAL_OR_ALL_VALUES
+
+int pkg_opterr = 1; /* if error message should be printed */
+int pkg_optind = 1; /* index into parent argv vector */
+int pkg_optopt = '?'; /* character checked for validity */
+int pkg_optreset; /* reset getopt */
+char *pkg_optarg; /* argument associated with option */
+
+#define PRINT_ERROR ((pkg_opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
+#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
+#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
+
+/* return values */
+#define BADCH (int)'?'
+#define BADARG ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+/* add some padding to EMSG to avoid overrun */
+#define EMSG "\0\0\0\0"
+
+#ifdef GNU_COMPATIBLE
+#define NO_PREFIX (-1)
+#define D_PREFIX 0
+#define DD_PREFIX 1
+#define W_PREFIX 2
+#endif
+
+static int getopt_internal(int, char * const *, const char *,
+ const struct pkg_option *, int *, int);
+static int parse_long_options(char * const *, const char *,
+ const struct pkg_option *, int *, int, int);
+static int gcd(int, int);
+static void permute_args(int, int, int, char * const *);
+
+static char *place = EMSG; /* option letter processing */
+
+/* XXX: set pkg_optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1; /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
+#ifdef GNU_COMPATIBLE
+static int dash_prefix = NO_PREFIX;
+static const char gnuoptchar[] = "invalid option -- %c";
+
+static const char recargstring[] = "option `%s%s' requires an argument";
+static const char ambig[] = "option `%s%.*s' is ambiguous";
+static const char noarg[] = "option `%s%.*s' doesn't allow an argument";
+static const char illoptstring[] = "unrecognized option `%s%s'";
+#else
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptstring[] = "unknown option -- %s";
+#endif
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(int a, int b)
+{
+ int c;
+
+ c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+
+ return (b);
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(int panonopt_start, int panonopt_end, int opt_end,
+ char * const *nargv)
+{
+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+ char *swap;
+
+ /*
+ * compute lengths of blocks and number and size of cycles
+ */
+ nnonopts = panonopt_end - panonopt_start;
+ nopts = opt_end - panonopt_end;
+ ncycle = gcd(nnonopts, nopts);
+ cyclelen = (opt_end - panonopt_start) / ncycle;
+
+ for (i = 0; i < ncycle; i++) {
+ cstart = panonopt_end+i;
+ pos = cstart;
+ for (j = 0; j < cyclelen; j++) {
+ if (pos >= panonopt_end)
+ pos -= nnonopts;
+ else
+ pos += nopts;
+ swap = nargv[pos];
+ /* LINTED const cast */
+ ((char **) nargv)[pos] = nargv[cstart];
+ /* LINTED const cast */
+ ((char **)nargv)[cstart] = swap;
+ }
+ }
+}
+
+/*
+ * parse_long_options --
+ * Parse long options in argc/argv argument vector.
+ * Returns -1 if short_too is set and the option does not match long_options.
+ */
+static int
+parse_long_options(char * const *nargv, const char *options,
+ const struct pkg_option *long_options, int *idx, int short_too, int flags)
+{
+ char *current_argv, *has_equal;
+#ifdef GNU_COMPATIBLE
+ char *current_dash;
+#endif
+ size_t current_argv_len;
+ int i, match, exact_match, second_partial_match;
+
+ current_argv = place;
+#ifdef GNU_COMPATIBLE
+ switch (dash_prefix) {
+ case D_PREFIX:
+ current_dash = "-";
+ break;
+ case DD_PREFIX:
+ current_dash = "--";
+ break;
+ case W_PREFIX:
+ current_dash = "-W ";
+ break;
+ default:
+ current_dash = "";
+ break;
+ }
+#endif
+ match = -1;
+ exact_match = 0;
+ second_partial_match = 0;
+
+ pkg_optind++;
+
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ /* argument found (--option=arg) */
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ /* find matching long option */
+ if (strncmp(current_argv, long_options[i].name,
+ current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == current_argv_len) {
+ /* exact match */
+ match = i;
+ exact_match = 1;
+ break;
+ }
+ /*
+ * If this is a known short option, don't allow
+ * a partial match of a single character.
+ */
+ if (short_too && current_argv_len == 1)
+ continue;
+
+ if (match == -1) /* first partial match */
+ match = i;
+ else if ((flags & FLAG_LONGONLY) ||
+ long_options[i].has_arg !=
+ long_options[match].has_arg ||
+ long_options[i].flag != long_options[match].flag ||
+ long_options[i].val != long_options[match].val)
+ second_partial_match = 1;
+ }
+ if (!exact_match && second_partial_match) {
+ /* ambiguous abbreviation */
+ if (PRINT_ERROR) {
+ fprintf(stderr, "pkgconf: ");
+ fprintf(stderr, ambig,
+#ifdef GNU_COMPATIBLE
+ current_dash,
+#endif
+ (int)current_argv_len,
+ current_argv);
+ fprintf(stderr, "\n");
+ }
+ pkg_optopt = 0;
+ return (BADCH);
+ }
+ if (match != -1) { /* option found */
+ if (long_options[match].has_arg == no_argument
+ && has_equal) {
+ if (PRINT_ERROR) {
+ fprintf(stderr, "pkgconf: ");
+ fprintf(stderr, noarg,
+#ifdef GNU_COMPATIBLE
+ current_dash,
+#endif
+ (int)current_argv_len,
+ current_argv);
+ fprintf(stderr, "\n");
+ }
+ /*
+ * XXX: GNU sets pkg_optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ pkg_optopt = (int)long_options[match].val;
+ else
+ pkg_optopt = 0;
+#ifdef GNU_COMPATIBLE
+ return (BADCH);
+#else
+ return (BADARG);
+#endif
+ }
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ pkg_optarg = has_equal;
+ else if (long_options[match].has_arg ==
+ required_argument) {
+ /*
+ * optional argument doesn't use next nargv
+ */
+ pkg_optarg = nargv[pkg_optind++];
+ }
+ }
+ if ((long_options[match].has_arg == required_argument)
+ && (pkg_optarg == NULL)) {
+ /*
+ * Missing argument; leading ':' indicates no error
+ * should be generated.
+ */
+ if (PRINT_ERROR) {
+ fprintf(stderr, "pkgconf: ");
+ fprintf(stderr, recargstring,
+#ifdef GNU_COMPATIBLE
+ current_dash,
+#endif
+ current_argv);
+ fprintf(stderr, "\n");
+ }
+ /*
+ * XXX: GNU sets pkg_optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ pkg_optopt = (int)long_options[match].val;
+ else
+ pkg_optopt = 0;
+ --pkg_optind;
+ return (BADARG);
+ }
+ } else { /* unknown option */
+ if (short_too) {
+ --pkg_optind;
+ return (-1);
+ }
+ if (PRINT_ERROR) {
+ fprintf(stderr, "pkgconf: ");
+ fprintf(stderr, illoptstring,
+#ifdef GNU_COMPATIBLE
+ current_dash,
+#endif
+ current_argv);
+ fprintf(stderr, "\n");
+ }
+ pkg_optopt = 0;
+ return (BADCH);
+ }
+ if (idx)
+ *idx = match;
+ if (long_options[match].flag) {
+#ifdef PKGCONF_HACK_LOGICAL_OR_ALL_VALUES
+ *long_options[match].flag |= long_options[match].val;
+#else
+ *long_options[match].flag = long_options[match].val;
+#endif
+ return (0);
+ } else
+ return ((int)long_options[match].val);
+}
+
+/*
+ * getopt_internal --
+ * Parse argc/argv argument vector. Called by user level routines.
+ */
+static int
+getopt_internal(int nargc, char * const *nargv, const char *options,
+ const struct pkg_option *long_options, int *idx, int flags)
+{
+ char *oli; /* option letter list index */
+ int optchar, short_too;
+ int posixly_correct; /* no static, can be changed on the fly */
+
+ if (options == NULL)
+ return (-1);
+
+ /*
+ * Disable GNU extensions if POSIXLY_CORRECT is set or options
+ * string begins with a '+'.
+ */
+ posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+#ifdef GNU_COMPATIBLE
+ if (*options == '-')
+ flags |= FLAG_ALLARGS;
+ else if (posixly_correct || *options == '+')
+ flags &= ~FLAG_PERMUTE;
+#else
+ if (posixly_correct || *options == '+')
+ flags &= ~FLAG_PERMUTE;
+ else if (*options == '-')
+ flags |= FLAG_ALLARGS;
+#endif
+
+ if (*options == '+' || *options == '-')
+ options++;
+
+ /*
+ * XXX Some GNU programs (like cvs) set pkg_optind to 0 instead of
+ * XXX using pkg_optreset. Work around this braindamage.
+ */
+ if (pkg_optind == 0)
+ pkg_optind = pkg_optreset = 1;
+
+ pkg_optarg = NULL;
+ if (pkg_optreset)
+ nonopt_start = nonopt_end = -1;
+start:
+ if (pkg_optreset || !*place) { /* update scanning pointer */
+ pkg_optreset = 0;
+ if (pkg_optind >= nargc) { /* end of argument vector */
+ place = EMSG;
+ if (nonopt_end != -1) {
+ /* do permutation, if we have to */
+ permute_args(nonopt_start, nonopt_end,
+ pkg_optind, nargv);
+ pkg_optind -= nonopt_end - nonopt_start;
+ }
+ else if (nonopt_start != -1) {
+ /*
+ * If we skipped non-options, set pkg_optind
+ * to the first of them.
+ */
+ pkg_optind = nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ if (*(place = nargv[pkg_optind]) != '-' ||
+#ifdef GNU_COMPATIBLE
+ place[1] == '\0') {
+#else
+ (place[1] == '\0' && strchr(options, '-') == NULL)) {
+#endif
+ place = EMSG; /* found non-option */
+ if (flags & FLAG_ALLARGS) {
+ /*
+ * GNU extension:
+ * return non-option as argument to option 1
+ */
+ pkg_optarg = nargv[pkg_optind++];
+ return (INORDER);
+ }
+ if (!(flags & FLAG_PERMUTE)) {
+ /*
+ * If no permutation wanted, stop parsing
+ * at first non-option.
+ */
+ return (-1);
+ }
+ /* do permutation */
+ if (nonopt_start == -1)
+ nonopt_start = pkg_optind;
+ else if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ pkg_optind, nargv);
+ nonopt_start = pkg_optind -
+ (nonopt_end - nonopt_start);
+ nonopt_end = -1;
+ }
+ pkg_optind++;
+ /* process next argument */
+ goto start;
+ }
+ if (nonopt_start != -1 && nonopt_end == -1)
+ nonopt_end = pkg_optind;
+
+ /*
+ * If we have "-" do nothing, if "--" we are done.
+ */
+ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
+ pkg_optind++;
+ place = EMSG;
+ /*
+ * We found an option (--), so if we skipped
+ * non-options, we have to permute.
+ */
+ if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ pkg_optind, nargv);
+ pkg_optind -= nonopt_end - nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ }
+
+ /*
+ * Check long options if:
+ * 1) we were passed some
+ * 2) the arg is not just "-"
+ * 3) either the arg starts with -- we are getopt_long_only()
+ */
+ if (long_options != NULL && place != nargv[pkg_optind] &&
+ (*place == '-' || (flags & FLAG_LONGONLY))) {
+ short_too = 0;
+#ifdef GNU_COMPATIBLE
+ dash_prefix = D_PREFIX;
+#endif
+ if (*place == '-') {
+ place++; /* --foo long option */
+#ifdef GNU_COMPATIBLE
+ dash_prefix = DD_PREFIX;
+#endif
+ } else if (*place != ':' && strchr(options, *place) != NULL)
+ short_too = 1; /* could be short option too */
+
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, short_too, flags);
+ if (optchar != -1) {
+ place = EMSG;
+ return (optchar);
+ }
+ }
+
+ if ((optchar = (int)*place++) == (int)':' ||
+ (optchar == (int)'-' && *place != '\0') ||
+ (oli = strchr(options, optchar)) == NULL) {
+ /*
+ * If the user specified "-" and '-' isn't listed in
+ * options, return -1 (non-option) as per POSIX.
+ * Otherwise, it is an unknown option character (or ':').
+ */
+ if (optchar == (int)'-' && *place == '\0')
+ return (-1);
+ if (!*place)
+ ++pkg_optind;
+#ifdef GNU_COMPATIBLE
+ if (PRINT_ERROR) {
+ fprintf(stderr, "pkgconf: ");
+ fprintf(stderr, posixly_correct ? illoptchar : gnuoptchar,
+ optchar);
+ fprintf(stderr, "\n");
+ }
+#else
+ if (PRINT_ERROR) {
+ fprintf(stderr, "pkgconf: ");
+ fprintf(stderr, illoptchar, optchar);
+ fprintf(stderr, "\n");
+ }
+#endif
+ pkg_optopt = optchar;
+ return (BADCH);
+ }
+ if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
+ /* -W long-option */
+ if (*place) /* no space */
+ /* NOTHING */;
+ else if (++pkg_optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR) {
+ fprintf(stderr, "pkgconf: ");
+ fprintf(stderr, recargchar, optchar);
+ fprintf(stderr, "\n");
+ }
+ pkg_optopt = optchar;
+ return (BADARG);
+ } else /* white space */
+ place = nargv[pkg_optind];
+#ifdef GNU_COMPATIBLE
+ dash_prefix = W_PREFIX;
+#endif
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, 0, flags);
+ place = EMSG;
+ return (optchar);
+ }
+ if (*++oli != ':') { /* doesn't take argument */
+ if (!*place)
+ ++pkg_optind;
+ } else { /* takes (optional) argument */
+ pkg_optarg = NULL;
+ if (*place) /* no white space */
+ pkg_optarg = place;
+ else if (oli[1] != ':') { /* arg not optional */
+ if (++pkg_optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR) {
+ fprintf(stderr, "pkgconf: ");
+ fprintf(stderr, recargchar, optchar);
+ fprintf(stderr, "\n");
+ }
+ pkg_optopt = optchar;
+ return (BADARG);
+ } else
+ pkg_optarg = nargv[pkg_optind];
+ }
+ place = EMSG;
+ ++pkg_optind;
+ }
+ /* dump back option letter */
+ return (optchar);
+}
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ *
+ * [eventually this will replace the BSD getopt]
+ */
+int
+pkg_getopt(int nargc, char * const *nargv, const char *options)
+{
+
+ /*
+ * We don't pass FLAG_PERMUTE to getopt_internal() since
+ * the BSD getopt(3) (unlike GNU) has never done this.
+ *
+ * Furthermore, since many privileged programs call getopt()
+ * before dropping privileges it makes sense to keep things
+ * as simple (and bug-free) as possible.
+ */
+ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
+}
+
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+pkg_getopt_long(int nargc, char * const *nargv, const char *options,
+ const struct pkg_option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE));
+}
+
+/*
+ * getopt_long_only --
+ * Parse argc/argv argument vector.
+ */
+int
+pkg_getopt_long_only(int nargc, char * const *nargv, const char *options,
+ const struct pkg_option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE|FLAG_LONGONLY));
+}
diff --git a/cli/getopt_long.h b/cli/getopt_long.h
new file mode 100644
index 000000000000..b1242915ab5b
--- /dev/null
+++ b/cli/getopt_long.h
@@ -0,0 +1,70 @@
+/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _GETOPT_LONG_H_
+#define _GETOPT_LONG_H_
+
+#include <stdint.h>
+
+/*
+ * GNU-like getopt_long()/getopt_long_only() with 4.4BSD optreset extension.
+ * getopt() is declared here too for GNU programs.
+ */
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+struct pkg_option {
+ /* name of long option */
+ const char *name;
+ /*
+ * one of no_argument, required_argument, and optional_argument:
+ * whether option takes an argument
+ */
+ int has_arg;
+ /* if not NULL, set *flag to val when option found */
+ uint64_t *flag;
+ /* if flag not NULL, value to set *flag to; else return value */
+ uint64_t val;
+};
+
+int pkg_getopt_long(int, char * const *, const char *,
+ const struct pkg_option *, int *);
+int pkg_getopt_long_only(int, char * const *, const char *,
+ const struct pkg_option *, int *);
+int pkg_getopt(int, char * const [], const char *);
+
+extern char *pkg_optarg; /* getopt(3) external variables */
+extern int pkg_optind, pkg_opterr, pkg_optopt;
+extern int pkg_optreset; /* getopt(3) external variable */
+
+#endif
diff --git a/cli/main.c b/cli/main.c
new file mode 100644
index 000000000000..53eee4c87443
--- /dev/null
+++ b/cli/main.c
@@ -0,0 +1,1823 @@
+/*
+ * main.c
+ * main() routine, printer functions
+ *
+ * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
+ * pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include "libpkgconf/config.h"
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+#include "getopt_long.h"
+#ifndef PKGCONF_LITE
+#include "renderer-msvc.h"
+#endif
+#ifdef _WIN32
+#include <io.h> /* for _setmode() */
+#include <fcntl.h>
+#endif
+
+#define PKG_CFLAGS_ONLY_I (((uint64_t) 1) << 2)
+#define PKG_CFLAGS_ONLY_OTHER (((uint64_t) 1) << 3)
+#define PKG_CFLAGS (PKG_CFLAGS_ONLY_I|PKG_CFLAGS_ONLY_OTHER)
+#define PKG_LIBS_ONLY_LDPATH (((uint64_t) 1) << 5)
+#define PKG_LIBS_ONLY_LIBNAME (((uint64_t) 1) << 6)
+#define PKG_LIBS_ONLY_OTHER (((uint64_t) 1) << 7)
+#define PKG_LIBS (PKG_LIBS_ONLY_LDPATH|PKG_LIBS_ONLY_LIBNAME|PKG_LIBS_ONLY_OTHER)
+#define PKG_MODVERSION (((uint64_t) 1) << 8)
+#define PKG_REQUIRES (((uint64_t) 1) << 9)
+#define PKG_REQUIRES_PRIVATE (((uint64_t) 1) << 10)
+#define PKG_VARIABLES (((uint64_t) 1) << 11)
+#define PKG_DIGRAPH (((uint64_t) 1) << 12)
+#define PKG_KEEP_SYSTEM_CFLAGS (((uint64_t) 1) << 13)
+#define PKG_KEEP_SYSTEM_LIBS (((uint64_t) 1) << 14)
+#define PKG_VERSION (((uint64_t) 1) << 15)
+#define PKG_ABOUT (((uint64_t) 1) << 16)
+#define PKG_ENV_ONLY (((uint64_t) 1) << 17)
+#define PKG_ERRORS_ON_STDOUT (((uint64_t) 1) << 18)
+#define PKG_SILENCE_ERRORS (((uint64_t) 1) << 19)
+#define PKG_IGNORE_CONFLICTS (((uint64_t) 1) << 20)
+#define PKG_STATIC (((uint64_t) 1) << 21)
+#define PKG_NO_UNINSTALLED (((uint64_t) 1) << 22)
+#define PKG_UNINSTALLED (((uint64_t) 1) << 23)
+#define PKG_LIST (((uint64_t) 1) << 24)
+#define PKG_HELP (((uint64_t) 1) << 25)
+#define PKG_PRINT_ERRORS (((uint64_t) 1) << 26)
+#define PKG_SIMULATE (((uint64_t) 1) << 27)
+#define PKG_NO_CACHE (((uint64_t) 1) << 28)
+#define PKG_PROVIDES (((uint64_t) 1) << 29)
+#define PKG_VALIDATE (((uint64_t) 1) << 30)
+#define PKG_LIST_PACKAGE_NAMES (((uint64_t) 1) << 31)
+#define PKG_NO_PROVIDES (((uint64_t) 1) << 32)
+#define PKG_PURE (((uint64_t) 1) << 33)
+#define PKG_PATH (((uint64_t) 1) << 34)
+#define PKG_DEFINE_PREFIX (((uint64_t) 1) << 35)
+#define PKG_DONT_DEFINE_PREFIX (((uint64_t) 1) << 36)
+#define PKG_DONT_RELOCATE_PATHS (((uint64_t) 1) << 37)
+#define PKG_DEBUG (((uint64_t) 1) << 38)
+#define PKG_SHORT_ERRORS (((uint64_t) 1) << 39)
+#define PKG_EXISTS (((uint64_t) 1) << 40)
+#define PKG_MSVC_SYNTAX (((uint64_t) 1) << 41)
+#define PKG_INTERNAL_CFLAGS (((uint64_t) 1) << 42)
+#define PKG_DUMP_PERSONALITY (((uint64_t) 1) << 43)
+#define PKG_SHARED (((uint64_t) 1) << 44)
+#define PKG_DUMP_LICENSE (((uint64_t) 1) << 45)
+#define PKG_SOLUTION (((uint64_t) 1) << 46)
+#define PKG_EXISTS_CFLAGS (((uint64_t) 1) << 47)
+#define PKG_FRAGMENT_TREE (((uint64_t) 1) << 48)
+
+static pkgconf_client_t pkg_client;
+static const pkgconf_fragment_render_ops_t *want_render_ops = NULL;
+
+static uint64_t want_flags;
+static int verbosity = 0;
+static int maximum_traverse_depth = 2000;
+static size_t maximum_package_count = 0;
+
+static char *want_variable = NULL;
+static char *want_fragment_filter = NULL;
+
+FILE *error_msgout = NULL;
+FILE *logfile_out = NULL;
+
+static bool
+error_handler(const char *msg, const pkgconf_client_t *client, void *data)
+{
+ (void) client;
+ (void) data;
+ fprintf(error_msgout, "%s", msg);
+ return true;
+}
+
+static bool
+print_list_entry(const pkgconf_pkg_t *entry, void *data)
+{
+ (void) data;
+
+ if (entry->flags & PKGCONF_PKG_PROPF_UNINSTALLED)
+ return false;
+
+ printf("%-30s %s - %s\n", entry->id, entry->realname, entry->description);
+
+ return false;
+}
+
+static bool
+print_package_entry(const pkgconf_pkg_t *entry, void *data)
+{
+ (void) data;
+
+ if (entry->flags & PKGCONF_PKG_PROPF_UNINSTALLED)
+ return false;
+
+ printf("%s\n", entry->id);
+
+ return false;
+}
+
+static bool
+filter_cflags(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data)
+{
+ int got_flags = 0;
+ (void) client;
+ (void) data;
+
+ if (!(want_flags & PKG_KEEP_SYSTEM_CFLAGS) && pkgconf_fragment_has_system_dir(client, frag))
+ return false;
+
+ if (want_fragment_filter != NULL && (strchr(want_fragment_filter, frag->type) == NULL || !frag->type))
+ return false;
+
+ if (frag->type == 'I')
+ got_flags = PKG_CFLAGS_ONLY_I;
+ else
+ got_flags = PKG_CFLAGS_ONLY_OTHER;
+
+ return (want_flags & got_flags) != 0;
+}
+
+static bool
+filter_libs(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data)
+{
+ int got_flags = 0;
+ (void) client;
+ (void) data;
+
+ if (!(want_flags & PKG_KEEP_SYSTEM_LIBS) && pkgconf_fragment_has_system_dir(client, frag))
+ return false;
+
+ if (want_fragment_filter != NULL && (strchr(want_fragment_filter, frag->type) == NULL || !frag->type))
+ return false;
+
+ switch (frag->type)
+ {
+ case 'L': got_flags = PKG_LIBS_ONLY_LDPATH; break;
+ case 'l': got_flags = PKG_LIBS_ONLY_LIBNAME; break;
+ default: got_flags = PKG_LIBS_ONLY_OTHER; break;
+ }
+
+ return (want_flags & got_flags) != 0;
+}
+
+static void
+print_variables(pkgconf_pkg_t *pkg)
+{
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->vars.head, node)
+ {
+ pkgconf_tuple_t *tuple = node->data;
+
+ printf("%s\n", tuple->key);
+ }
+}
+
+static void
+print_requires(pkgconf_pkg_t *pkg)
+{
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+
+ printf("%s", dep->package);
+
+ if (dep->version != NULL)
+ printf(" %s %s", pkgconf_pkg_get_comparator(dep), dep->version);
+
+ printf("\n");
+ }
+}
+
+static void
+print_requires_private(pkgconf_pkg_t *pkg)
+{
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+
+ printf("%s", dep->package);
+
+ if (dep->version != NULL)
+ printf(" %s %s", pkgconf_pkg_get_comparator(dep), dep->version);
+
+ printf("\n");
+ }
+}
+
+static void
+print_provides(pkgconf_pkg_t *pkg)
+{
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->provides.head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+
+ printf("%s", dep->package);
+
+ if (dep->version != NULL)
+ printf(" %s %s", pkgconf_pkg_get_comparator(dep), dep->version);
+
+ printf("\n");
+ }
+}
+
+static bool
+apply_provides(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth)
+{
+ pkgconf_node_t *iter;
+ (void) client;
+ (void) unused;
+ (void) maxdepth;
+
+ PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter)
+ {
+ pkgconf_dependency_t *dep = iter->data;
+ pkgconf_pkg_t *pkg = dep->match;
+
+ print_provides(pkg);
+ }
+
+ return true;
+}
+
+#ifndef PKGCONF_LITE
+static void
+print_digraph_node(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
+{
+ pkgconf_node_t *node;
+ (void) client;
+ pkgconf_pkg_t **last_seen = data;
+
+ if(pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
+ return;
+
+ if (pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE)
+ printf("\"%s\" [fontname=Sans fontsize=8 fontcolor=gray color=gray]\n", pkg->id);
+ else
+ printf("\"%s\" [fontname=Sans fontsize=8]\n", pkg->id);
+
+ if (last_seen != NULL)
+ {
+ if (*last_seen != NULL)
+ printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8 color=red]\n", (*last_seen)->id, pkg->id);
+
+ *last_seen = pkg;
+ }
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+ const char *dep_id = (dep->match != NULL) ? dep->match->id : dep->package;
+
+ if ((dep->flags & PKGCONF_PKG_DEPF_PRIVATE) == 0)
+ printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8]\n", pkg->id, dep_id);
+ else
+ printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8 color=gray]\n", pkg->id, dep_id);
+ }
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+ const char *dep_id = (dep->match != NULL) ? dep->match->id : dep->package;
+
+ printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8 color=gray]\n", pkg->id, dep_id);
+ }
+}
+
+static bool
+apply_digraph(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth)
+{
+ int eflag;
+ pkgconf_list_t *list = data;
+ pkgconf_pkg_t *last_seen = NULL;
+ pkgconf_node_t *iter;
+
+ printf("digraph deptree {\n");
+ printf("edge [color=blue len=7.5 fontname=Sans fontsize=8]\n");
+ printf("node [fontname=Sans fontsize=8]\n");
+ printf("\"user:request\" [fontname=Sans fontsize=8]\n");
+
+ PKGCONF_FOREACH_LIST_ENTRY(list->head, iter)
+ {
+ pkgconf_queue_t *pkgq = iter->data;
+ pkgconf_pkg_t *pkg = pkgconf_pkg_find(client, pkgq->package);
+ printf("\"user:request\" -> \"%s\" [fontname=Sans fontsize=8]\n", pkg == NULL ? pkgq->package : pkg->id);
+ if (pkg != NULL)
+ pkgconf_pkg_unref(client, pkg);
+ }
+
+ eflag = pkgconf_pkg_traverse(client, world, print_digraph_node, &last_seen, maxdepth, 0);
+
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ return false;
+
+ printf("}\n");
+ return true;
+}
+
+static void
+print_solution_node(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *unused)
+{
+ (void) client;
+ (void) unused;
+
+ printf("%s (%"PRIu64")%s\n", pkg->id, pkg->identifier, (pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE) == PKGCONF_PKG_PROPF_VISITED_PRIVATE ? " [private]" : "");
+}
+
+static bool
+apply_print_solution(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth)
+{
+ int eflag;
+
+ eflag = pkgconf_pkg_traverse(client, world, print_solution_node, unused, maxdepth, 0);
+
+ return eflag == PKGCONF_PKG_ERRF_OK;
+}
+#endif
+
+static bool
+apply_modversion(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth)
+{
+ pkgconf_node_t *queue_iter;
+ pkgconf_list_t *pkgq = data;
+ (void) client;
+ (void) maxdepth;
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkgq->head, queue_iter)
+ {
+ pkgconf_node_t *world_iter;
+ pkgconf_queue_t *queue_node = queue_iter->data;
+
+ PKGCONF_FOREACH_LIST_ENTRY(world->required.head, world_iter)
+ {
+ pkgconf_dependency_t *dep = world_iter->data;
+ pkgconf_pkg_t *pkg = dep->match;
+
+ const size_t name_len = strlen(pkg->why);
+ if (name_len > strlen(queue_node->package) ||
+ strncmp(pkg->why, queue_node->package, name_len) ||
+ (queue_node->package[name_len] != 0 &&
+ !isspace((unsigned char)queue_node->package[name_len]) &&
+ !PKGCONF_IS_OPERATOR_CHAR(queue_node->package[name_len])))
+ continue;
+
+ if (pkg->version != NULL) {
+ if (verbosity)
+ printf("%s: ", pkg->id);
+
+ printf("%s\n", pkg->version);
+ }
+
+ break;
+ }
+ }
+
+ return true;
+}
+
+static bool
+apply_variables(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth)
+{
+ pkgconf_node_t *iter;
+ (void) client;
+ (void) unused;
+ (void) maxdepth;
+
+ PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter)
+ {
+ pkgconf_dependency_t *dep = iter->data;
+ pkgconf_pkg_t *pkg = dep->match;
+
+ print_variables(pkg);
+ }
+
+ return true;
+}
+
+static bool
+apply_path(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth)
+{
+ pkgconf_node_t *iter;
+ (void) client;
+ (void) unused;
+ (void) maxdepth;
+
+ PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter)
+ {
+ pkgconf_dependency_t *dep = iter->data;
+ pkgconf_pkg_t *pkg = dep->match;
+
+ /* a module entry with no filename is either virtual, static (builtin) or synthesized. */
+ if (pkg->filename != NULL)
+ printf("%s\n", pkg->filename);
+ }
+
+ return true;
+}
+
+static bool
+apply_variable(pkgconf_client_t *client, pkgconf_pkg_t *world, void *variable, int maxdepth)
+{
+ pkgconf_node_t *iter;
+ (void) maxdepth;
+
+ PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter)
+ {
+ pkgconf_dependency_t *dep = iter->data;
+ pkgconf_pkg_t *pkg = dep->match;
+ const char *var;
+
+ var = pkgconf_tuple_find(client, &pkg->vars, variable);
+
+ if (var != NULL)
+ printf("%s%s", iter->prev != NULL ? " " : "", var);
+ }
+
+ printf("\n");
+
+ return true;
+}
+
+static bool
+apply_env_var(const char *prefix, pkgconf_client_t *client, pkgconf_pkg_t *world, int maxdepth,
+ unsigned int (*collect_fn)(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list, int maxdepth),
+ bool (*filter_fn)(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data),
+ void (*postprocess_fn)(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *fragment_list))
+{
+ pkgconf_list_t unfiltered_list = PKGCONF_LIST_INITIALIZER;
+ pkgconf_list_t filtered_list = PKGCONF_LIST_INITIALIZER;
+ unsigned int eflag;
+ char *render_buf;
+
+ eflag = collect_fn(client, world, &unfiltered_list, maxdepth);
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ return false;
+
+ pkgconf_fragment_filter(client, &filtered_list, &unfiltered_list, filter_fn, NULL);
+
+ if (postprocess_fn != NULL)
+ postprocess_fn(client, world, &filtered_list);
+
+ if (filtered_list.head == NULL)
+ goto out;
+
+ render_buf = pkgconf_fragment_render(&filtered_list, true, want_render_ops);
+ printf("%s='%s'\n", prefix, render_buf);
+ free(render_buf);
+
+out:
+ pkgconf_fragment_free(&unfiltered_list);
+ pkgconf_fragment_free(&filtered_list);
+
+ return true;
+}
+
+static void
+maybe_add_module_definitions(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *fragment_list)
+{
+ pkgconf_node_t *world_iter;
+
+ if ((want_flags & PKG_EXISTS_CFLAGS) != PKG_EXISTS_CFLAGS)
+ return;
+
+ PKGCONF_FOREACH_LIST_ENTRY(world->required.head, world_iter)
+ {
+ pkgconf_dependency_t *dep = world_iter->data;
+ char havebuf[PKGCONF_ITEM_SIZE];
+ char *p;
+
+ if ((dep->flags & PKGCONF_PKG_DEPF_QUERY) != PKGCONF_PKG_DEPF_QUERY)
+ continue;
+
+ if (dep->match == NULL)
+ continue;
+
+ snprintf(havebuf, sizeof havebuf, "HAVE_%s", dep->match->id);
+
+ for (p = havebuf; *p; p++)
+ {
+ switch (*p)
+ {
+ case ' ':
+ case '-':
+ *p = '_';
+ break;
+
+ default:
+ *p = toupper((unsigned char) *p);
+ }
+ }
+
+ pkgconf_fragment_insert(client, fragment_list, 'D', havebuf, false);
+ }
+}
+
+static void
+apply_env_variables(pkgconf_client_t *client, pkgconf_pkg_t *world, const char *env_prefix)
+{
+ (void) client;
+ pkgconf_node_t *world_iter;
+
+ PKGCONF_FOREACH_LIST_ENTRY(world->required.head, world_iter)
+ {
+ pkgconf_dependency_t *dep = world_iter->data;
+ pkgconf_pkg_t *pkg = dep->match;
+ pkgconf_node_t *tuple_iter;
+
+ if ((dep->flags & PKGCONF_PKG_DEPF_QUERY) != PKGCONF_PKG_DEPF_QUERY)
+ continue;
+
+ if (dep->match == NULL)
+ continue;
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->vars.head, tuple_iter)
+ {
+ pkgconf_tuple_t *tuple = tuple_iter->data;
+ char havebuf[PKGCONF_ITEM_SIZE];
+ char *p;
+
+ if (want_variable != NULL && strcmp(want_variable, tuple->key))
+ continue;
+
+ snprintf(havebuf, sizeof havebuf, "%s_%s", env_prefix, tuple->key);
+
+ for (p = havebuf; *p; p++)
+ {
+ switch (*p)
+ {
+ case ' ':
+ case '-':
+ *p = '_';
+ break;
+
+ default:
+ *p = toupper((unsigned char) *p);
+ }
+ }
+
+ printf("%s='%s'\n", havebuf, tuple->value);
+ }
+ }
+}
+
+static bool
+apply_env(pkgconf_client_t *client, pkgconf_pkg_t *world, void *env_prefix_p, int maxdepth)
+{
+ const char *want_env_prefix = env_prefix_p, *it;
+ char workbuf[PKGCONF_ITEM_SIZE];
+
+ for (it = want_env_prefix; *it != '\0'; it++)
+ if (!isalpha((unsigned char)*it) &&
+ !isdigit((unsigned char)*it))
+ return false;
+
+ snprintf(workbuf, sizeof workbuf, "%s_CFLAGS", want_env_prefix);
+ if (!apply_env_var(workbuf, client, world, maxdepth, pkgconf_pkg_cflags, filter_cflags, maybe_add_module_definitions))
+ return false;
+
+ snprintf(workbuf, sizeof workbuf, "%s_LIBS", want_env_prefix);
+ if (!apply_env_var(workbuf, client, world, maxdepth, pkgconf_pkg_libs, filter_libs, NULL))
+ return false;
+
+ if ((want_flags & PKG_VARIABLES) == PKG_VARIABLES || want_variable != NULL)
+ apply_env_variables(client, world, want_env_prefix);
+
+ return true;
+}
+
+static bool
+apply_cflags(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth)
+{
+ pkgconf_list_t unfiltered_list = PKGCONF_LIST_INITIALIZER;
+ pkgconf_list_t filtered_list = PKGCONF_LIST_INITIALIZER;
+ int eflag;
+ char *render_buf;
+ (void) unused;
+
+ eflag = pkgconf_pkg_cflags(client, world, &unfiltered_list, maxdepth);
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ return false;
+
+ pkgconf_fragment_filter(client, &filtered_list, &unfiltered_list, filter_cflags, NULL);
+ maybe_add_module_definitions(client, world, &filtered_list);
+
+ if (filtered_list.head == NULL)
+ goto out;
+
+ render_buf = pkgconf_fragment_render(&filtered_list, true, want_render_ops);
+ printf("%s", render_buf);
+ free(render_buf);
+
+out:
+ pkgconf_fragment_free(&unfiltered_list);
+ pkgconf_fragment_free(&filtered_list);
+
+ return true;
+}
+
+static bool
+apply_libs(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth)
+{
+ pkgconf_list_t unfiltered_list = PKGCONF_LIST_INITIALIZER;
+ pkgconf_list_t filtered_list = PKGCONF_LIST_INITIALIZER;
+ int eflag;
+ char *render_buf;
+ (void) unused;
+
+ eflag = pkgconf_pkg_libs(client, world, &unfiltered_list, maxdepth);
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ return false;
+
+ pkgconf_fragment_filter(client, &filtered_list, &unfiltered_list, filter_libs, NULL);
+
+ if (filtered_list.head == NULL)
+ goto out;
+
+ render_buf = pkgconf_fragment_render(&filtered_list, true, want_render_ops);
+ printf("%s", render_buf);
+ free(render_buf);
+
+out:
+ pkgconf_fragment_free(&unfiltered_list);
+ pkgconf_fragment_free(&filtered_list);
+
+ return true;
+}
+
+static bool
+apply_requires(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth)
+{
+ pkgconf_node_t *iter;
+ (void) client;
+ (void) unused;
+ (void) maxdepth;
+
+ PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter)
+ {
+ pkgconf_dependency_t *dep = iter->data;
+ pkgconf_pkg_t *pkg = dep->match;
+
+ print_requires(pkg);
+ }
+
+ return true;
+}
+
+static bool
+apply_requires_private(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth)
+{
+ pkgconf_node_t *iter;
+ (void) client;
+ (void) unused;
+ (void) maxdepth;
+
+ PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter)
+ {
+ pkgconf_dependency_t *dep = iter->data;
+ pkgconf_pkg_t *pkg = dep->match;
+
+ print_requires_private(pkg);
+ }
+ return true;
+}
+
+static void
+check_uninstalled(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
+{
+ int *retval = data;
+ (void) client;
+
+ if (pkg->flags & PKGCONF_PKG_PROPF_UNINSTALLED)
+ *retval = EXIT_SUCCESS;
+}
+
+static bool
+apply_uninstalled(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth)
+{
+ int eflag;
+
+ eflag = pkgconf_pkg_traverse(client, world, check_uninstalled, data, maxdepth, 0);
+
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ return false;
+
+ return true;
+}
+
+#ifndef PKGCONF_LITE
+static void
+print_graph_node(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
+{
+ pkgconf_node_t *n;
+
+ (void) client;
+ (void) data;
+
+ printf("node '%s' {\n", pkg->id);
+
+ if (pkg->version != NULL)
+ printf(" version = '%s';\n", pkg->version);
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, n)
+ {
+ pkgconf_dependency_t *dep = n->data;
+ printf(" dependency '%s'", dep->package);
+ if (dep->compare != PKGCONF_CMP_ANY)
+ {
+ printf(" {\n");
+ printf(" comparator = '%s';\n", pkgconf_pkg_get_comparator(dep));
+ printf(" version = '%s';\n", dep->version);
+ printf(" };\n");
+ }
+ else
+ printf(";\n");
+ }
+
+ printf("};\n");
+}
+
+static bool
+apply_simulate(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth)
+{
+ int eflag;
+
+ eflag = pkgconf_pkg_traverse(client, world, print_graph_node, data, maxdepth, 0);
+
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ return false;
+
+ return true;
+}
+#endif
+
+static void
+print_fragment_tree_branch(pkgconf_list_t *fragment_list, int indent)
+{
+ pkgconf_node_t *iter;
+
+ PKGCONF_FOREACH_LIST_ENTRY(fragment_list->head, iter)
+ {
+ pkgconf_fragment_t *frag = iter->data;
+
+ if (frag->type)
+ printf("%*s'-%c%s' [type %c]\n", indent, "", frag->type, frag->data, frag->type);
+ else
+ printf("%*s'%s' [untyped]\n", indent, "", frag->data);
+
+ print_fragment_tree_branch(&frag->children, indent + 2);
+ }
+
+ if (fragment_list->head != NULL)
+ printf("\n");
+}
+
+static bool
+apply_fragment_tree(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth)
+{
+ pkgconf_list_t unfiltered_list = PKGCONF_LIST_INITIALIZER;
+ int eflag;
+
+ (void) data;
+
+ eflag = pkgconf_pkg_cflags(client, world, &unfiltered_list, maxdepth);
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ return false;
+
+ eflag = pkgconf_pkg_libs(client, world, &unfiltered_list, maxdepth);
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ return false;
+
+ print_fragment_tree_branch(&unfiltered_list, 0);
+ pkgconf_fragment_free(&unfiltered_list);
+
+ return true;
+}
+
+static void
+print_license(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
+{
+ (void) client;
+ (void) data;
+
+ if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
+ return;
+
+ /* NOASSERTION is the default when the license is unknown, per SPDX spec § 3.15 */
+ printf("%s: %s\n", pkg->id, pkg->license != NULL ? pkg->license : "NOASSERTION");
+}
+
+static bool
+apply_license(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth)
+{
+ int eflag;
+
+ eflag = pkgconf_pkg_traverse(client, world, print_license, data, maxdepth, 0);
+
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ return false;
+
+ return true;
+}
+
+static void
+version(void)
+{
+ printf("%s\n", PACKAGE_VERSION);
+}
+
+static void
+about(void)
+{
+ printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+ printf("Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021\n");
+ printf(" pkgconf authors (see AUTHORS in documentation directory).\n\n");
+ printf("Permission to use, copy, modify, and/or distribute this software for any\n");
+ printf("purpose with or without fee is hereby granted, provided that the above\n");
+ printf("copyright notice and this permission notice appear in all copies.\n\n");
+ printf("This software is provided 'as is' and without any warranty, express or\n");
+ printf("implied. In no event shall the authors be liable for any damages arising\n");
+ printf("from the use of this software.\n\n");
+ printf("Report bugs at <%s>.\n", PACKAGE_BUGREPORT);
+}
+
+static void
+usage(void)
+{
+ printf("usage: %s [OPTIONS] [LIBRARIES]\n", PACKAGE_NAME);
+
+ printf("\nbasic options:\n\n");
+
+ printf(" --help this message\n");
+ printf(" --about print pkgconf version and license to stdout\n");
+ printf(" --version print supported pkg-config version to stdout\n");
+ printf(" --verbose print additional information\n");
+ printf(" --atleast-pkgconfig-version check whether or not pkgconf is compatible\n");
+ printf(" with a specified pkg-config version\n");
+ printf(" --errors-to-stdout print all errors on stdout instead of stderr\n");
+ printf(" --print-errors ensure all errors are printed\n");
+ printf(" --short-errors be less verbose about some errors\n");
+ printf(" --silence-errors explicitly be silent about errors\n");
+ printf(" --list-all list all known packages\n");
+ printf(" --list-package-names list all known package names\n");
+#ifndef PKGCONF_LITE
+ printf(" --simulate simulate walking the calculated dependency graph\n");
+#endif
+ printf(" --no-cache do not cache already seen packages when\n");
+ printf(" walking the dependency graph\n");
+ printf(" --log-file=filename write an audit log to a specified file\n");
+ printf(" --with-path=path adds a directory to the search path\n");
+ printf(" --define-prefix override the prefix variable with one that is guessed based on\n");
+ printf(" the location of the .pc file\n");
+ printf(" --dont-define-prefix do not override the prefix variable under any circumstances\n");
+ printf(" --prefix-variable=varname sets the name of the variable that pkgconf considers\n");
+ printf(" to be the package prefix\n");
+ printf(" --relocate=path relocates a path and exits (mostly for testsuite)\n");
+ printf(" --dont-relocate-paths disables path relocation support\n");
+
+#ifndef PKGCONF_LITE
+ printf("\ncross-compilation personality support:\n\n");
+ printf(" --personality=triplet|filename sets the personality to 'triplet' or a file named 'filename'\n");
+ printf(" --dump-personality dumps details concerning selected personality\n");
+#endif
+
+ printf("\nchecking specific pkg-config database entries:\n\n");
+
+ printf(" --atleast-version require a specific version of a module\n");
+ printf(" --exact-version require an exact version of a module\n");
+ printf(" --max-version require a maximum version of a module\n");
+ printf(" --exists check whether or not a module exists\n");
+ printf(" --uninstalled check whether or not an uninstalled module will be used\n");
+ printf(" --no-uninstalled never use uninstalled modules when satisfying dependencies\n");
+ printf(" --no-provides do not use 'provides' rules to resolve dependencies\n");
+ printf(" --maximum-traverse-depth maximum allowed depth for dependency graph\n");
+ printf(" --static be more aggressive when computing dependency graph\n");
+ printf(" (for static linking)\n");
+ printf(" --shared use a simplified dependency graph (usually default)\n");
+ printf(" --pure optimize a static dependency graph as if it were a normal\n");
+ printf(" dependency graph\n");
+ printf(" --env-only look only for package entries in PKG_CONFIG_PATH\n");
+ printf(" --ignore-conflicts ignore 'conflicts' rules in modules\n");
+ printf(" --validate validate specific .pc files for correctness\n");
+
+ printf("\nquerying specific pkg-config database fields:\n\n");
+
+ printf(" --define-variable=varname=value define variable 'varname' as 'value'\n");
+ printf(" --variable=varname print specified variable entry to stdout\n");
+ printf(" --cflags print required CFLAGS to stdout\n");
+ printf(" --cflags-only-I print required include-dir CFLAGS to stdout\n");
+ printf(" --cflags-only-other print required non-include-dir CFLAGS to stdout\n");
+ printf(" --libs print required linker flags to stdout\n");
+ printf(" --libs-only-L print required LDPATH linker flags to stdout\n");
+ printf(" --libs-only-l print required LIBNAME linker flags to stdout\n");
+ printf(" --libs-only-other print required other linker flags to stdout\n");
+ printf(" --print-requires print required dependency frameworks to stdout\n");
+ printf(" --print-requires-private print required dependency frameworks for static\n");
+ printf(" linking to stdout\n");
+ printf(" --print-provides print provided dependencies to stdout\n");
+ printf(" --print-variables print all known variables in module to stdout\n");
+#ifndef PKGCONF_LITE
+ printf(" --digraph print entire dependency graph in graphviz 'dot' format\n");
+ printf(" --solution print dependency graph solution in a simple format\n");
+#endif
+ printf(" --keep-system-cflags keep -I%s entries in cflags output\n", SYSTEM_INCLUDEDIR);
+ printf(" --keep-system-libs keep -L%s entries in libs output\n", SYSTEM_LIBDIR);
+ printf(" --path show the exact filenames for any matching .pc files\n");
+ printf(" --modversion print the specified module's version to stdout\n");
+ printf(" --internal-cflags do not filter 'internal' cflags from output\n");
+ printf(" --license print the specified module's license to stdout if known\n");
+ printf(" --exists-cflags add -DHAVE_FOO fragments to cflags for each found module\n");
+
+ printf("\nfiltering output:\n\n");
+#ifndef PKGCONF_LITE
+ printf(" --msvc-syntax print translatable fragments in MSVC syntax\n");
+#endif
+ printf(" --fragment-filter=types filter output fragments to the specified types\n");
+ printf(" --env=prefix print output as shell-compatible environmental variables\n");
+ printf(" --fragment-tree visualize printed CFLAGS/LIBS fragments as a tree\n");
+
+ printf("\nreport bugs to <%s>.\n", PACKAGE_BUGREPORT);
+}
+
+static void
+relocate_path(const char *path)
+{
+ char buf[PKGCONF_BUFSIZE];
+
+ pkgconf_strlcpy(buf, path, sizeof buf);
+ pkgconf_path_relocate(buf, sizeof buf);
+
+ printf("%s\n", buf);
+}
+
+#ifndef PKGCONF_LITE
+static void
+dump_personality(const pkgconf_cross_personality_t *p)
+{
+ pkgconf_node_t *n;
+
+ printf("Triplet: %s\n", p->name);
+
+ if (p->sysroot_dir)
+ printf("SysrootDir: %s\n", p->sysroot_dir);
+
+ printf("DefaultSearchPaths: ");
+ PKGCONF_FOREACH_LIST_ENTRY(p->dir_list.head, n)
+ {
+ pkgconf_path_t *pn = n->data;
+ printf("%s ", pn->path);
+ }
+
+ printf("\n");
+ printf("SystemIncludePaths: ");
+ PKGCONF_FOREACH_LIST_ENTRY(p->filter_includedirs.head, n)
+ {
+ pkgconf_path_t *pn = n->data;
+ printf("%s ", pn->path);
+ }
+
+ printf("\n");
+ printf("SystemLibraryPaths: ");
+ PKGCONF_FOREACH_LIST_ENTRY(p->filter_libdirs.head, n)
+ {
+ pkgconf_path_t *pn = n->data;
+ printf("%s ", pn->path);
+ }
+
+ printf("\n");
+}
+
+static pkgconf_cross_personality_t *
+deduce_personality(char *argv[])
+{
+ const char *argv0 = argv[0];
+ char *i, *prefix;
+ pkgconf_cross_personality_t *out;
+
+ i = strrchr(argv0, '/');
+ if (i != NULL)
+ argv0 = i + 1;
+
+#if defined(_WIN32) || defined(_WIN64)
+ i = strrchr(argv0, '\\');
+ if (i != NULL)
+ argv0 = i + 1;
+#endif
+
+ i = strstr(argv0, "-pkg");
+ if (i == NULL)
+ return pkgconf_cross_personality_default();
+
+ prefix = pkgconf_strndup(argv0, i - argv0);
+ out = pkgconf_cross_personality_find(prefix);
+ free(prefix);
+ if (out == NULL)
+ return pkgconf_cross_personality_default();
+
+ return out;
+}
+#endif
+
+static void
+unveil_handler(const pkgconf_client_t *client, const char *path, const char *permissions)
+{
+ (void) client;
+
+ if (pkgconf_unveil(path, permissions) == -1)
+ {
+ fprintf(stderr, "pkgconf: unveil failed: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static bool
+unveil_search_paths(const pkgconf_client_t *client, const pkgconf_cross_personality_t *personality)
+{
+ pkgconf_node_t *n;
+
+ if (pkgconf_unveil("/dev/null", "rwc") == -1)
+ return false;
+
+ PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n)
+ {
+ pkgconf_path_t *pn = n->data;
+
+ if (pkgconf_unveil(pn->path, "r") == -1)
+ return false;
+ }
+
+ PKGCONF_FOREACH_LIST_ENTRY(personality->dir_list.head, n)
+ {
+ pkgconf_path_t *pn = n->data;
+
+ if (pkgconf_unveil(pn->path, "r") == -1)
+ return false;
+ }
+
+ pkgconf_client_set_unveil_handler(&pkg_client, unveil_handler);
+
+ return true;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ret;
+ pkgconf_list_t pkgq = PKGCONF_LIST_INITIALIZER;
+ pkgconf_list_t dir_list = PKGCONF_LIST_INITIALIZER;
+ char *builddir;
+ char *sysroot_dir;
+ char *env_traverse_depth;
+ char *required_pkgconfig_version = NULL;
+ char *required_exact_module_version = NULL;
+ char *required_max_module_version = NULL;
+ char *required_module_version = NULL;
+ char *logfile_arg = NULL;
+ char *want_env_prefix = NULL;
+ unsigned int want_client_flags = PKGCONF_PKG_PKGF_NONE;
+ pkgconf_cross_personality_t *personality = NULL;
+ bool opened_error_msgout = false;
+ pkgconf_pkg_t world = {
+ .id = "virtual:world",
+ .realname = "virtual world package",
+ .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL,
+ };
+
+ if (pkgconf_pledge("stdio rpath wpath cpath unveil", NULL) == -1)
+ {
+ fprintf(stderr, "pkgconf: pledge failed: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ want_flags = 0;
+
+#ifdef _WIN32
+ /* When running regression tests in cygwin, and building native
+ * executable, tests fail unless native executable outputs unix
+ * line endings. Come to think of it, this will probably help
+ * real people who use cygwin build environments but native pkgconf, too.
+ */
+ _setmode(fileno(stdout), O_BINARY);
+ _setmode(fileno(stderr), O_BINARY);
+#endif
+
+ struct pkg_option options[] = {
+ { "version", no_argument, &want_flags, PKG_VERSION|PKG_PRINT_ERRORS, },
+ { "about", no_argument, &want_flags, PKG_ABOUT|PKG_PRINT_ERRORS, },
+ { "atleast-version", required_argument, NULL, 2, },
+ { "atleast-pkgconfig-version", required_argument, NULL, 3, },
+ { "libs", no_argument, &want_flags, PKG_LIBS|PKG_PRINT_ERRORS, },
+ { "cflags", no_argument, &want_flags, PKG_CFLAGS|PKG_PRINT_ERRORS, },
+ { "modversion", no_argument, &want_flags, PKG_MODVERSION|PKG_PRINT_ERRORS, },
+ { "variable", required_argument, NULL, 7, },
+ { "exists", no_argument, &want_flags, PKG_EXISTS, },
+ { "print-errors", no_argument, &want_flags, PKG_PRINT_ERRORS, },
+ { "short-errors", no_argument, &want_flags, PKG_SHORT_ERRORS, },
+ { "maximum-traverse-depth", required_argument, NULL, 11, },
+ { "static", no_argument, &want_flags, PKG_STATIC, },
+ { "shared", no_argument, &want_flags, PKG_SHARED, },
+ { "pure", no_argument, &want_flags, PKG_PURE, },
+ { "print-requires", no_argument, &want_flags, PKG_REQUIRES, },
+ { "print-variables", no_argument, &want_flags, PKG_VARIABLES|PKG_PRINT_ERRORS, },
+#ifndef PKGCONF_LITE
+ { "digraph", no_argument, &want_flags, PKG_DIGRAPH, },
+ { "solution", no_argument, &want_flags, PKG_SOLUTION, },
+#endif
+ { "help", no_argument, &want_flags, PKG_HELP, },
+ { "env-only", no_argument, &want_flags, PKG_ENV_ONLY, },
+ { "print-requires-private", no_argument, &want_flags, PKG_REQUIRES_PRIVATE, },
+ { "cflags-only-I", no_argument, &want_flags, PKG_CFLAGS_ONLY_I|PKG_PRINT_ERRORS, },
+ { "cflags-only-other", no_argument, &want_flags, PKG_CFLAGS_ONLY_OTHER|PKG_PRINT_ERRORS, },
+ { "libs-only-L", no_argument, &want_flags, PKG_LIBS_ONLY_LDPATH|PKG_PRINT_ERRORS, },
+ { "libs-only-l", no_argument, &want_flags, PKG_LIBS_ONLY_LIBNAME|PKG_PRINT_ERRORS, },
+ { "libs-only-other", no_argument, &want_flags, PKG_LIBS_ONLY_OTHER|PKG_PRINT_ERRORS, },
+ { "uninstalled", no_argument, &want_flags, PKG_UNINSTALLED, },
+ { "no-uninstalled", no_argument, &want_flags, PKG_NO_UNINSTALLED, },
+ { "keep-system-cflags", no_argument, &want_flags, PKG_KEEP_SYSTEM_CFLAGS, },
+ { "keep-system-libs", no_argument, &want_flags, PKG_KEEP_SYSTEM_LIBS, },
+ { "define-variable", required_argument, NULL, 27, },
+ { "exact-version", required_argument, NULL, 28, },
+ { "max-version", required_argument, NULL, 29, },
+ { "ignore-conflicts", no_argument, &want_flags, PKG_IGNORE_CONFLICTS, },
+ { "errors-to-stdout", no_argument, &want_flags, PKG_ERRORS_ON_STDOUT, },
+ { "silence-errors", no_argument, &want_flags, PKG_SILENCE_ERRORS, },
+ { "list-all", no_argument, &want_flags, PKG_LIST|PKG_PRINT_ERRORS, },
+ { "list-package-names", no_argument, &want_flags, PKG_LIST_PACKAGE_NAMES|PKG_PRINT_ERRORS, },
+#ifndef PKGCONF_LITE
+ { "simulate", no_argument, &want_flags, PKG_SIMULATE, },
+#endif
+ { "no-cache", no_argument, &want_flags, PKG_NO_CACHE, },
+ { "print-provides", no_argument, &want_flags, PKG_PROVIDES, },
+ { "no-provides", no_argument, &want_flags, PKG_NO_PROVIDES, },
+ { "debug", no_argument, &want_flags, PKG_DEBUG|PKG_PRINT_ERRORS, },
+ { "validate", no_argument, &want_flags, PKG_VALIDATE|PKG_PRINT_ERRORS|PKG_ERRORS_ON_STDOUT },
+ { "log-file", required_argument, NULL, 40 },
+ { "path", no_argument, &want_flags, PKG_PATH },
+ { "with-path", required_argument, NULL, 42 },
+ { "prefix-variable", required_argument, NULL, 43 },
+ { "define-prefix", no_argument, &want_flags, PKG_DEFINE_PREFIX },
+ { "relocate", required_argument, NULL, 45 },
+ { "dont-define-prefix", no_argument, &want_flags, PKG_DONT_DEFINE_PREFIX },
+ { "dont-relocate-paths", no_argument, &want_flags, PKG_DONT_RELOCATE_PATHS },
+ { "env", required_argument, NULL, 48 },
+#ifndef PKGCONF_LITE
+ { "msvc-syntax", no_argument, &want_flags, PKG_MSVC_SYNTAX },
+#endif
+ { "fragment-filter", required_argument, NULL, 50 },
+ { "internal-cflags", no_argument, &want_flags, PKG_INTERNAL_CFLAGS },
+#ifndef PKGCONF_LITE
+ { "dump-personality", no_argument, &want_flags, PKG_DUMP_PERSONALITY },
+ { "personality", required_argument, NULL, 53 },
+#endif
+ { "license", no_argument, &want_flags, PKG_DUMP_LICENSE },
+ { "verbose", no_argument, NULL, 55 },
+ { "exists-cflags", no_argument, &want_flags, PKG_EXISTS_CFLAGS },
+ { "fragment-tree", no_argument, &want_flags, PKG_FRAGMENT_TREE },
+ { NULL, 0, NULL, 0 }
+ };
+
+#ifndef PKGCONF_LITE
+ if (getenv("PKG_CONFIG_EARLY_TRACE"))
+ {
+ error_msgout = stderr;
+ pkgconf_client_set_trace_handler(&pkg_client, error_handler, NULL);
+ }
+#endif
+
+ while ((ret = pkg_getopt_long_only(argc, argv, "", options, NULL)) != -1)
+ {
+ switch (ret)
+ {
+ case 2:
+ required_module_version = pkg_optarg;
+ break;
+ case 3:
+ required_pkgconfig_version = pkg_optarg;
+ break;
+ case 7:
+ want_variable = pkg_optarg;
+ break;
+ case 11:
+ maximum_traverse_depth = atoi(pkg_optarg);
+ break;
+ case 27:
+ pkgconf_tuple_define_global(&pkg_client, pkg_optarg);
+ break;
+ case 28:
+ required_exact_module_version = pkg_optarg;
+ break;
+ case 29:
+ required_max_module_version = pkg_optarg;
+ break;
+ case 40:
+ logfile_arg = pkg_optarg;
+ break;
+ case 42:
+ pkgconf_path_prepend(pkg_optarg, &dir_list, true);
+ break;
+ case 43:
+ pkgconf_client_set_prefix_varname(&pkg_client, pkg_optarg);
+ break;
+ case 45:
+ relocate_path(pkg_optarg);
+ return EXIT_SUCCESS;
+ case 48:
+ want_env_prefix = pkg_optarg;
+ break;
+ case 50:
+ want_fragment_filter = pkg_optarg;
+ break;
+#ifndef PKGCONF_LITE
+ case 53:
+ personality = pkgconf_cross_personality_find(pkg_optarg);
+ break;
+#endif
+ case 55:
+ verbosity++;
+ break;
+ case '?':
+ case ':':
+ ret = EXIT_FAILURE;
+ goto out;
+ default:
+ break;
+ }
+ }
+
+ if (personality == NULL) {
+#ifndef PKGCONF_LITE
+ personality = deduce_personality(argv);
+#else
+ personality = pkgconf_cross_personality_default();
+#endif
+ }
+
+#ifndef PKGCONF_LITE
+ if ((want_flags & PKG_DUMP_PERSONALITY) == PKG_DUMP_PERSONALITY)
+ {
+ dump_personality(personality);
+ return EXIT_SUCCESS;
+ }
+#endif
+
+ /* now, bring up the client. settings are preserved since the client is prealloced */
+ pkgconf_client_init(&pkg_client, error_handler, NULL, personality);
+
+ /* unveil the entire search path now that we have loaded the personality data. */
+ if (!unveil_search_paths(&pkg_client, personality))
+ {
+ fprintf(stderr, "pkgconf: unveil failed: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+#ifndef PKGCONF_LITE
+ if ((want_flags & PKG_MSVC_SYNTAX) == PKG_MSVC_SYNTAX || getenv("PKG_CONFIG_MSVC_SYNTAX") != NULL)
+ want_render_ops = msvc_renderer_get();
+#endif
+
+ if ((env_traverse_depth = getenv("PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH")) != NULL)
+ maximum_traverse_depth = atoi(env_traverse_depth);
+
+ if ((want_flags & PKG_PRINT_ERRORS) != PKG_PRINT_ERRORS)
+ want_flags |= (PKG_SILENCE_ERRORS);
+
+ if ((want_flags & PKG_SILENCE_ERRORS) == PKG_SILENCE_ERRORS && !getenv("PKG_CONFIG_DEBUG_SPEW"))
+ want_flags |= (PKG_SILENCE_ERRORS);
+ else
+ want_flags &= ~(PKG_SILENCE_ERRORS);
+
+ if (getenv("PKG_CONFIG_DONT_RELOCATE_PATHS"))
+ want_flags |= (PKG_DONT_RELOCATE_PATHS);
+
+ if ((want_flags & PKG_VALIDATE) == PKG_VALIDATE || (want_flags & PKG_DEBUG) == PKG_DEBUG)
+ pkgconf_client_set_warn_handler(&pkg_client, error_handler, NULL);
+
+#ifndef PKGCONF_LITE
+ if ((want_flags & PKG_DEBUG) == PKG_DEBUG)
+ pkgconf_client_set_trace_handler(&pkg_client, error_handler, NULL);
+#endif
+
+ pkgconf_path_prepend_list(&pkg_client.dir_list, &dir_list);
+ pkgconf_path_free(&dir_list);
+
+ if ((want_flags & PKG_ABOUT) == PKG_ABOUT)
+ {
+ about();
+
+ ret = EXIT_SUCCESS;
+ goto out;
+ }
+
+ if ((want_flags & PKG_VERSION) == PKG_VERSION)
+ {
+ version();
+
+ ret = EXIT_SUCCESS;
+ goto out;
+ }
+
+ if ((want_flags & PKG_HELP) == PKG_HELP)
+ {
+ usage();
+
+ ret = EXIT_SUCCESS;
+ goto out;
+ }
+
+ if (getenv("PKG_CONFIG_FDO_SYSROOT_RULES"))
+ want_client_flags |= PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES;
+
+ if (getenv("PKG_CONFIG_PKGCONF1_SYSROOT_RULES"))
+ want_client_flags |= PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES;
+
+ if ((want_flags & PKG_SHORT_ERRORS) == PKG_SHORT_ERRORS)
+ want_client_flags |= PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS;
+
+ if ((want_flags & PKG_DONT_RELOCATE_PATHS) == PKG_DONT_RELOCATE_PATHS)
+ want_client_flags |= PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS;
+
+ error_msgout = stderr;
+ if ((want_flags & PKG_ERRORS_ON_STDOUT) == PKG_ERRORS_ON_STDOUT)
+ error_msgout = stdout;
+ if ((want_flags & PKG_SILENCE_ERRORS) == PKG_SILENCE_ERRORS) {
+ error_msgout = fopen(PATH_DEV_NULL, "w");
+ opened_error_msgout = true;
+ }
+
+ if ((want_flags & PKG_IGNORE_CONFLICTS) == PKG_IGNORE_CONFLICTS || getenv("PKG_CONFIG_IGNORE_CONFLICTS") != NULL)
+ want_client_flags |= PKGCONF_PKG_PKGF_SKIP_CONFLICTS;
+
+ if ((want_flags & PKG_STATIC) == PKG_STATIC || personality->want_default_static)
+ want_client_flags |= (PKGCONF_PKG_PKGF_SEARCH_PRIVATE | PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS);
+
+ if ((want_flags & PKG_SHARED) == PKG_SHARED)
+ want_client_flags &= ~(PKGCONF_PKG_PKGF_SEARCH_PRIVATE | PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS);
+
+ /* if --static and --pure are both specified, then disable merge-back.
+ * this allows for a --static which searches private modules, but has the same fragment behaviour as if
+ * --static were disabled. see <https://github.com/pkgconf/pkgconf/issues/83> for rationale.
+ */
+ if ((want_flags & PKG_PURE) == PKG_PURE || getenv("PKG_CONFIG_PURE_DEPGRAPH") != NULL || personality->want_default_pure)
+ want_client_flags &= ~PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS;
+
+ if ((want_flags & PKG_ENV_ONLY) == PKG_ENV_ONLY)
+ want_client_flags |= PKGCONF_PKG_PKGF_ENV_ONLY;
+
+ if ((want_flags & PKG_NO_CACHE) == PKG_NO_CACHE)
+ want_client_flags |= PKGCONF_PKG_PKGF_NO_CACHE;
+
+/* On Windows we want to always redefine the prefix by default
+ * but allow that behavior to be manually disabled */
+#if !defined(_WIN32) && !defined(_WIN64)
+ if ((want_flags & PKG_DEFINE_PREFIX) == PKG_DEFINE_PREFIX || getenv("PKG_CONFIG_RELOCATE_PATHS") != NULL)
+#endif
+ want_client_flags |= PKGCONF_PKG_PKGF_REDEFINE_PREFIX;
+
+ if ((want_flags & PKG_NO_UNINSTALLED) == PKG_NO_UNINSTALLED || getenv("PKG_CONFIG_DISABLE_UNINSTALLED") != NULL)
+ want_client_flags |= PKGCONF_PKG_PKGF_NO_UNINSTALLED;
+
+ if ((want_flags & PKG_NO_PROVIDES) == PKG_NO_PROVIDES)
+ want_client_flags |= PKGCONF_PKG_PKGF_SKIP_PROVIDES;
+
+ if ((want_flags & PKG_DONT_DEFINE_PREFIX) == PKG_DONT_DEFINE_PREFIX || getenv("PKG_CONFIG_DONT_DEFINE_PREFIX") != NULL)
+ want_client_flags &= ~PKGCONF_PKG_PKGF_REDEFINE_PREFIX;
+
+ if ((want_flags & PKG_INTERNAL_CFLAGS) == PKG_INTERNAL_CFLAGS)
+ want_client_flags |= PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS;
+
+ /* if these selectors are used, it means that we are querying metadata.
+ * so signal to libpkgconf that we only want to walk the flattened dependency set.
+ */
+ if ((want_flags & PKG_MODVERSION) == PKG_MODVERSION ||
+ (want_flags & PKG_REQUIRES) == PKG_REQUIRES ||
+ (want_flags & PKG_REQUIRES_PRIVATE) == PKG_REQUIRES_PRIVATE ||
+ (want_flags & PKG_PROVIDES) == PKG_PROVIDES ||
+ (want_flags & PKG_VARIABLES) == PKG_VARIABLES ||
+ (want_flags & PKG_PATH) == PKG_PATH ||
+ want_variable != NULL)
+ maximum_traverse_depth = 1;
+
+ /* if we are asking for a variable, path or list of variables, this only makes sense
+ * for a single package.
+ */
+ if ((want_flags & PKG_VARIABLES) == PKG_VARIABLES ||
+ (want_flags & PKG_PATH) == PKG_PATH ||
+ want_variable != NULL)
+ maximum_package_count = 1;
+
+ if (getenv("PKG_CONFIG_ALLOW_SYSTEM_CFLAGS") != NULL)
+ want_flags |= PKG_KEEP_SYSTEM_CFLAGS;
+
+ if (getenv("PKG_CONFIG_ALLOW_SYSTEM_LIBS") != NULL)
+ want_flags |= PKG_KEEP_SYSTEM_LIBS;
+
+ if ((builddir = getenv("PKG_CONFIG_TOP_BUILD_DIR")) != NULL)
+ pkgconf_client_set_buildroot_dir(&pkg_client, builddir);
+
+ if ((want_flags & PKG_REQUIRES_PRIVATE) == PKG_REQUIRES_PRIVATE ||
+ (want_flags & PKG_CFLAGS))
+ {
+ want_client_flags |= PKGCONF_PKG_PKGF_SEARCH_PRIVATE;
+ }
+
+ if ((sysroot_dir = getenv("PKG_CONFIG_SYSROOT_DIR")) != NULL)
+ {
+ const char *destdir;
+
+ pkgconf_client_set_sysroot_dir(&pkg_client, sysroot_dir);
+
+ if ((destdir = getenv("DESTDIR")) != NULL)
+ {
+ if (!strcmp(destdir, sysroot_dir))
+ want_client_flags |= PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES;
+ }
+ }
+
+ /* we have determined what features we want most likely. in some cases, we override later. */
+ pkgconf_client_set_flags(&pkg_client, want_client_flags);
+
+ /* at this point, want_client_flags should be set, so build the dir list */
+ pkgconf_client_dir_list_build(&pkg_client, personality);
+
+ /* preload any files in PKG_CONFIG_PRELOADED_FILES */
+ pkgconf_client_preload_from_environ(&pkg_client, "PKG_CONFIG_PRELOADED_FILES");
+
+ if (required_pkgconfig_version != NULL)
+ {
+ if (pkgconf_compare_version(PACKAGE_VERSION, required_pkgconfig_version) >= 0)
+ ret = EXIT_SUCCESS;
+ else
+ ret = EXIT_FAILURE;
+
+ goto out;
+ }
+
+ if ((want_flags & PKG_LIST) == PKG_LIST)
+ {
+ pkgconf_scan_all(&pkg_client, NULL, print_list_entry);
+ ret = EXIT_SUCCESS;
+ goto out;
+ }
+
+ if ((want_flags & PKG_LIST_PACKAGE_NAMES) == PKG_LIST_PACKAGE_NAMES)
+ {
+ pkgconf_scan_all(&pkg_client, NULL, print_package_entry);
+ ret = EXIT_SUCCESS;
+ goto out;
+ }
+
+ if (logfile_arg == NULL)
+ logfile_arg = getenv("PKG_CONFIG_LOG");
+
+ if (logfile_arg != NULL)
+ {
+ if (pkgconf_unveil(logfile_arg, "rwc") == -1)
+ {
+ fprintf(stderr, "pkgconf: unveil failed: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ logfile_out = fopen(logfile_arg, "w");
+ pkgconf_audit_set_log(&pkg_client, logfile_out);
+ }
+
+ if (required_module_version != NULL)
+ {
+ pkgconf_pkg_t *pkg = NULL;
+ pkgconf_node_t *node;
+ pkgconf_list_t deplist = PKGCONF_LIST_INITIALIZER;
+
+ while (argv[pkg_optind])
+ {
+ pkgconf_dependency_parse_str(&pkg_client, &deplist, argv[pkg_optind], 0);
+ pkg_optind++;
+ }
+
+ PKGCONF_FOREACH_LIST_ENTRY(deplist.head, node)
+ {
+ pkgconf_dependency_t *pkgiter = node->data;
+
+ pkg = pkgconf_pkg_find(&pkg_client, pkgiter->package);
+ if (pkg == NULL)
+ {
+ if (want_flags & PKG_PRINT_ERRORS)
+ pkgconf_error(&pkg_client, "Package '%s' was not found\n", pkgiter->package);
+
+ ret = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+ if (pkgconf_compare_version(pkg->version, required_module_version) >= 0)
+ {
+ ret = EXIT_SUCCESS;
+ goto cleanup;
+ }
+ }
+
+ ret = EXIT_FAILURE;
+cleanup:
+ if (pkg != NULL)
+ pkgconf_pkg_unref(&pkg_client, pkg);
+ pkgconf_dependency_free(&deplist);
+ goto out;
+ }
+ else if (required_exact_module_version != NULL)
+ {
+ pkgconf_pkg_t *pkg = NULL;
+ pkgconf_node_t *node;
+ pkgconf_list_t deplist = PKGCONF_LIST_INITIALIZER;
+
+ while (argv[pkg_optind])
+ {
+ pkgconf_dependency_parse_str(&pkg_client, &deplist, argv[pkg_optind], 0);
+ pkg_optind++;
+ }
+
+ PKGCONF_FOREACH_LIST_ENTRY(deplist.head, node)
+ {
+ pkgconf_dependency_t *pkgiter = node->data;
+
+ pkg = pkgconf_pkg_find(&pkg_client, pkgiter->package);
+ if (pkg == NULL)
+ {
+ if (want_flags & PKG_PRINT_ERRORS)
+ pkgconf_error(&pkg_client, "Package '%s' was not found\n", pkgiter->package);
+
+ ret = EXIT_FAILURE;
+ goto cleanup2;
+ }
+
+ if (pkgconf_compare_version(pkg->version, required_exact_module_version) == 0)
+ {
+ ret = EXIT_SUCCESS;
+ goto cleanup2;
+ }
+ }
+
+ ret = EXIT_FAILURE;
+cleanup2:
+ if (pkg != NULL)
+ pkgconf_pkg_unref(&pkg_client, pkg);
+ pkgconf_dependency_free(&deplist);
+ goto out;
+ }
+ else if (required_max_module_version != NULL)
+ {
+ pkgconf_pkg_t *pkg = NULL;
+ pkgconf_node_t *node;
+ pkgconf_list_t deplist = PKGCONF_LIST_INITIALIZER;
+
+ while (argv[pkg_optind])
+ {
+ pkgconf_dependency_parse_str(&pkg_client, &deplist, argv[pkg_optind], 0);
+ pkg_optind++;
+ }
+
+ PKGCONF_FOREACH_LIST_ENTRY(deplist.head, node)
+ {
+ pkgconf_dependency_t *pkgiter = node->data;
+
+ pkg = pkgconf_pkg_find(&pkg_client, pkgiter->package);
+ if (pkg == NULL)
+ {
+ if (want_flags & PKG_PRINT_ERRORS)
+ pkgconf_error(&pkg_client, "Package '%s' was not found\n", pkgiter->package);
+
+ ret = EXIT_FAILURE;
+ goto cleanup3;
+ }
+
+ if (pkgconf_compare_version(pkg->version, required_max_module_version) <= 0)
+ {
+ ret = EXIT_SUCCESS;
+ goto cleanup3;
+ }
+ }
+
+ ret = EXIT_FAILURE;
+cleanup3:
+ if (pkg != NULL)
+ pkgconf_pkg_unref(&pkg_client, pkg);
+ pkgconf_dependency_free(&deplist);
+ goto out;
+ }
+
+ while (1)
+ {
+ char *package = argv[pkg_optind];
+ char *end;
+
+ if (package == NULL)
+ break;
+
+ /* check if there is a limit to the number of packages allowed to be included, if so and we have hit
+ * the limit, stop adding packages to the queue.
+ */
+ if (maximum_package_count > 0 && pkgq.length >= maximum_package_count)
+ break;
+
+ while (isspace((unsigned char)package[0]))
+ package++;
+
+ /* skip empty packages */
+ if (package[0] == '\0') {
+ pkg_optind++;
+ continue;
+ }
+
+ end = package + strlen(package) - 1;
+ while(end > package && isspace((unsigned char)end[0])) end--;
+ end[1] = '\0';
+
+ if (argv[pkg_optind + 1] == NULL || !PKGCONF_IS_OPERATOR_CHAR(*(argv[pkg_optind + 1])))
+ {
+ pkgconf_queue_push(&pkgq, package);
+ pkg_optind++;
+ }
+ else if (argv[pkg_optind + 2] == NULL)
+ {
+ char packagebuf[PKGCONF_BUFSIZE];
+
+ snprintf(packagebuf, sizeof packagebuf, "%s %s", package, argv[pkg_optind + 1]);
+ pkg_optind += 2;
+
+ pkgconf_queue_push(&pkgq, packagebuf);
+ }
+ else
+ {
+ char packagebuf[PKGCONF_BUFSIZE];
+
+ snprintf(packagebuf, sizeof packagebuf, "%s %s %s", package, argv[pkg_optind + 1], argv[pkg_optind + 2]);
+ pkg_optind += 3;
+
+ pkgconf_queue_push(&pkgq, packagebuf);
+ }
+ }
+
+ if (pkgq.head == NULL)
+ {
+ fprintf(stderr, "Please specify at least one package name on the command line.\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ ret = EXIT_SUCCESS;
+
+ if (!pkgconf_queue_solve(&pkg_client, &pkgq, &world, maximum_traverse_depth))
+ {
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ /* we shouldn't need to unveil any more filesystem accesses from this point, so lock it down */
+ if (pkgconf_unveil(NULL, NULL) == -1)
+ {
+ fprintf(stderr, "pkgconf: unveil lockdown failed: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+#ifndef PKGCONF_LITE
+ if ((want_flags & PKG_SIMULATE) == PKG_SIMULATE)
+ {
+ want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
+
+ pkgconf_client_set_flags(&pkg_client, want_client_flags | PKGCONF_PKG_PKGF_SKIP_ERRORS);
+ apply_simulate(&pkg_client, &world, NULL, -1);
+ }
+#endif
+
+ if ((want_flags & PKG_VALIDATE) == PKG_VALIDATE)
+ goto out;
+
+ if ((want_flags & PKG_DUMP_LICENSE) == PKG_DUMP_LICENSE)
+ {
+ apply_license(&pkg_client, &world, &ret, 2);
+ goto out;
+ }
+
+ if ((want_flags & PKG_UNINSTALLED) == PKG_UNINSTALLED)
+ {
+ ret = EXIT_FAILURE;
+ apply_uninstalled(&pkg_client, &world, &ret, 2);
+ goto out;
+ }
+
+ if (want_env_prefix != NULL)
+ {
+ apply_env(&pkg_client, &world, want_env_prefix, 2);
+ goto out;
+ }
+
+ if ((want_flags & PKG_PROVIDES) == PKG_PROVIDES)
+ {
+ want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
+ apply_provides(&pkg_client, &world, NULL, 2);
+ }
+
+#ifndef PKGCONF_LITE
+ if ((want_flags & PKG_DIGRAPH) == PKG_DIGRAPH)
+ {
+ want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
+ apply_digraph(&pkg_client, &world, &pkgq, 2);
+ }
+
+ if ((want_flags & PKG_SOLUTION) == PKG_SOLUTION)
+ {
+ want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
+ apply_print_solution(&pkg_client, &world, NULL, 2);
+ }
+#endif
+
+ if ((want_flags & PKG_MODVERSION) == PKG_MODVERSION)
+ {
+ want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
+ apply_modversion(&pkg_client, &world, &pkgq, 2);
+ }
+
+ if ((want_flags & PKG_PATH) == PKG_PATH)
+ {
+ want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
+
+ pkgconf_client_set_flags(&pkg_client, want_client_flags | PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL);
+ apply_path(&pkg_client, &world, NULL, 2);
+ }
+
+ if ((want_flags & PKG_VARIABLES) == PKG_VARIABLES)
+ {
+ want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
+ apply_variables(&pkg_client, &world, NULL, 2);
+ }
+
+ if (want_variable)
+ {
+ want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
+
+ pkgconf_client_set_flags(&pkg_client, want_client_flags | PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL);
+ apply_variable(&pkg_client, &world, want_variable, 2);
+ }
+
+ if ((want_flags & PKG_REQUIRES) == PKG_REQUIRES)
+ {
+ want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
+ apply_requires(&pkg_client, &world, NULL, 2);
+ }
+
+ if ((want_flags & PKG_REQUIRES_PRIVATE) == PKG_REQUIRES_PRIVATE)
+ {
+ want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
+
+ apply_requires_private(&pkg_client, &world, NULL, 2);
+ }
+
+ if ((want_flags & PKG_FRAGMENT_TREE))
+ {
+ want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
+
+ apply_fragment_tree(&pkg_client, &world, NULL, 2);
+ }
+
+ if ((want_flags & PKG_CFLAGS))
+ {
+ apply_cflags(&pkg_client, &world, NULL, 2);
+ }
+
+ if ((want_flags & PKG_LIBS))
+ {
+ if (want_flags & PKG_CFLAGS)
+ printf(" ");
+
+ if (!(want_flags & PKG_STATIC))
+ pkgconf_client_set_flags(&pkg_client, pkg_client.flags & ~PKGCONF_PKG_PKGF_SEARCH_PRIVATE);
+
+ apply_libs(&pkg_client, &world, NULL, 2);
+ }
+
+ if (want_flags & (PKG_CFLAGS|PKG_LIBS))
+ printf("\n");
+
+out:
+ pkgconf_solution_free(&pkg_client, &world);
+ pkgconf_queue_free(&pkgq);
+ pkgconf_cross_personality_deinit(personality);
+ pkgconf_client_deinit(&pkg_client);
+
+ if (logfile_out != NULL)
+ fclose(logfile_out);
+ if (opened_error_msgout)
+ fclose(error_msgout);
+
+ return ret;
+}
diff --git a/cli/renderer-msvc.c b/cli/renderer-msvc.c
new file mode 100644
index 000000000000..8e31699bbca4
--- /dev/null
+++ b/cli/renderer-msvc.c
@@ -0,0 +1,172 @@
+/*
+ * renderer-msvc.c
+ * MSVC library syntax renderer
+ *
+ * Copyright (c) 2017 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <libpkgconf/libpkgconf.h>
+#include "renderer-msvc.h"
+
+static inline bool
+fragment_should_quote(const pkgconf_fragment_t *frag)
+{
+ const char *src;
+
+ if (frag->data == NULL)
+ return false;
+
+ for (src = frag->data; *src; src++)
+ {
+ if (((*src < ' ') ||
+ (*src >= (' ' + (frag->children.head != NULL ? 1 : 0)) && *src < '$') ||
+ (*src > '$' && *src < '(') ||
+ (*src > ')' && *src < '+') ||
+ (*src > ':' && *src < '=') ||
+ (*src > '=' && *src < '@') ||
+ (*src > 'Z' && *src < '^') ||
+ (*src == '`') ||
+ (*src > 'z' && *src < '~') ||
+ (*src > '~')))
+ return true;
+ }
+
+ return false;
+}
+
+static inline size_t
+fragment_len(const pkgconf_fragment_t *frag)
+{
+ size_t len = 1;
+
+ if (frag->type)
+ len += 2;
+
+ if (frag->data != NULL)
+ {
+ len += strlen(frag->data);
+
+ if (fragment_should_quote(frag))
+ len += 2;
+ }
+
+ return len;
+}
+
+static inline bool
+allowed_fragment(const pkgconf_fragment_t *frag)
+{
+ return !(!frag->type || frag->data == NULL || strchr("DILl", frag->type) == NULL);
+}
+
+static size_t
+msvc_renderer_render_len(const pkgconf_list_t *list, bool escape)
+{
+ (void) escape;
+
+ size_t out = 1; /* trailing nul */
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
+ {
+ const pkgconf_fragment_t *frag = node->data;
+
+ if (!allowed_fragment(frag))
+ continue;
+
+ switch (frag->type)
+ {
+ case 'L':
+ out += 9; /* "/libpath:" */
+ break;
+ case 'l':
+ out += 4; /* ".lib" */
+ break;
+ default:
+ break;
+ }
+
+ out += fragment_len(frag);
+ }
+
+ return out;
+}
+
+static void
+msvc_renderer_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape)
+{
+ pkgconf_node_t *node;
+ char *bptr = buf;
+
+ memset(buf, 0, buflen);
+
+ PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
+ {
+ const pkgconf_fragment_t *frag = node->data;
+ size_t buf_remaining = buflen - (bptr - buf);
+ size_t cnt;
+
+ if (!allowed_fragment(frag))
+ continue;
+
+ if (fragment_len(frag) > buf_remaining)
+ break;
+
+ switch(frag->type) {
+ case 'D':
+ case 'I':
+ *bptr++ = '/';
+ *bptr++ = frag->type;
+ break;
+ case 'L':
+ cnt = pkgconf_strlcpy(bptr, "/libpath:", buf_remaining);
+ bptr += cnt;
+ buf_remaining -= cnt;
+ break;
+ }
+
+ escape = fragment_should_quote(frag);
+
+ if (escape)
+ *bptr++ = '"';
+
+ cnt = pkgconf_strlcpy(bptr, frag->data, buf_remaining);
+ bptr += cnt;
+ buf_remaining -= cnt;
+
+ if (frag->type == 'l')
+ {
+ cnt = pkgconf_strlcpy(bptr, ".lib", buf_remaining);
+ bptr += cnt;
+ }
+
+ if (escape)
+ *bptr++ = '"';
+
+ *bptr++ = ' ';
+ }
+
+ *bptr = '\0';
+}
+
+static const pkgconf_fragment_render_ops_t msvc_renderer_ops = {
+ .render_len = msvc_renderer_render_len,
+ .render_buf = msvc_renderer_render_buf
+};
+
+const pkgconf_fragment_render_ops_t *
+msvc_renderer_get(void)
+{
+ return &msvc_renderer_ops;
+}
diff --git a/cli/renderer-msvc.h b/cli/renderer-msvc.h
new file mode 100644
index 000000000000..2a095fdc36b4
--- /dev/null
+++ b/cli/renderer-msvc.h
@@ -0,0 +1,23 @@
+/*
+ * renderer-msvc.h
+ * MSVC library syntax renderer header
+ *
+ * Copyright (c) 2017 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#ifndef RENDERER_MSVC_H
+#define RENDERER_MSVC_H
+
+#include <libpkgconf/libpkgconf.h>
+
+const pkgconf_fragment_render_ops_t *msvc_renderer_get(void);
+
+#endif
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 000000000000..3fee9bd444e0
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,66 @@
+dnl configure.ac
+dnl m4 preprocessor script for autotools
+dnl
+dnl Copyright (c) 2011, 2012, 2013, 2014 pkgconf authors (see AUTHORS).
+dnl
+dnl Permission to use, copy, modify, and/or distribute this software for any
+dnl purpose with or without fee is hereby granted, provided that the above
+dnl copyright notice and this permission notice appear in all copies.
+dnl
+dnl This software is provided 'as is' and without any warranty, express or
+dnl implied. In no event shall the authors be liable for any damages arising
+dnl from the use of this software.
+
+AC_PREREQ([2.71])
+AC_INIT([pkgconf],[2.5.1],[https://github.com/pkgconf/pkgconf/issues/new])
+AC_CONFIG_SRCDIR([cli/main.c])
+AC_CONFIG_MACRO_DIR([m4])
+AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
+AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS="$CFLAGS -Wextra"])
+AX_CHECK_COMPILE_FLAG([-Wformat=2], [CFLAGS="$CFLAGS -Wformat=2"])
+AX_CHECK_COMPILE_FLAG([-std=gnu99], [CFLAGS="$CFLAGS -std=gnu99"], [
+ AX_CHECK_COMPILE_FLAG([-std=c99], [CFLAGS="$CFLAGS -std=c99"])
+])
+AC_CONFIG_HEADERS([libpkgconf/config.h])
+AC_CHECK_DECLS([strlcpy, strlcat, strndup], [], [], [[#include <string.h>]])
+AC_CHECK_DECLS([pledge, unveil], [], [], [[#include <unistd.h>]])
+AC_CHECK_DECLS([reallocarray])
+AC_CHECK_HEADERS([sys/stat.h])
+AM_INIT_AUTOMAKE([foreign dist-xz subdir-objects])
+AM_SILENT_RULES([yes])
+LT_INIT
+
+AC_SYS_LARGEFILE
+
+AC_ARG_WITH([personality-dir],[AS_HELP_STRING([--with-personality-dir],[specify
+ the place where cross-compile personality files will be found])],
+ PERSONALITY_PATH="$withval",
+ PERSONALITY_PATH="${datadir}/pkgconfig/personality.d:${sysconfdir}/pkgconfig/personality.d")
+
+AC_SUBST([PERSONALITY_PATH])
+
+AC_ARG_WITH([pkg-config-dir],[AS_HELP_STRING([--with-pkg-config-dir],[specify
+ the place where pc files will be found])],PKG_DEFAULT_PATH="$withval",
+ PKG_DEFAULT_PATH="${libdir}/pkgconfig:${datadir}/pkgconfig")
+
+AC_SUBST([PKG_DEFAULT_PATH])
+
+AC_ARG_WITH([system-libdir],[AS_HELP_STRING([--with-system-libdir],[specify the
+ system library directory (default LIBDIR)])],
+ SYSTEM_LIBDIR="$withval", SYSTEM_LIBDIR="${libdir}")
+
+AC_SUBST([SYSTEM_LIBDIR])
+
+AC_ARG_WITH([system-includedir],[AS_HELP_STRING([--with-system-includedir],[specify the
+ system include directory (default INCLUDEDIR)])],
+ SYSTEM_INCLUDEDIR="$withval", SYSTEM_INCLUDEDIR="${includedir}")
+
+AC_SUBST([SYSTEM_INCLUDEDIR])
+
+AC_PROG_CPP
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LN_S
+
+AC_CONFIG_FILES([Makefile Kyuafile libpkgconf.pc tests/Kyuafile tests/test_env.sh])
+AC_OUTPUT
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 000000000000..ca608c7f1065
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,339 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# pkgconf documentation build configuration file, created by
+# sphinx-quickstart on Sat Dec 10 16:54:40 2016.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The encoding of source files.
+#
+# source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'pkgconf'
+copyright = '2016, pkgconf authors'
+author = 'pkgconf authors'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '1.1.0'
+# The full version, including alpha/beta/rc tags.
+release = '1.1.0'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = 'en'
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#
+# today = ''
+#
+# Else, today_fmt is used as the format for a strftime call.
+#
+# today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#
+# add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#
+# add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#
+# show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+# modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+# html_theme_path = []
+
+# The name for this set of Sphinx documents.
+# "<project> v<release> documentation" by default.
+#
+# html_title = 'pkgconf v1.1.0'
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#
+# html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#
+# html_logo = None
+
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#
+# html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#
+# html_extra_path = []
+
+# If not None, a 'Last updated on:' timestamp is inserted at every page
+# bottom, using the given strftime format.
+# The empty string is equivalent to '%b %d, %Y'.
+#
+# html_last_updated_fmt = None
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#
+# html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#
+# html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#
+# html_additional_pages = {}
+
+# If false, no module index is generated.
+#
+# html_domain_indices = True
+
+# If false, no index is generated.
+#
+# html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#
+# html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#
+# html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#
+# html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#
+# html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#
+# html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+# html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh'
+#
+# html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# 'ja' uses this config value.
+# 'zh' user can custom change `jieba` dictionary path.
+#
+# html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#
+# html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'pkgconfdoc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'pkgconf.tex', 'pkgconf Documentation',
+ 'pkgconf authors', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#
+# latex_use_parts = False
+
+# If true, show page references after internal links.
+#
+# latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#
+# latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#
+# latex_appendices = []
+
+# It false, will not define \strong, \code, itleref, \crossref ... but only
+# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
+# packages.
+#
+# latex_keep_old_macro_names = True
+
+# If false, no module index is generated.
+#
+# latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'pkgconf', 'pkgconf Documentation',
+ [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#
+# man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'pkgconf', 'pkgconf Documentation',
+ author, 'pkgconf', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+#
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#
+# texinfo_no_detailmenu = False
diff --git a/doc/extract.py b/doc/extract.py
new file mode 100644
index 000000000000..10ea62037869
--- /dev/null
+++ b/doc/extract.py
@@ -0,0 +1,149 @@
+# derived from https://github.com/jeanralphaviles/comment_parser/blob/master/comment_parser/parsers/c_parser.py
+# MIT license - https://github.com/jeanralphaviles/comment_parser/blob/master/LICENSE
+
+
+class Comment:
+ def __init__(self, comment, line, multiline):
+ self.comment = comment
+ self.line = line
+ self.multiline = multiline
+
+ def __repr__(self):
+ return "Comment(comment=%r, line=%r, multiline=%r)" % (self.comment, self.line, self.multiline)
+
+ @property
+ def clean_text(self):
+ if not self.multiline:
+ return self.comment.strip()
+
+ lines = self.comment.splitlines()
+ cleanlines = []
+ for line in lines:
+ if line[0:3] == ' * ':
+ cleanlines.append(line[3:])
+ elif len(line) == 2:
+ cleanlines.append('')
+ return '\n'.join(cleanlines)
+
+ @property
+ def doc_text(self):
+ text = self.clean_text
+ if '!doc' in text[0:4]:
+ return text[5:]
+ return None
+
+
+class FileError(Exception):
+ pass
+
+
+class UnterminatedCommentError(Exception):
+ pass
+
+
+def extract_comments(filename):
+ """Extracts a list of comments from the given C family source file.
+ Comments are represented with the Comment class found in the common module.
+ C family comments come in two forms, single and multi-line comments.
+ - Single-line comments begin with '//' and continue to the end of line.
+ - Multi-line comments begin with '/*' and end with '*/' and can span
+ multiple lines of code. If a multi-line comment does not terminate
+ before EOF is reached, then an exception is raised.
+ Note that this doesn't take language-specific preprocessor directives into
+ consideration.
+ Args:
+ filename: String name of the file to extract comments from.
+ Returns:
+ Python list of Comment objects in the order that they appear in the file.
+ Raises:
+ FileError: File was unable to be open or read.
+ UnterminatedCommentError: Encountered an unterminated multi-line
+ comment.
+ """
+ try:
+ with open(filename, 'r') as source_file:
+ state = 0
+ current_comment = ''
+ comments = []
+ line_counter = 1
+ comment_start = 1
+ while True:
+ char = source_file.read(1)
+ if not char:
+ if state == 3 or state == 4:
+ raise UnterminatedCommentError()
+ if state == 2:
+ # Was in single line comment. Create comment.
+ comment = Comment(current_comment, line_counter, False)
+ comments.append(comment)
+ return comments
+ if state == 0:
+ # Waiting for comment start character or beginning of
+ # string.
+ if char == '/':
+ state = 1
+ elif char == '"':
+ state = 5
+ elif state == 1:
+ # Found comment start character, classify next character and
+ # determine if single or multiline comment.
+ if char == '/':
+ state = 2
+ elif char == '*':
+ comment_start = line_counter
+ state = 3
+ else:
+ state = 0
+ elif state == 2:
+ # In single line comment, read characters until EOL.
+ if char == '\n':
+ comment = Comment(current_comment, line_counter, False)
+ comments.append(comment)
+ current_comment = ''
+ state = 0
+ else:
+ current_comment += char
+ elif state == 3:
+ # In multi-line comment, add characters until '*'
+ # encountered.
+ if char == '*':
+ state = 4
+ else:
+ current_comment += char
+ elif state == 4:
+ # In multi-line comment with asterisk found. Determine if
+ # comment is ending.
+ if char == '/':
+ comment = Comment(
+ current_comment, comment_start, True)
+ comments.append(comment)
+ current_comment = ''
+ state = 0
+ else:
+ current_comment += '*'
+ # Care for multiple '*' in a row
+ if char != '*':
+ current_comment += char
+ state = 3
+ elif state == 5:
+ # In string literal, expect literal end or escape char.
+ if char == '"':
+ state = 0
+ elif char == '\\':
+ state = 6
+ elif state == 6:
+ # In string literal, escaping current char.
+ state = 5
+ if char == '\n':
+ line_counter += 1
+ except OSError as exception:
+ raise FileError(str(exception))
+
+
+if __name__ == '__main__':
+ import sys
+ from pprint import pprint
+
+ comments = [comment for comment in extract_comments(sys.argv[1]) if comment.doc_text]
+ for comment in comments:
+ print(comment.doc_text)
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 000000000000..b0c7c4bd24b9
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,14 @@
+pkgconf
+=======
+
+.. toctree::
+ :maxdepth: 2
+
+ libpkgconf
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`search`
+
diff --git a/doc/libpkgconf-argvsplit.rst b/doc/libpkgconf-argvsplit.rst
new file mode 100644
index 000000000000..94ec6d93f852
--- /dev/null
+++ b/doc/libpkgconf-argvsplit.rst
@@ -0,0 +1,23 @@
+
+libpkgconf `argvsplit` module
+=============================
+
+This is a lowlevel module which provides parsing of strings into argument vectors,
+similar to what a shell would do.
+
+.. c:function:: void pkgconf_argv_free(char **argv)
+
+ Frees an argument vector.
+
+ :param char** argv: The argument vector to free.
+ :return: nothing
+
+.. c:function:: int pkgconf_argv_split(const char *src, int *argc, char ***argv)
+
+ Splits a string into an argument vector.
+
+ :param char* src: The string to split.
+ :param int* argc: A pointer to an integer to store the argument count.
+ :param char*** argv: A pointer to a pointer for an argument vector.
+ :return: 0 on success, -1 on error.
+ :rtype: int
diff --git a/doc/libpkgconf-audit.rst b/doc/libpkgconf-audit.rst
new file mode 100644
index 000000000000..0baaba308186
--- /dev/null
+++ b/doc/libpkgconf-audit.rst
@@ -0,0 +1,35 @@
+
+libpkgconf `audit` module
+=========================
+
+The libpkgconf `audit` module contains the functions related to attaching an audit log file
+to a ``pkgconf_client_t`` object.
+
+The audit log format is the same as the output generated by the ``PKG_CONFIG_LOG`` environment
+variable.
+
+.. c:function:: void pkgconf_audit_set_log(pkgconf_client_t *client, FILE *auditf)
+
+ Sets the audit log file pointer on `client` to `auditf`.
+ The callee is responsible for closing any previous log files.
+
+ :param pkgconf_client_t* client: The client object to modify.
+ :param FILE* auditf: The file pointer for the already open log file.
+ :return: nothing
+
+.. c:function:: void pkgconf_audit_log(pkgconf_client_t *client, const char *format, ...)
+
+ Logs a message to the opened audit log (if any).
+
+ :param pkgconf_client_t* client: The client object the log message is for.
+ :param char* format: The format string to use for the log messages.
+ :return: nothing
+
+.. c:function:: void pkgconf_audit_log_dependency(pkgconf_client_t *client, const pkgconf_pkg_t *dep, const pkgconf_dependency_t *depnode)
+
+ Convenience function which logs a dependency node to the opened audit log (if any).
+
+ :param pkgconf_client_t* client: The client object the log message is for.
+ :param pkgconf_pkg_t* dep: The dependency package object being logged.
+ :param pkgconf_dependency_t* depnode: The dependency object itself being logged.
+ :return: nothing
diff --git a/doc/libpkgconf-cache.rst b/doc/libpkgconf-cache.rst
new file mode 100644
index 000000000000..adfa79d79587
--- /dev/null
+++ b/doc/libpkgconf-cache.rst
@@ -0,0 +1,45 @@
+
+libpkgconf `cache` module
+=========================
+
+The libpkgconf `cache` module manages a package/module object cache, allowing it to
+avoid loading duplicate copies of a package/module.
+
+A cache is tied to a specific pkgconf client object, so package objects should not
+be shared across threads.
+
+.. c:function:: pkgconf_pkg_t *pkgconf_cache_lookup(const pkgconf_client_t *client, const char *id)
+
+ Looks up a package in the cache given an `id` atom,
+ such as ``gtk+-3.0`` and returns the already loaded version
+ if present.
+
+ :param pkgconf_client_t* client: The client object to access.
+ :param char* id: The package atom to look up in the client object's cache.
+ :return: A package object if present, else ``NULL``.
+ :rtype: pkgconf_pkg_t *
+
+.. c:function:: void pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+
+ Adds an entry for the package to the package cache.
+ The cache entry must be removed if the package is freed.
+
+ :param pkgconf_client_t* client: The client object to modify.
+ :param pkgconf_pkg_t* pkg: The package object to add to the client object's cache.
+ :return: nothing
+
+.. c:function:: void pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+
+ Deletes a package from the client object's package cache.
+
+ :param pkgconf_client_t* client: The client object to modify.
+ :param pkgconf_pkg_t* pkg: The package object to remove from the client object's cache.
+ :return: nothing
+
+.. c:function:: void pkgconf_cache_free(pkgconf_client_t *client)
+
+ Releases all resources related to a client object's package cache.
+ This function should only be called to clear a client object's package cache,
+ as it may release any package in the cache.
+
+ :param pkgconf_client_t* client: The client object to modify.
diff --git a/doc/libpkgconf-client.rst b/doc/libpkgconf-client.rst
new file mode 100644
index 000000000000..6816fdf4b341
--- /dev/null
+++ b/doc/libpkgconf-client.rst
@@ -0,0 +1,212 @@
+
+libpkgconf `client` module
+==========================
+
+The libpkgconf `client` module implements the `pkgconf_client_t` "client" object.
+Client objects store all necessary state for libpkgconf allowing for multiple instances to run
+in parallel.
+
+Client objects are not thread safe, in other words, a client object should not be shared across
+thread boundaries.
+
+.. c:function:: void pkgconf_client_dir_list_build(pkgconf_client_t *client)
+
+ Bootstraps the package search paths. If the ``PKGCONF_PKG_PKGF_ENV_ONLY`` `flag` is set on the client,
+ then only the ``PKG_CONFIG_PATH`` environment variable will be used, otherwise both the
+ ``PKG_CONFIG_PATH`` and ``PKG_CONFIG_LIBDIR`` environment variables will be used.
+
+ :param pkgconf_client_t* client: The pkgconf client object to bootstrap.
+ :return: nothing
+
+.. c:function:: void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality)
+
+ Initialise a pkgconf client object.
+
+ :param pkgconf_client_t* client: The client to initialise.
+ :param pkgconf_error_handler_func_t error_handler: An optional error handler to use for logging errors.
+ :param void* error_handler_data: user data passed to optional error handler
+ :param pkgconf_cross_personality_t* personality: the cross-compile personality to use for defaults
+ :return: nothing
+
+.. c:function:: pkgconf_client_t* pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality)
+
+ Allocate and initialise a pkgconf client object.
+
+ :param pkgconf_error_handler_func_t error_handler: An optional error handler to use for logging errors.
+ :param void* error_handler_data: user data passed to optional error handler
+ :param pkgconf_cross_personality_t* personality: cross-compile personality to use
+ :return: A pkgconf client object.
+ :rtype: pkgconf_client_t*
+
+.. c:function:: void pkgconf_client_deinit(pkgconf_client_t *client)
+
+ Release resources belonging to a pkgconf client object.
+
+ :param pkgconf_client_t* client: The client to deinitialise.
+ :return: nothing
+
+.. c:function:: void pkgconf_client_free(pkgconf_client_t *client)
+
+ Release resources belonging to a pkgconf client object and then free the client object itself.
+
+ :param pkgconf_client_t* client: The client to deinitialise and free.
+ :return: nothing
+
+.. c:function:: const char *pkgconf_client_get_sysroot_dir(const pkgconf_client_t *client)
+
+ Retrieves the client's sysroot directory (if any).
+
+ :param pkgconf_client_t* client: The client object being accessed.
+ :return: A string containing the sysroot directory or NULL.
+ :rtype: const char *
+
+.. c:function:: void pkgconf_client_set_sysroot_dir(pkgconf_client_t *client, const char *sysroot_dir)
+
+ Sets or clears the sysroot directory on a client object. Any previous sysroot directory setting is
+ automatically released if one was previously set.
+
+ Additionally, the global tuple ``$(pc_sysrootdir)`` is set as appropriate based on the new setting.
+
+ :param pkgconf_client_t* client: The client object being modified.
+ :param char* sysroot_dir: The sysroot directory to set or NULL to unset.
+ :return: nothing
+
+.. c:function:: const char *pkgconf_client_get_buildroot_dir(const pkgconf_client_t *client)
+
+ Retrieves the client's buildroot directory (if any).
+
+ :param pkgconf_client_t* client: The client object being accessed.
+ :return: A string containing the buildroot directory or NULL.
+ :rtype: const char *
+
+.. c:function:: void pkgconf_client_set_buildroot_dir(pkgconf_client_t *client, const char *buildroot_dir)
+
+ Sets or clears the buildroot directory on a client object. Any previous buildroot directory setting is
+ automatically released if one was previously set.
+
+ Additionally, the global tuple ``$(pc_top_builddir)`` is set as appropriate based on the new setting.
+
+ :param pkgconf_client_t* client: The client object being modified.
+ :param char* buildroot_dir: The buildroot directory to set or NULL to unset.
+ :return: nothing
+
+.. c:function:: bool pkgconf_error(const pkgconf_client_t *client, const char *format, ...)
+
+ Report an error to a client-registered error handler.
+
+ :param pkgconf_client_t* client: The pkgconf client object to report the error to.
+ :param char* format: A printf-style format string to use for formatting the error message.
+ :return: true if the error handler processed the message, else false.
+ :rtype: bool
+
+.. c:function:: bool pkgconf_warn(const pkgconf_client_t *client, const char *format, ...)
+
+ Report an error to a client-registered warn handler.
+
+ :param pkgconf_client_t* client: The pkgconf client object to report the error to.
+ :param char* format: A printf-style format string to use for formatting the warning message.
+ :return: true if the warn handler processed the message, else false.
+ :rtype: bool
+
+.. c:function:: bool pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t len, const char *funcname, const char *format, ...)
+
+ Report a message to a client-registered trace handler.
+
+ :param pkgconf_client_t* client: The pkgconf client object to report the trace message to.
+ :param char* filename: The file the function is in.
+ :param size_t lineno: The line number currently being executed.
+ :param char* funcname: The function name to use.
+ :param char* format: A printf-style format string to use for formatting the trace message.
+ :return: true if the trace handler processed the message, else false.
+ :rtype: bool
+
+.. c:function:: bool pkgconf_default_error_handler(const char *msg, const pkgconf_client_t *client, const void *data)
+
+ The default pkgconf error handler.
+
+ :param char* msg: The error message to handle.
+ :param pkgconf_client_t* client: The client object the error originated from.
+ :param void* data: An opaque pointer to extra data associated with the client for error handling.
+ :return: true (the function does nothing to process the message)
+ :rtype: bool
+
+.. c:function:: unsigned int pkgconf_client_get_flags(const pkgconf_client_t *client)
+
+ Retrieves resolver-specific flags associated with a client object.
+
+ :param pkgconf_client_t* client: The client object to retrieve the resolver-specific flags from.
+ :return: a bitfield of resolver-specific flags
+ :rtype: uint
+
+.. c:function:: void pkgconf_client_set_flags(pkgconf_client_t *client, unsigned int flags)
+
+ Sets resolver-specific flags associated with a client object.
+
+ :param pkgconf_client_t* client: The client object to set the resolver-specific flags on.
+ :return: nothing
+
+.. c:function:: const char *pkgconf_client_get_prefix_varname(const pkgconf_client_t *client)
+
+ Retrieves the name of the variable that should contain a module's prefix.
+ In some cases, it is necessary to override this variable to allow proper path relocation.
+
+ :param pkgconf_client_t* client: The client object to retrieve the prefix variable name from.
+ :return: the prefix variable name as a string
+ :rtype: const char *
+
+.. c:function:: void pkgconf_client_set_prefix_varname(pkgconf_client_t *client, const char *prefix_varname)
+
+ Sets the name of the variable that should contain a module's prefix.
+ If the variable name is ``NULL``, then the default variable name (``prefix``) is used.
+
+ :param pkgconf_client_t* client: The client object to set the prefix variable name on.
+ :param char* prefix_varname: The prefix variable name to set.
+ :return: nothing
+
+.. c:function:: pkgconf_client_get_warn_handler(const pkgconf_client_t *client)
+
+ Returns the warning handler if one is set, else ``NULL``.
+
+ :param pkgconf_client_t* client: The client object to get the warn handler from.
+ :return: a function pointer to the warn handler or ``NULL``
+
+.. c:function:: pkgconf_client_set_warn_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t warn_handler, void *warn_handler_data)
+
+ Sets a warn handler on a client object or uninstalls one if set to ``NULL``.
+
+ :param pkgconf_client_t* client: The client object to set the warn handler on.
+ :param pkgconf_error_handler_func_t warn_handler: The warn handler to set.
+ :param void* warn_handler_data: Optional data to associate with the warn handler.
+ :return: nothing
+
+.. c:function:: pkgconf_client_get_error_handler(const pkgconf_client_t *client)
+
+ Returns the error handler if one is set, else ``NULL``.
+
+ :param pkgconf_client_t* client: The client object to get the error handler from.
+ :return: a function pointer to the error handler or ``NULL``
+
+.. c:function:: pkgconf_client_set_error_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data)
+
+ Sets a warn handler on a client object or uninstalls one if set to ``NULL``.
+
+ :param pkgconf_client_t* client: The client object to set the error handler on.
+ :param pkgconf_error_handler_func_t error_handler: The error handler to set.
+ :param void* error_handler_data: Optional data to associate with the error handler.
+ :return: nothing
+
+.. c:function:: pkgconf_client_get_trace_handler(const pkgconf_client_t *client)
+
+ Returns the error handler if one is set, else ``NULL``.
+
+ :param pkgconf_client_t* client: The client object to get the error handler from.
+ :return: a function pointer to the error handler or ``NULL``
+
+.. c:function:: pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t trace_handler, void *trace_handler_data)
+
+ Sets a warn handler on a client object or uninstalls one if set to ``NULL``.
+
+ :param pkgconf_client_t* client: The client object to set the error handler on.
+ :param pkgconf_error_handler_func_t trace_handler: The error handler to set.
+ :param void* trace_handler_data: Optional data to associate with the error handler.
+ :return: nothing
diff --git a/doc/libpkgconf-dependency.rst b/doc/libpkgconf-dependency.rst
new file mode 100644
index 000000000000..ce70d6c11c35
--- /dev/null
+++ b/doc/libpkgconf-dependency.rst
@@ -0,0 +1,90 @@
+
+libpkgconf `dependency` module
+==============================
+
+The `dependency` module provides support for building `dependency lists` (the basic component of the overall `dependency graph`) and
+`dependency nodes` which store dependency information.
+
+.. c:function:: pkgconf_dependency_t *pkgconf_dependency_add(pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare)
+
+ Adds a parsed dependency to a dependency list as a dependency node.
+
+ :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to.
+ :param pkgconf_list_t* list: The dependency list to add a dependency node to.
+ :param char* package: The package `atom` to set on the dependency node.
+ :param char* version: The package `version` to set on the dependency node.
+ :param pkgconf_pkg_comparator_t compare: The comparison operator to set on the dependency node.
+ :param uint flags: Any flags to attach to the dependency node.
+ :return: A dependency node.
+ :rtype: pkgconf_dependency_t *
+
+.. c:function:: void pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail)
+
+ Adds a dependency node to a pre-existing dependency list.
+
+ :param pkgconf_list_t* list: The dependency list to add a dependency node to.
+ :param pkgconf_dependency_t* tail: The dependency node to add to the tail of the dependency list.
+ :return: nothing
+
+.. c:function:: void pkgconf_dependency_free_one(pkgconf_dependency_t *dep)
+
+ Frees a dependency node.
+
+ :param pkgconf_dependency_t* dep: The dependency node to free.
+ :return: nothing
+
+.. c:function:: pkgconf_dependency_t *pkgconf_dependency_ref(pkgconf_client_t *owner, pkgconf_dependency_t *dep)
+
+ Increases a dependency node's refcount.
+
+ :param pkgconf_client_t* owner: The client object which owns the memory of this dependency node.
+ :param pkgconf_dependency_t* dep: The dependency to increase the refcount of.
+ :return: the dependency node on success, else NULL
+
+.. c:function:: void pkgconf_dependency_unref(pkgconf_client_t *owner, pkgconf_dependency_t *dep)
+
+ Decreases a dependency node's refcount and frees it if necessary.
+
+ :param pkgconf_client_t* owner: The client object which owns the memory of this dependency node.
+ :param pkgconf_dependency_t* dep: The dependency to decrease the refcount of.
+ :return: nothing
+
+.. c:function:: void pkgconf_dependency_free(pkgconf_list_t *list)
+
+ Release a dependency list and its child dependency nodes.
+
+ :param pkgconf_list_t* list: The dependency list to release.
+ :return: nothing
+
+.. c:function:: void pkgconf_dependency_parse_str(pkgconf_list_t *deplist_head, const char *depends)
+
+ Parse a dependency declaration into a dependency list.
+ Commas are counted as whitespace to allow for constructs such as ``@SUBSTVAR@, zlib`` being processed
+ into ``, zlib``.
+
+ :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to.
+ :param pkgconf_list_t* deplist_head: The dependency list to populate with dependency nodes.
+ :param char* depends: The dependency data to parse.
+ :param uint flags: Any flags to attach to the dependency nodes.
+ :return: nothing
+
+.. c:function:: void pkgconf_dependency_parse(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist, const char *depends)
+
+ Preprocess dependency data and then process that dependency declaration into a dependency list.
+ Commas are counted as whitespace to allow for constructs such as ``@SUBSTVAR@, zlib`` being processed
+ into ``, zlib``.
+
+ :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to.
+ :param pkgconf_pkg_t* pkg: The package object that owns this dependency list.
+ :param pkgconf_list_t* deplist: The dependency list to populate with dependency nodes.
+ :param char* depends: The dependency data to parse.
+ :param uint flags: Any flags to attach to the dependency nodes.
+ :return: nothing
+
+.. c:function:: pkgconf_dependency_t *pkgconf_dependency_copy(pkgconf_client_t *client, const pkgconf_dependency_t *dep)
+
+ Copies a dependency node to a new one.
+
+ :param pkgconf_client_t* client: The client object that will own this dependency.
+ :param pkgconf_dependency_t* dep: The dependency node to copy.
+ :return: a pointer to a new dependency node, else NULL
diff --git a/doc/libpkgconf-fragment.rst b/doc/libpkgconf-fragment.rst
new file mode 100644
index 000000000000..00759b7de968
--- /dev/null
+++ b/doc/libpkgconf-fragment.rst
@@ -0,0 +1,116 @@
+
+libpkgconf `fragment` module
+============================
+
+The `fragment` module provides low-level management and rendering of fragment lists. A
+`fragment list` contains various `fragments` of text (such as ``-I /usr/include``) in a matter
+which is composable, mergeable and reorderable.
+
+.. c:function:: void pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string, unsigned int flags)
+
+ Adds a `fragment` of text to a `fragment list`, possibly modifying the fragment if a sysroot is set.
+
+ :param pkgconf_client_t* client: The pkgconf client being accessed.
+ :param pkgconf_list_t* list: The fragment list.
+ :param char* string: The string of text to add as a fragment to the fragment list.
+ :param uint flags: Parsing-related flags for the package.
+ :return: nothing
+
+.. c:function:: bool pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag)
+
+ Checks if a `fragment` contains a `system path`. System paths are detected at compile time and optionally overridden by
+ the ``PKG_CONFIG_SYSTEM_INCLUDE_PATH`` and ``PKG_CONFIG_SYSTEM_LIBRARY_PATH`` environment variables.
+
+ :param pkgconf_client_t* client: The pkgconf client object the fragment belongs to.
+ :param pkgconf_fragment_t* frag: The fragment being checked.
+ :return: true if the fragment contains a system path, else false
+ :rtype: bool
+
+.. c:function:: void pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private)
+
+ Copies a `fragment` to another `fragment list`, possibly removing a previous copy of the `fragment`
+ in a process known as `mergeback`.
+
+ :param pkgconf_client_t* client: The pkgconf client being accessed.
+ :param pkgconf_list_t* list: The list the fragment is being added to.
+ :param pkgconf_fragment_t* base: The fragment being copied.
+ :param bool is_private: Whether the fragment list is a `private` fragment list (static linking).
+ :return: nothing
+
+.. c:function:: void pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base)
+
+ Copies a `fragment list` to another `fragment list`, possibly removing a previous copy of the fragments
+ in a process known as `mergeback`.
+
+ :param pkgconf_client_t* client: The pkgconf client being accessed.
+ :param pkgconf_list_t* list: The list the fragments are being added to.
+ :param pkgconf_list_t* base: The list the fragments are being copied from.
+ :return: nothing
+
+.. 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)
+
+ Copies a `fragment list` to another `fragment list` which match a user-specified filtering function.
+
+ :param pkgconf_client_t* client: The pkgconf client being accessed.
+ :param pkgconf_list_t* dest: The destination list.
+ :param pkgconf_list_t* src: The source list.
+ :param pkgconf_fragment_filter_func_t filter_func: The filter function to use.
+ :param void* data: Optional data to pass to the filter function.
+ :return: nothing
+
+.. c:function:: size_t pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops)
+
+ Calculates the required memory to store a `fragment list` when rendered as a string.
+
+ :param pkgconf_list_t* list: The `fragment list` being rendered.
+ :param bool escape: Whether or not to escape special shell characters (deprecated).
+ :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
+ :return: the amount of bytes required to represent the `fragment list` when rendered
+ :rtype: size_t
+
+.. 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)
+
+ Renders a `fragment list` into a buffer.
+
+ :param pkgconf_list_t* list: The `fragment list` being rendered.
+ :param char* buf: The buffer to render the fragment list into.
+ :param size_t buflen: The length of the buffer.
+ :param bool escape: Whether or not to escape special shell characters (deprecated).
+ :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
+ :return: nothing
+
+.. c:function:: char *pkgconf_fragment_render(const pkgconf_list_t *list)
+
+ Allocate memory and render a `fragment list` into it.
+
+ :param pkgconf_list_t* list: The `fragment list` being rendered.
+ :param bool escape: Whether or not to escape special shell characters (deprecated).
+ :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
+ :return: An allocated string containing the rendered `fragment list`.
+ :rtype: char *
+
+.. c:function:: void pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node)
+
+ Delete a `fragment node` from a `fragment list`.
+
+ :param pkgconf_list_t* list: The `fragment list` to delete from.
+ :param pkgconf_fragment_t* node: The `fragment node` to delete.
+ :return: nothing
+
+.. c:function:: void pkgconf_fragment_free(pkgconf_list_t *list)
+
+ Delete an entire `fragment list`.
+
+ :param pkgconf_list_t* list: The `fragment list` to delete.
+ :return: nothing
+
+.. c:function:: bool pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value)
+
+ Parse a string into a `fragment list`.
+
+ :param pkgconf_client_t* client: The pkgconf client being accessed.
+ :param pkgconf_list_t* list: The `fragment list` to add the fragment entries to.
+ :param pkgconf_list_t* vars: A list of variables to use for variable substitution.
+ :param uint flags: Any parsing flags to be aware of.
+ :param char* value: The string to parse into fragments.
+ :return: true on success, false on parse error
diff --git a/doc/libpkgconf-path.rst b/doc/libpkgconf-path.rst
new file mode 100644
index 000000000000..04789440a2d3
--- /dev/null
+++ b/doc/libpkgconf-path.rst
@@ -0,0 +1,71 @@
+
+libpkgconf `path` module
+========================
+
+The `path` module provides functions for manipulating lists of paths in a cross-platform manner. Notably,
+it is used by the `pkgconf client` to parse the ``PKG_CONFIG_PATH``, ``PKG_CONFIG_LIBDIR`` and related environment
+variables.
+
+.. c:function:: void pkgconf_path_add(const char *text, pkgconf_list_t *dirlist)
+
+ Adds a path node to a path list. If the path is already in the list, do nothing.
+
+ :param char* text: The path text to add as a path node.
+ :param pkgconf_list_t* dirlist: The path list to add the path node to.
+ :param bool filter: Whether to perform duplicate filtering.
+ :return: nothing
+
+.. c:function:: size_t pkgconf_path_split(const char *text, pkgconf_list_t *dirlist)
+
+ Splits a given text input and inserts paths into a path list.
+
+ :param char* text: The path text to split and add as path nodes.
+ :param pkgconf_list_t* dirlist: The path list to have the path nodes added to.
+ :param bool filter: Whether to perform duplicate filtering.
+ :return: number of path nodes added to the path list
+ :rtype: size_t
+
+.. c:function:: size_t pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist)
+
+ Adds the paths specified in an environment variable to a path list. If the environment variable is not set,
+ an optional default set of paths is added.
+
+ :param char* envvarname: The environment variable to look up.
+ :param char* fallback: The fallback paths to use if the environment variable is not set.
+ :param pkgconf_list_t* dirlist: The path list to add the path nodes to.
+ :param bool filter: Whether to perform duplicate filtering.
+ :return: number of path nodes added to the path list
+ :rtype: size_t
+
+.. c:function:: bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist)
+
+ Checks whether a path has a matching prefix in a path list.
+
+ :param char* path: The path to check against a path list.
+ :param pkgconf_list_t* dirlist: The path list to check the path against.
+ :return: true if the path list has a matching prefix, otherwise false
+ :rtype: bool
+
+.. c:function:: void pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
+
+ Copies a path list to another path list.
+
+ :param pkgconf_list_t* dst: The path list to copy to.
+ :param pkgconf_list_t* src: The path list to copy from.
+ :return: nothing
+
+.. c:function:: void pkgconf_path_free(pkgconf_list_t *dirlist)
+
+ Releases any path nodes attached to the given path list.
+
+ :param pkgconf_list_t* dirlist: The path list to clean up.
+ :return: nothing
+
+.. c:function:: bool pkgconf_path_relocate(char *buf, size_t buflen)
+
+ Relocates a path, possibly calling normpath() on it.
+
+ :param char* buf: The path to relocate.
+ :param size_t buflen: The buffer length the path is contained in.
+ :return: true on success, false on error
+ :rtype: bool
diff --git a/doc/libpkgconf-personality.rst b/doc/libpkgconf-personality.rst
new file mode 100644
index 000000000000..134671ad1211
--- /dev/null
+++ b/doc/libpkgconf-personality.rst
@@ -0,0 +1,27 @@
+
+libpkgconf `personality` module
+=========================
+
+.. c:function:: const pkgconf_cross_personality_t *pkgconf_cross_personality_default(void)
+
+ Returns the default cross-compile personality.
+
+ Not thread safe.
+
+ :rtype: pkgconf_cross_personality_t*
+ :return: the default cross-compile personality
+
+.. c:function:: void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *)
+
+ Decrements the count of default cross personality instances.
+
+ Not thread safe.
+
+ :rtype: void
+
+.. c:function:: pkgconf_cross_personality_t *pkgconf_cross_personality_find(const char *triplet)
+
+ Attempts to find a cross-compile personality given a triplet.
+
+ :rtype: pkgconf_cross_personality_t*
+ :return: the default cross-compile personality
diff --git a/doc/libpkgconf-pkg.rst b/doc/libpkgconf-pkg.rst
new file mode 100644
index 000000000000..044bd1f2f011
--- /dev/null
+++ b/doc/libpkgconf-pkg.rst
@@ -0,0 +1,154 @@
+
+libpkgconf `pkg` module
+=======================
+
+The `pkg` module provides dependency resolution services and the overall `.pc` file parsing
+routines.
+
+.. c:function:: pkgconf_pkg_t *pkgconf_pkg_new_from_file(const pkgconf_client_t *client, const char *filename, FILE *f, unsigned int flags)
+
+ Parse a .pc file into a pkgconf_pkg_t object structure.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param char* filename: The filename of the package file (including full path).
+ :param FILE* f: The file object to read from.
+ :param uint flags: The flags to use when parsing.
+ :returns: A ``pkgconf_pkg_t`` object which contains the package data.
+ :rtype: pkgconf_pkg_t *
+
+.. c:function:: void pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+
+ Releases all releases for a given ``pkgconf_pkg_t`` object.
+
+ :param pkgconf_client_t* client: The client which owns the ``pkgconf_pkg_t`` object, `pkg`.
+ :param pkgconf_pkg_t* pkg: The package to free.
+ :return: nothing
+
+.. c:function:: pkgconf_pkg_t *pkgconf_pkg_ref(const pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+
+ Adds an additional reference to the package object.
+
+ :param pkgconf_client_t* client: The pkgconf client object which owns the package being referenced.
+ :param pkgconf_pkg_t* pkg: The package object being referenced.
+ :return: The package itself with an incremented reference count.
+ :rtype: pkgconf_pkg_t *
+
+.. c:function:: void pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+
+ Releases a reference on the package object. If the reference count is 0, then also free the package.
+
+ :param pkgconf_client_t* client: The pkgconf client object which owns the package being dereferenced.
+ :param pkgconf_pkg_t* pkg: The package object being dereferenced.
+ :return: nothing
+
+.. c:function:: pkgconf_pkg_t *pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func)
+
+ Iterates over all packages found in the `package directory list`, running ``func`` on them. If ``func`` returns true,
+ then stop iteration and return the last iterated package.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param void* data: An opaque pointer to data to provide the iteration function with.
+ :param pkgconf_pkg_iteration_func_t func: A function which is called for each package to determine if the package matches,
+ always return ``false`` to iterate over all packages.
+ :return: A package object reference if one is found by the scan function, else ``NULL``.
+ :rtype: pkgconf_pkg_t *
+
+.. c:function:: pkgconf_pkg_t *pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
+
+ Search for a package.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param char* name: The name of the package `atom` to use for searching.
+ :return: A package object reference if the package was found, else ``NULL``.
+ :rtype: pkgconf_pkg_t *
+
+.. c:function:: int pkgconf_compare_version(const char *a, const char *b)
+
+ Compare versions using RPM version comparison rules as described in the LSB.
+
+ :param char* a: The first version to compare in the pair.
+ :param char* b: The second version to compare in the pair.
+ :return: -1 if the first version is less than, 0 if both versions are equal, 1 if the second version is less than.
+ :rtype: int
+
+.. c:function:: pkgconf_pkg_t *pkgconf_builtin_pkg_get(const char *name)
+
+ Looks up a built-in package. The package should not be freed or dereferenced.
+
+ :param char* name: An atom corresponding to a built-in package to search for.
+ :return: the built-in package if present, else ``NULL``.
+ :rtype: pkgconf_pkg_t *
+
+.. c:function:: const char *pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep)
+
+ Returns the comparator used in a depgraph dependency node as a string.
+
+ :param pkgconf_dependency_t* pkgdep: The depgraph dependency node to return the comparator for.
+ :return: A string matching the comparator or ``"???"``.
+ :rtype: char *
+
+.. c:function:: pkgconf_pkg_comparator_t pkgconf_pkg_comparator_lookup_by_name(const char *name)
+
+ Look up the appropriate comparator bytecode in the comparator set (defined
+ in ``pkg.c``, see ``pkgconf_pkg_comparator_names`` and ``pkgconf_pkg_comparator_impls``).
+
+ :param char* name: The comparator to look up by `name`.
+ :return: The comparator bytecode if found, else ``PKGCONF_CMP_ANY``.
+ :rtype: pkgconf_pkg_comparator_t
+
+.. c:function:: pkgconf_pkg_t *pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags)
+
+ Verify a pkgconf_dependency_t node in the depgraph. If the dependency is solvable,
+ return the appropriate ``pkgconf_pkg_t`` object, else ``NULL``.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param pkgconf_dependency_t* pkgdep: The dependency graph node to solve.
+ :param uint* eflags: An optional pointer that, if set, will be populated with an error code from the resolver.
+ :return: On success, the appropriate ``pkgconf_pkg_t`` object to solve the dependency, else ``NULL``.
+ :rtype: pkgconf_pkg_t *
+
+.. c:function:: unsigned int pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth)
+
+ Verify the graph dependency nodes are satisfiable by walking the tree using
+ ``pkgconf_pkg_traverse()``.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param pkgconf_pkg_t* root: The root entry in the package dependency graph which should contain the top-level dependencies to resolve.
+ :param int depth: The maximum allowed depth for dependency resolution.
+ :return: On success, ``PKGCONF_PKG_ERRF_OK`` (0), else an error code.
+ :rtype: unsigned int
+
+.. 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)
+
+ Walk and resolve the dependency graph up to `maxdepth` levels.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param pkgconf_pkg_t* root: The root of the dependency graph.
+ :param pkgconf_pkg_traverse_func_t func: A traversal function to call for each resolved node in the dependency graph.
+ :param void* data: An opaque pointer to data to be passed to the traversal function.
+ :param int maxdepth: The maximum depth to walk the dependency graph for. -1 means infinite recursion.
+ :param uint skip_flags: Skip over dependency nodes containing the specified flags. A setting of 0 skips no dependency nodes.
+ :return: ``PKGCONF_PKG_ERRF_OK`` on success, else an error code.
+ :rtype: unsigned int
+
+.. c:function:: int pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
+
+ Walks a dependency graph and extracts relevant ``CFLAGS`` fragments.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param pkgconf_pkg_t* root: The root of the dependency graph.
+ :param pkgconf_list_t* list: The fragment list to add the extracted ``CFLAGS`` fragments to.
+ :param int maxdepth: The maximum allowed depth for dependency resolution. -1 means infinite recursion.
+ :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code.
+ :rtype: unsigned int
+
+.. c:function:: int pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
+
+ Walks a dependency graph and extracts relevant ``LIBS`` fragments.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param pkgconf_pkg_t* root: The root of the dependency graph.
+ :param pkgconf_list_t* list: The fragment list to add the extracted ``LIBS`` fragments to.
+ :param int maxdepth: The maximum allowed depth for dependency resolution. -1 means infinite recursion.
+ :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code.
+ :rtype: unsigned int
diff --git a/doc/libpkgconf-queue.rst b/doc/libpkgconf-queue.rst
new file mode 100644
index 000000000000..43fca6960d25
--- /dev/null
+++ b/doc/libpkgconf-queue.rst
@@ -0,0 +1,78 @@
+
+libpkgconf `queue` module
+=========================
+
+The `queue` module provides an interface that allows easily building a dependency graph from an
+arbitrary set of dependencies. It also provides support for doing "preflight" checks on the entire
+dependency graph prior to working with it.
+
+Using the `queue` module functions is the recommended way of working with dependency graphs.
+
+.. c:function:: void pkgconf_queue_push(pkgconf_list_t *list, const char *package)
+
+ Pushes a requested dependency onto the dependency resolver's queue.
+
+ :param pkgconf_list_t* list: the dependency resolution queue to add the package request to.
+ :param char* package: the dependency atom requested
+ :return: nothing
+
+.. c:function:: bool pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list)
+
+ Compile a dependency resolution queue into a dependency resolution problem if possible, otherwise report an error.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param pkgconf_pkg_t* world: The designated root of the dependency graph.
+ :param pkgconf_list_t* list: The list of dependency requests to consider.
+ :return: true if the built dependency resolution problem is consistent, else false
+ :rtype: bool
+
+.. c:function:: void pkgconf_queue_free(pkgconf_list_t *list)
+
+ Release any memory related to a dependency resolution queue.
+
+ :param pkgconf_list_t* list: The dependency resolution queue to release.
+ :return: nothing
+
+.. c:function:: void pkgconf_solution_free(pkgconf_client_t *client, pkgconf_pkg_t *world, int maxdepth)
+
+ Removes references to package nodes contained in a solution.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param pkgconf_pkg_t* world: The root for the generated dependency graph. Should have PKGCONF_PKG_PROPF_VIRTUAL flag.
+ :returns: nothing
+
+.. c:function:: bool pkgconf_queue_solve(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_pkg_t *world, int maxdepth)
+
+ Solves and flattens the dependency graph for the supplied dependency list.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param pkgconf_list_t* list: The list of dependency requests to consider.
+ :param pkgconf_pkg_t* world: The root for the generated dependency graph, provided by the caller. Should have PKGCONF_PKG_PROPF_VIRTUAL flag.
+ :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited.
+ :returns: true if the dependency resolver found a solution, otherwise false.
+ :rtype: bool
+
+.. c:function:: void pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data)
+
+ Attempt to compile a dependency resolution queue into a dependency resolution problem, then attempt to solve the problem and
+ feed the solution to a callback function if a complete dependency graph is found.
+
+ This function should not be used in new code. Use pkgconf_queue_solve instead.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param pkgconf_list_t* list: The list of dependency requests to consider.
+ :param pkgconf_queue_apply_func_t func: The callback function to call if a solution is found by the dependency resolver.
+ :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited.
+ :param void* data: An opaque pointer which is passed to the callback function.
+ :returns: true if the dependency resolver found a solution, otherwise false.
+ :rtype: bool
+
+.. c:function:: void pkgconf_queue_validate(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data)
+
+ Attempt to compile a dependency resolution queue into a dependency resolution problem, then attempt to solve the problem.
+
+ :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ :param pkgconf_list_t* list: The list of dependency requests to consider.
+ :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited.
+ :returns: true if the dependency resolver found a solution, otherwise false.
+ :rtype: bool
diff --git a/doc/libpkgconf-tuple.rst b/doc/libpkgconf-tuple.rst
new file mode 100644
index 000000000000..419168b0e466
--- /dev/null
+++ b/doc/libpkgconf-tuple.rst
@@ -0,0 +1,92 @@
+
+libpkgconf `tuple` module
+=========================
+
+The `tuple` module provides key-value mappings backed by a linked list. The key-value
+mapping is mainly used for variable substitution when parsing .pc files.
+
+There are two sets of mappings: a ``pkgconf_pkg_t`` specific mapping, and a `global` mapping.
+The `tuple` module provides convenience wrappers for managing the `global` mapping, which is
+attached to a given client object.
+
+.. c:function:: void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value)
+
+ Defines a global variable, replacing the previous declaration if one was set.
+
+ :param pkgconf_client_t* client: The pkgconf client object to modify.
+ :param char* key: The key for the mapping (variable name).
+ :param char* value: The value for the mapped entry.
+ :return: nothing
+
+.. c:function:: void pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key)
+
+ Looks up a global variable.
+
+ :param pkgconf_client_t* client: The pkgconf client object to access.
+ :param char* key: The key or variable name to look up.
+ :return: the contents of the variable or ``NULL``
+ :rtype: char *
+
+.. c:function:: void pkgconf_tuple_free_global(pkgconf_client_t *client)
+
+ Delete all global variables associated with a pkgconf client object.
+
+ :param pkgconf_client_t* client: The pkgconf client object to modify.
+ :return: nothing
+
+.. c:function:: void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv)
+
+ Parse and define a global variable.
+
+ :param pkgconf_client_t* client: The pkgconf client object to modify.
+ :param char* kv: The variable in the form of ``key=value``.
+ :return: nothing
+
+.. c:function:: pkgconf_tuple_t *pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse)
+
+ Optionally parse and then define a variable.
+
+ :param pkgconf_client_t* client: The pkgconf client object to access.
+ :param pkgconf_list_t* list: The variable list to add the new variable to.
+ :param char* key: The name of the variable being added.
+ :param char* value: The value of the variable being added.
+ :param bool parse: Whether or not to parse the value for variable substitution.
+ :return: a variable object
+ :rtype: pkgconf_tuple_t *
+
+.. c:function:: char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key)
+
+ Look up a variable in a variable list.
+
+ :param pkgconf_client_t* client: The pkgconf client object to access.
+ :param pkgconf_list_t* list: The variable list to search.
+ :param char* key: The variable name to search for.
+ :return: the value of the variable or ``NULL``
+ :rtype: char *
+
+.. c:function:: char *pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value, unsigned int flags)
+
+ Parse an expression for variable substitution.
+
+ :param pkgconf_client_t* client: The pkgconf client object to access.
+ :param pkgconf_list_t* list: The variable list to search for variables (along side the global variable list).
+ :param char* value: The ``key=value`` string to parse.
+ :param uint flags: Any flags to consider while parsing.
+ :return: the variable data with any variables substituted
+ :rtype: char *
+
+.. c:function:: void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list)
+
+ Deletes a variable object, removing it from any variable lists and releasing any memory associated
+ with it.
+
+ :param pkgconf_tuple_t* tuple: The variable object to release.
+ :param pkgconf_list_t* list: The variable list the variable object is attached to.
+ :return: nothing
+
+.. c:function:: void pkgconf_tuple_free(pkgconf_list_t *list)
+
+ Deletes a variable list and any variables attached to it.
+
+ :param pkgconf_list_t* list: The variable list to delete.
+ :return: nothing
diff --git a/doc/libpkgconf.rst b/doc/libpkgconf.rst
new file mode 100644
index 000000000000..37167f0f25fe
--- /dev/null
+++ b/doc/libpkgconf.rst
@@ -0,0 +1,17 @@
+libpkgconf - an API for managing `pkg-config` modules
+=====================================================
+
+.. toctree::
+ :maxdepth: 2
+
+ libpkgconf-argvsplit
+ libpkgconf-audit
+ libpkgconf-cache
+ libpkgconf-client
+ libpkgconf-dependency
+ libpkgconf-fragment
+ libpkgconf-path
+ libpkgconf-personality
+ libpkgconf-pkg
+ libpkgconf-queue
+ libpkgconf-tuple
diff --git a/libpkgconf.pc.in b/libpkgconf.pc.in
new file mode 100644
index 000000000000..119fe57f827d
--- /dev/null
+++ b/libpkgconf.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=${prefix}
+includedir=@includedir@
+libdir=@libdir@
+
+Name: libpkgconf
+Description: a library for accessing and manipulating development framework configuration
+URL: https://gitea.treehouse.systems/ariadne/pkgconf
+License: ISC
+Version: @PACKAGE_VERSION@
+CFlags: -I${includedir}/pkgconf
+Libs: -L${libdir} -lpkgconf
diff --git a/libpkgconf/argvsplit.c b/libpkgconf/argvsplit.c
new file mode 100644
index 000000000000..1ff221ac63f8
--- /dev/null
+++ b/libpkgconf/argvsplit.c
@@ -0,0 +1,161 @@
+/*
+ * argvsplit.c
+ * argv_split() routine
+ *
+ * Copyright (c) 2012, 2017 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !doc
+ *
+ * libpkgconf `argvsplit` module
+ * =============================
+ *
+ * This is a lowlevel module which provides parsing of strings into argument vectors,
+ * similar to what a shell would do.
+ */
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_argv_free(char **argv)
+ *
+ * Frees an argument vector.
+ *
+ * :param char** argv: The argument vector to free.
+ * :return: nothing
+ */
+void
+pkgconf_argv_free(char **argv)
+{
+ free(argv[0]);
+ free(argv);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: int pkgconf_argv_split(const char *src, int *argc, char ***argv)
+ *
+ * Splits a string into an argument vector.
+ *
+ * :param char* src: The string to split.
+ * :param int* argc: A pointer to an integer to store the argument count.
+ * :param char*** argv: A pointer to a pointer for an argument vector.
+ * :return: 0 on success, -1 on error.
+ * :rtype: int
+ */
+int
+pkgconf_argv_split(const char *src, int *argc, char ***argv)
+{
+ char *buf = calloc(1, strlen(src) + 1);
+ if (buf == NULL)
+ return -1;
+
+ const char *src_iter;
+ char *dst_iter;
+ int argc_count = 0;
+ int argv_size = 5;
+ char quote = 0;
+ bool escaped = false;
+
+ src_iter = src;
+ dst_iter = buf;
+
+ *argv = calloc(argv_size, sizeof (void *));
+ if (*argv == NULL)
+ {
+ free(buf);
+ return -1;
+ }
+
+ (*argv)[argc_count] = dst_iter;
+
+ while (*src_iter)
+ {
+ if (escaped)
+ {
+ /* POSIX: only \CHAR is special inside a double quote if CHAR is {$, `, ", \, newline}. */
+ if (quote == '"')
+ {
+ if (!(*src_iter == '$' || *src_iter == '`' || *src_iter == '"' || *src_iter == '\\'))
+ *dst_iter++ = '\\';
+
+ *dst_iter++ = *src_iter;
+ }
+ else
+ {
+ *dst_iter++ = *src_iter;
+ }
+
+ escaped = false;
+ }
+ else if (quote)
+ {
+ if (*src_iter == quote)
+ quote = 0;
+ else if (*src_iter == '\\' && quote != '\'')
+ escaped = true;
+ else
+ *dst_iter++ = *src_iter;
+ }
+ else if (isspace((unsigned char)*src_iter))
+ {
+ if ((*argv)[argc_count] != NULL)
+ {
+ argc_count++, dst_iter++;
+
+ if (argc_count == argv_size)
+ {
+ argv_size += 5;
+ *argv = realloc(*argv, sizeof(void *) * argv_size);
+ }
+
+ (*argv)[argc_count] = dst_iter;
+ }
+ }
+ else switch(*src_iter)
+ {
+ case '\\':
+ escaped = true;
+ break;
+
+ case '\"':
+ case '\'':
+ quote = *src_iter;
+ break;
+
+ default:
+ *dst_iter++ = *src_iter;
+ break;
+ }
+
+ src_iter++;
+ }
+
+ if (escaped || quote)
+ {
+ free(*argv);
+ free(buf);
+ return -1;
+ }
+
+ if (strlen((*argv)[argc_count]))
+ {
+ argc_count++;
+ }
+
+ *argc = argc_count;
+ return 0;
+}
diff --git a/libpkgconf/audit.c b/libpkgconf/audit.c
new file mode 100644
index 000000000000..a06eb24fc728
--- /dev/null
+++ b/libpkgconf/audit.c
@@ -0,0 +1,98 @@
+/*
+ * audit.c
+ * package audit log functions
+ *
+ * Copyright (c) 2016 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !doc
+ *
+ * libpkgconf `audit` module
+ * =========================
+ *
+ * The libpkgconf `audit` module contains the functions related to attaching an audit log file
+ * to a ``pkgconf_client_t`` object.
+ *
+ * The audit log format is the same as the output generated by the ``PKG_CONFIG_LOG`` environment
+ * variable.
+ */
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_audit_set_log(pkgconf_client_t *client, FILE *auditf)
+ *
+ * Sets the audit log file pointer on `client` to `auditf`.
+ * The callee is responsible for closing any previous log files.
+ *
+ * :param pkgconf_client_t* client: The client object to modify.
+ * :param FILE* auditf: The file pointer for the already open log file.
+ * :return: nothing
+ */
+void
+pkgconf_audit_set_log(pkgconf_client_t *client, FILE *auditf)
+{
+ client->auditf = auditf;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_audit_log(pkgconf_client_t *client, const char *format, ...)
+ *
+ * Logs a message to the opened audit log (if any).
+ *
+ * :param pkgconf_client_t* client: The client object the log message is for.
+ * :param char* format: The format string to use for the log messages.
+ * :return: nothing
+ */
+void
+pkgconf_audit_log(pkgconf_client_t *client, const char *format, ...)
+{
+ va_list va;
+
+ if (client->auditf == NULL)
+ return;
+
+ va_start(va, format);
+ vfprintf(client->auditf, format, va);
+ va_end(va);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_audit_log_dependency(pkgconf_client_t *client, const pkgconf_pkg_t *dep, const pkgconf_dependency_t *depnode)
+ *
+ * Convenience function which logs a dependency node to the opened audit log (if any).
+ *
+ * :param pkgconf_client_t* client: The client object the log message is for.
+ * :param pkgconf_pkg_t* dep: The dependency package object being logged.
+ * :param pkgconf_dependency_t* depnode: The dependency object itself being logged.
+ * :return: nothing
+ */
+void
+pkgconf_audit_log_dependency(pkgconf_client_t *client, const pkgconf_pkg_t *dep, const pkgconf_dependency_t *depnode)
+{
+ if (client->auditf == NULL)
+ return;
+
+ fprintf(client->auditf, "%s ", dep->id);
+ if (depnode->version != NULL && depnode->compare != PKGCONF_CMP_ANY)
+ {
+ fprintf(client->auditf, "%s %s ", pkgconf_pkg_get_comparator(depnode), depnode->version);
+ }
+
+ fprintf(client->auditf, "[%s]\n", dep->version);
+}
diff --git a/libpkgconf/bsdstubs.c b/libpkgconf/bsdstubs.c
new file mode 100644
index 000000000000..a5291f7a5a93
--- /dev/null
+++ b/libpkgconf/bsdstubs.c
@@ -0,0 +1,197 @@
+/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */
+/* $OpenBSD: strlcat.c,v 1.12 2005/03/30 20:13:52 otto Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include <libpkgconf/bsdstubs.h>
+#include <libpkgconf/config.h>
+
+#if !HAVE_DECL_STRLCPY
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+static inline size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+#endif
+
+#if !HAVE_DECL_STRLCAT
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+static inline size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
+#endif
+
+/*
+ * Copyright (c) 2012 William Pitcock <nenolod@dereferenced.org>.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#if !HAVE_DECL_STRNDUP
+/*
+ * Creates a memory buffer and copies at most 'len' characters to it.
+ * If 'len' is less than the length of the source string, truncation occured.
+ */
+static inline char *
+strndup(const char *src, size_t len)
+{
+ char *out = malloc(len + 1);
+ pkgconf_strlcpy(out, src, len + 1);
+ return out;
+}
+#endif
+
+#if !HAVE_DECL_PLEDGE
+static inline int
+pledge(const char *promises, const char *execpromises)
+{
+ (void) promises;
+ (void) execpromises;
+
+ return 0;
+}
+#endif
+
+#if !HAVE_DECL_UNVEIL
+static inline int
+unveil(const char *path, const char *permissions)
+{
+ (void) path;
+ (void) permissions;
+
+ return 0;
+}
+#endif
+
+size_t
+pkgconf_strlcpy(char *dst, const char *src, size_t siz)
+{
+ return strlcpy(dst, src, siz);
+}
+
+size_t
+pkgconf_strlcat(char *dst, const char *src, size_t siz)
+{
+ return strlcat(dst, src, siz);
+}
+
+char *
+pkgconf_strndup(const char *src, size_t len)
+{
+ return strndup(src, len);
+}
+
+#if !HAVE_DECL_REALLOCARRAY
+void *
+reallocarray(void *ptr, size_t m, size_t n)
+{
+ if (n && m > -1 / n)
+ {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ return realloc(ptr, m * n);
+}
+#endif
+
+void *
+pkgconf_reallocarray(void *ptr, size_t m, size_t n)
+{
+ return reallocarray(ptr, m, n);
+}
+
+int
+pkgconf_pledge(const char *promises, const char *execpromises)
+{
+ return pledge(promises, execpromises);
+}
+
+int
+pkgconf_unveil(const char *path, const char *permissions)
+{
+ return unveil(path, permissions);
+}
diff --git a/libpkgconf/bsdstubs.h b/libpkgconf/bsdstubs.h
new file mode 100644
index 000000000000..21b9432a4a55
--- /dev/null
+++ b/libpkgconf/bsdstubs.h
@@ -0,0 +1,36 @@
+/*
+ * bsdstubs.h
+ * Header for stub BSD function prototypes if unavailable on a specific platform.
+ *
+ * Copyright (c) 2012 William Pitcock <nenolod@dereferenced.org>.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#ifndef LIBPKGCONF_BSDSTUBS_H
+#define LIBPKGCONF_BSDSTUBS_H
+
+#include <libpkgconf/libpkgconf-api.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+PKGCONF_API extern size_t pkgconf_strlcpy(char *dst, const char *src, size_t siz);
+PKGCONF_API extern size_t pkgconf_strlcat(char *dst, const char *src, size_t siz);
+PKGCONF_API extern char *pkgconf_strndup(const char *src, size_t len);
+PKGCONF_API extern void *pkgconf_reallocarray(void *ptr, size_t m, size_t n);
+PKGCONF_API extern int pkgconf_pledge(const char *promises, const char *execpromises);
+PKGCONF_API extern int pkgconf_unveil(const char *path, const char *permissions);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libpkgconf/buffer.c b/libpkgconf/buffer.c
new file mode 100644
index 000000000000..6857022db414
--- /dev/null
+++ b/libpkgconf/buffer.c
@@ -0,0 +1,87 @@
+/*
+ * buffer.c
+ * dynamically-managed buffers
+ *
+ * Copyright (c) 2024 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !doc
+ *
+ * libpkgconf `buffer` module
+ * ==========================
+ *
+ * The libpkgconf `buffer` module contains the functions related to managing
+ * dynamically-allocated buffers.
+ */
+
+static inline size_t
+target_allocation_size(size_t target_size)
+{
+ return 4096 + (4096 * (target_size / 4096));
+}
+
+void
+pkgconf_buffer_append(pkgconf_buffer_t *buffer, const char *text)
+{
+ size_t needed = strlen(text) + 1;
+ size_t newsize = pkgconf_buffer_len(buffer) + needed;
+
+ char *newbase = realloc(buffer->base, target_allocation_size(newsize));
+
+ /* XXX: silently failing here is antisocial */
+ if (newbase == NULL)
+ return;
+
+ char *newend = newbase + pkgconf_buffer_len(buffer);
+ pkgconf_strlcpy(newend, text, needed);
+
+ buffer->base = newbase;
+ buffer->end = newend + needed;
+}
+
+void
+pkgconf_buffer_push_byte(pkgconf_buffer_t *buffer, char byte)
+{
+ size_t newsize = pkgconf_buffer_len(buffer) + 1;
+ char *newbase = realloc(buffer->base, target_allocation_size(newsize));
+
+ /* XXX: silently failing here remains antisocial */
+ if (newbase == NULL)
+ return;
+
+ char *newend = newbase + newsize;
+ *(newend - 1) = byte;
+ *newend = '\0';
+
+ buffer->base = newbase;
+ buffer->end = newend;
+}
+
+void
+pkgconf_buffer_trim_byte(pkgconf_buffer_t *buffer)
+{
+ size_t newsize = pkgconf_buffer_len(buffer) - 1;
+ char *newbase = realloc(buffer->base, target_allocation_size(newsize));
+
+ buffer->base = newbase;
+ buffer->end = newbase + newsize;
+ *(buffer->end) = '\0';
+}
+
+void
+pkgconf_buffer_finalize(pkgconf_buffer_t *buffer)
+{
+ free(buffer->base);
+}
diff --git a/libpkgconf/cache.c b/libpkgconf/cache.c
new file mode 100644
index 000000000000..883c8df7127e
--- /dev/null
+++ b/libpkgconf/cache.c
@@ -0,0 +1,231 @@
+/*
+ * cache.c
+ * package object cache
+ *
+ * Copyright (c) 2013 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+#include <assert.h>
+
+/*
+ * !doc
+ *
+ * libpkgconf `cache` module
+ * =========================
+ *
+ * The libpkgconf `cache` module manages a package/module object cache, allowing it to
+ * avoid loading duplicate copies of a package/module.
+ *
+ * A cache is tied to a specific pkgconf client object, so package objects should not
+ * be shared across threads.
+ */
+
+static int
+cache_member_cmp(const void *a, const void *b)
+{
+ const char *key = a;
+ const pkgconf_pkg_t *pkg = *(void **) b;
+
+ return strcmp(key, pkg->id);
+}
+
+static int
+cache_member_sort_cmp(const void *a, const void *b)
+{
+ const pkgconf_pkg_t *pkgA = *(void **) a;
+ const pkgconf_pkg_t *pkgB = *(void **) b;
+
+ if (pkgA == NULL)
+ return 1;
+
+ if (pkgB == NULL)
+ return -1;
+
+ return strcmp(pkgA->id, pkgB->id);
+}
+
+static void
+cache_dump(const pkgconf_client_t *client)
+{
+ size_t i;
+
+ PKGCONF_TRACE(client, "dumping package cache contents");
+
+ for (i = 0; i < client->cache_count; i++)
+ {
+ const pkgconf_pkg_t *pkg = client->cache_table[i];
+
+ PKGCONF_TRACE(client, SIZE_FMT_SPECIFIER": %p(%s)",
+ i, pkg, pkg == NULL ? "NULL" : pkg->id);
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_pkg_t *pkgconf_cache_lookup(const pkgconf_client_t *client, const char *id)
+ *
+ * Looks up a package in the cache given an `id` atom,
+ * such as ``gtk+-3.0`` and returns the already loaded version
+ * if present.
+ *
+ * :param pkgconf_client_t* client: The client object to access.
+ * :param char* id: The package atom to look up in the client object's cache.
+ * :return: A package object if present, else ``NULL``.
+ * :rtype: pkgconf_pkg_t *
+ */
+pkgconf_pkg_t *
+pkgconf_cache_lookup(pkgconf_client_t *client, const char *id)
+{
+ if (client->cache_table == NULL)
+ return NULL;
+
+ pkgconf_pkg_t **pkg;
+
+ pkg = bsearch(id, client->cache_table,
+ client->cache_count, sizeof (void *),
+ cache_member_cmp);
+
+ if (pkg != NULL)
+ {
+ PKGCONF_TRACE(client, "found: %s @%p", id, *pkg);
+ return pkgconf_pkg_ref(client, *pkg);
+ }
+
+ PKGCONF_TRACE(client, "miss: %s", id);
+ return NULL;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+ *
+ * Adds an entry for the package to the package cache.
+ * The cache entry must be removed if the package is freed.
+ *
+ * :param pkgconf_client_t* client: The client object to modify.
+ * :param pkgconf_pkg_t* pkg: The package object to add to the client object's cache.
+ * :return: nothing
+ */
+void
+pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+{
+ if (pkg == NULL)
+ return;
+
+ pkgconf_pkg_ref(client, pkg);
+
+ PKGCONF_TRACE(client, "added @%p to cache", pkg);
+
+ /* mark package as cached */
+ pkg->flags |= PKGCONF_PKG_PROPF_CACHED;
+
+ ++client->cache_count;
+ client->cache_table = pkgconf_reallocarray(client->cache_table,
+ client->cache_count, sizeof (void *));
+ client->cache_table[client->cache_count - 1] = pkg;
+
+ qsort(client->cache_table, client->cache_count,
+ sizeof(void *), cache_member_sort_cmp);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+ *
+ * Deletes a package from the client object's package cache.
+ *
+ * :param pkgconf_client_t* client: The client object to modify.
+ * :param pkgconf_pkg_t* pkg: The package object to remove from the client object's cache.
+ * :return: nothing
+ */
+void
+pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+{
+ if (client->cache_table == NULL)
+ return;
+
+ if (pkg == NULL)
+ return;
+
+ if (!(pkg->flags & PKGCONF_PKG_PROPF_CACHED))
+ return;
+
+ PKGCONF_TRACE(client, "removed @%p from cache", pkg);
+
+ pkgconf_pkg_t **slot;
+
+ slot = bsearch(pkg->id, client->cache_table,
+ client->cache_count, sizeof (void *),
+ cache_member_cmp);
+
+ if (slot == NULL)
+ return;
+
+ (*slot)->flags &= ~PKGCONF_PKG_PROPF_CACHED;
+ pkgconf_pkg_unref(client, *slot);
+ *slot = NULL;
+
+ qsort(client->cache_table, client->cache_count,
+ sizeof(void *), cache_member_sort_cmp);
+
+ if (client->cache_table[client->cache_count - 1] != NULL)
+ {
+ PKGCONF_TRACE(client, "end of cache table refers to %p, not NULL",
+ client->cache_table[client->cache_count - 1]);
+ cache_dump(client);
+ abort();
+ }
+
+ client->cache_count--;
+ if (client->cache_count > 0)
+ {
+ client->cache_table = pkgconf_reallocarray(client->cache_table,
+ client->cache_count, sizeof(void *));
+ }
+ else
+ {
+ free(client->cache_table);
+ client->cache_table = NULL;
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_cache_free(pkgconf_client_t *client)
+ *
+ * Releases all resources related to a client object's package cache.
+ * This function should only be called to clear a client object's package cache,
+ * as it may release any package in the cache.
+ *
+ * :param pkgconf_client_t* client: The client object to modify.
+ */
+void
+pkgconf_cache_free(pkgconf_client_t *client)
+{
+ if (client->cache_table == NULL)
+ return;
+
+ while (client->cache_count > 0)
+ pkgconf_cache_remove(client, client->cache_table[0]);
+
+ free(client->cache_table);
+ client->cache_table = NULL;
+ client->cache_count = 0;
+
+ PKGCONF_TRACE(client, "cleared package cache");
+}
diff --git a/libpkgconf/client.c b/libpkgconf/client.c
new file mode 100644
index 000000000000..4fe36ecd9af9
--- /dev/null
+++ b/libpkgconf/client.c
@@ -0,0 +1,817 @@
+/*
+ * client.c
+ * libpkgconf consumer lifecycle management
+ *
+ * Copyright (c) 2016 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/config.h>
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !doc
+ *
+ * libpkgconf `client` module
+ * ==========================
+ *
+ * The libpkgconf `client` module implements the `pkgconf_client_t` "client" object.
+ * Client objects store all necessary state for libpkgconf allowing for multiple instances to run
+ * in parallel.
+ *
+ * Client objects are not thread safe, in other words, a client object should not be shared across
+ * thread boundaries.
+ */
+
+static void
+trace_path_list(const pkgconf_client_t *client, const char *desc, pkgconf_list_t *list)
+{
+ const pkgconf_node_t *n;
+
+ PKGCONF_TRACE(client, "%s:", desc);
+ PKGCONF_FOREACH_LIST_ENTRY(list->head, n)
+ {
+ const pkgconf_path_t *p = n->data;
+
+ PKGCONF_TRACE(client, " - '%s'", p->path);
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_client_dir_list_build(pkgconf_client_t *client)
+ *
+ * Bootstraps the package search paths. If the ``PKGCONF_PKG_PKGF_ENV_ONLY`` `flag` is set on the client,
+ * then only the ``PKG_CONFIG_PATH`` environment variable will be used, otherwise both the
+ * ``PKG_CONFIG_PATH`` and ``PKG_CONFIG_LIBDIR`` environment variables will be used.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to bootstrap.
+ * :return: nothing
+ */
+void
+pkgconf_client_dir_list_build(pkgconf_client_t *client, const pkgconf_cross_personality_t *personality)
+{
+ pkgconf_path_build_from_environ("PKG_CONFIG_PATH", NULL, &client->dir_list, true);
+
+ if (!(client->flags & PKGCONF_PKG_PKGF_ENV_ONLY))
+ {
+ pkgconf_list_t dir_list = PKGCONF_LIST_INITIALIZER;
+ const pkgconf_list_t *prepend_list = &personality->dir_list;
+
+#ifdef _WIN32
+ (void) pkgconf_path_build_from_registry(HKEY_CURRENT_USER, &client->dir_list, true);
+ (void) pkgconf_path_build_from_registry(HKEY_LOCAL_MACHINE, &client->dir_list, true);
+#endif
+
+ if (getenv("PKG_CONFIG_LIBDIR") != NULL)
+ {
+ /* PKG_CONFIG_LIBDIR= should empty the search path entirely. */
+ (void) pkgconf_path_build_from_environ("PKG_CONFIG_LIBDIR", NULL, &dir_list, true);
+ prepend_list = &dir_list;
+ }
+
+ pkgconf_path_copy_list(&client->dir_list, prepend_list);
+ pkgconf_path_free(&dir_list);
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality)
+ *
+ * Initialise a pkgconf client object.
+ *
+ * :param pkgconf_client_t* client: The client to initialise.
+ * :param pkgconf_error_handler_func_t error_handler: An optional error handler to use for logging errors.
+ * :param void* error_handler_data: user data passed to optional error handler
+ * :param pkgconf_cross_personality_t* personality: the cross-compile personality to use for defaults
+ * :return: nothing
+ */
+void
+pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality)
+{
+ client->error_handler_data = error_handler_data;
+ client->error_handler = error_handler;
+ client->auditf = NULL;
+ client->cache_table = NULL;
+ client->cache_count = 0;
+
+#ifndef PKGCONF_LITE
+ if (client->trace_handler == NULL)
+ pkgconf_client_set_trace_handler(client, NULL, NULL);
+#endif
+
+ if (client->unveil_handler == NULL)
+ pkgconf_client_set_unveil_handler(client, NULL);
+
+ pkgconf_client_set_error_handler(client, error_handler, error_handler_data);
+ pkgconf_client_set_warn_handler(client, NULL, NULL);
+
+ pkgconf_client_set_sysroot_dir(client, personality->sysroot_dir);
+ pkgconf_client_set_buildroot_dir(client, NULL);
+ pkgconf_client_set_prefix_varname(client, NULL);
+
+ if(getenv("PKG_CONFIG_SYSTEM_LIBRARY_PATH") == NULL)
+ pkgconf_path_copy_list(&client->filter_libdirs, &personality->filter_libdirs);
+ else
+ pkgconf_path_build_from_environ("PKG_CONFIG_SYSTEM_LIBRARY_PATH", NULL, &client->filter_libdirs, false);
+
+ if(getenv("PKG_CONFIG_SYSTEM_INCLUDE_PATH") == NULL)
+ pkgconf_path_copy_list(&client->filter_includedirs, &personality->filter_includedirs);
+ else
+ pkgconf_path_build_from_environ("PKG_CONFIG_SYSTEM_INCLUDE_PATH", NULL, &client->filter_includedirs, false);
+
+ /* GCC uses these environment variables to define system include paths, so we should check them. */
+#ifdef __HAIKU__
+ pkgconf_path_build_from_environ("BELIBRARIES", NULL, &client->filter_libdirs, false);
+#else
+ pkgconf_path_build_from_environ("LIBRARY_PATH", NULL, &client->filter_libdirs, false);
+#endif
+ pkgconf_path_build_from_environ("CPATH", NULL, &client->filter_includedirs, false);
+ pkgconf_path_build_from_environ("C_INCLUDE_PATH", NULL, &client->filter_includedirs, false);
+ pkgconf_path_build_from_environ("CPLUS_INCLUDE_PATH", NULL, &client->filter_includedirs, false);
+ pkgconf_path_build_from_environ("OBJC_INCLUDE_PATH", NULL, &client->filter_includedirs, false);
+
+#ifdef _WIN32
+ /* also use the path lists that MSVC uses on windows */
+ pkgconf_path_build_from_environ("INCLUDE", NULL, &client->filter_includedirs, false);
+#endif
+
+ PKGCONF_TRACE(client, "initialized client @%p", client);
+
+ trace_path_list(client, "filtered library paths", &client->filter_libdirs);
+ trace_path_list(client, "filtered include paths", &client->filter_includedirs);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_client_t* pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality)
+ *
+ * Allocate and initialise a pkgconf client object.
+ *
+ * :param pkgconf_error_handler_func_t error_handler: An optional error handler to use for logging errors.
+ * :param void* error_handler_data: user data passed to optional error handler
+ * :param pkgconf_cross_personality_t* personality: cross-compile personality to use
+ * :return: A pkgconf client object.
+ * :rtype: pkgconf_client_t*
+ */
+pkgconf_client_t *
+pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality)
+{
+ pkgconf_client_t *out = calloc(1, sizeof(pkgconf_client_t));
+ if (out == NULL)
+ return NULL;
+
+ pkgconf_client_init(out, error_handler, error_handler_data, personality);
+ return out;
+}
+
+static void
+unref_preload_list(pkgconf_client_t *client)
+{
+ pkgconf_node_t *n, *tn;
+
+ PKGCONF_FOREACH_LIST_ENTRY_SAFE(client->preloaded_pkgs.head, n, tn)
+ {
+ pkgconf_pkg_t *pkg = n->data;
+ pkgconf_pkg_unref(client, pkg);
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_client_deinit(pkgconf_client_t *client)
+ *
+ * Release resources belonging to a pkgconf client object.
+ *
+ * :param pkgconf_client_t* client: The client to deinitialise.
+ * :return: nothing
+ */
+void
+pkgconf_client_deinit(pkgconf_client_t *client)
+{
+ PKGCONF_TRACE(client, "deinit @%p", client);
+
+ unref_preload_list(client);
+
+ if (client->prefix_varname != NULL)
+ free(client->prefix_varname);
+
+ if (client->sysroot_dir != NULL)
+ free(client->sysroot_dir);
+
+ if (client->buildroot_dir != NULL)
+ free(client->buildroot_dir);
+
+ pkgconf_path_free(&client->filter_libdirs);
+ pkgconf_path_free(&client->filter_includedirs);
+
+ pkgconf_tuple_free_global(client);
+ pkgconf_path_free(&client->dir_list);
+ pkgconf_cache_free(client);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_client_free(pkgconf_client_t *client)
+ *
+ * Release resources belonging to a pkgconf client object and then free the client object itself.
+ *
+ * :param pkgconf_client_t* client: The client to deinitialise and free.
+ * :return: nothing
+ */
+void
+pkgconf_client_free(pkgconf_client_t *client)
+{
+ pkgconf_client_deinit(client);
+ free(client);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: const char *pkgconf_client_get_sysroot_dir(const pkgconf_client_t *client)
+ *
+ * Retrieves the client's sysroot directory (if any).
+ *
+ * :param pkgconf_client_t* client: The client object being accessed.
+ * :return: A string containing the sysroot directory or NULL.
+ * :rtype: const char *
+ */
+const char *
+pkgconf_client_get_sysroot_dir(const pkgconf_client_t *client)
+{
+ return client->sysroot_dir;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_client_set_sysroot_dir(pkgconf_client_t *client, const char *sysroot_dir)
+ *
+ * Sets or clears the sysroot directory on a client object. Any previous sysroot directory setting is
+ * automatically released if one was previously set.
+ *
+ * Additionally, the global tuple ``$(pc_sysrootdir)`` is set as appropriate based on the new setting.
+ *
+ * :param pkgconf_client_t* client: The client object being modified.
+ * :param char* sysroot_dir: The sysroot directory to set or NULL to unset.
+ * :return: nothing
+ */
+void
+pkgconf_client_set_sysroot_dir(pkgconf_client_t *client, const char *sysroot_dir)
+{
+ if (client->sysroot_dir != NULL)
+ free(client->sysroot_dir);
+
+ client->sysroot_dir = sysroot_dir != NULL ? strdup(sysroot_dir) : NULL;
+
+ PKGCONF_TRACE(client, "set sysroot_dir to: %s", client->sysroot_dir != NULL ? client->sysroot_dir : "<default>");
+
+ pkgconf_tuple_add_global(client, "pc_sysrootdir", client->sysroot_dir != NULL ? client->sysroot_dir : "/");
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: const char *pkgconf_client_get_buildroot_dir(const pkgconf_client_t *client)
+ *
+ * Retrieves the client's buildroot directory (if any).
+ *
+ * :param pkgconf_client_t* client: The client object being accessed.
+ * :return: A string containing the buildroot directory or NULL.
+ * :rtype: const char *
+ */
+const char *
+pkgconf_client_get_buildroot_dir(const pkgconf_client_t *client)
+{
+ return client->buildroot_dir;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_client_set_buildroot_dir(pkgconf_client_t *client, const char *buildroot_dir)
+ *
+ * Sets or clears the buildroot directory on a client object. Any previous buildroot directory setting is
+ * automatically released if one was previously set.
+ *
+ * Additionally, the global tuple ``$(pc_top_builddir)`` is set as appropriate based on the new setting.
+ *
+ * :param pkgconf_client_t* client: The client object being modified.
+ * :param char* buildroot_dir: The buildroot directory to set or NULL to unset.
+ * :return: nothing
+ */
+void
+pkgconf_client_set_buildroot_dir(pkgconf_client_t *client, const char *buildroot_dir)
+{
+ if (client->buildroot_dir != NULL)
+ free(client->buildroot_dir);
+
+ client->buildroot_dir = buildroot_dir != NULL ? strdup(buildroot_dir) : NULL;
+
+ PKGCONF_TRACE(client, "set buildroot_dir to: %s", client->buildroot_dir != NULL ? client->buildroot_dir : "<default>");
+
+ pkgconf_tuple_add_global(client, "pc_top_builddir", client->buildroot_dir != NULL ? client->buildroot_dir : "$(top_builddir)");
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_error(const pkgconf_client_t *client, const char *format, ...)
+ *
+ * Report an error to a client-registered error handler.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to report the error to.
+ * :param char* format: A printf-style format string to use for formatting the error message.
+ * :return: true if the error handler processed the message, else false.
+ * :rtype: bool
+ */
+bool
+pkgconf_error(const pkgconf_client_t *client, const char *format, ...)
+{
+ char *errbuf;
+ ssize_t msgsize = 0;
+ bool ret;
+ va_list va;
+
+ va_start(va, format);
+ msgsize = vsnprintf(NULL, 0, format, va);
+ va_end(va);
+
+ if (msgsize < 0)
+ return false;
+
+ msgsize++;
+
+ errbuf = calloc(1, msgsize);
+ if (errbuf == NULL)
+ return false;
+
+ va_start(va, format);
+ vsnprintf(errbuf, msgsize, format, va);
+ va_end(va);
+
+ ret = client->error_handler(errbuf, client, client->error_handler_data);
+ free(errbuf);
+
+ return ret;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_warn(const pkgconf_client_t *client, const char *format, ...)
+ *
+ * Report an error to a client-registered warn handler.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to report the error to.
+ * :param char* format: A printf-style format string to use for formatting the warning message.
+ * :return: true if the warn handler processed the message, else false.
+ * :rtype: bool
+ */
+bool
+pkgconf_warn(const pkgconf_client_t *client, const char *format, ...)
+{
+ char *errbuf;
+ ssize_t msgsize = 0;
+ bool ret;
+ va_list va;
+
+ va_start(va, format);
+ msgsize = vsnprintf(NULL, 0, format, va);
+ va_end(va);
+
+ if (msgsize < 0)
+ return false;
+
+ msgsize++;
+
+ errbuf = calloc(1, msgsize);
+ if (errbuf == NULL)
+ return false;
+
+ va_start(va, format);
+ vsnprintf(errbuf, msgsize, format, va);
+ va_end(va);
+
+ ret = client->warn_handler(errbuf, client, client->warn_handler_data);
+ free(errbuf);
+
+ return ret;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t len, const char *funcname, const char *format, ...)
+ *
+ * Report a message to a client-registered trace handler.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to report the trace message to.
+ * :param char* filename: The file the function is in.
+ * :param size_t lineno: The line number currently being executed.
+ * :param char* funcname: The function name to use.
+ * :param char* format: A printf-style format string to use for formatting the trace message.
+ * :return: true if the trace handler processed the message, else false.
+ * :rtype: bool
+ */
+bool
+pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t lineno, const char *funcname, const char *format, ...)
+{
+ char prefix[PKGCONF_ITEM_SIZE];
+ char *errbuf = NULL;
+ ssize_t errlen;
+ char *finalbuf = NULL;
+ ssize_t finallen;
+ bool ret;
+ va_list va;
+
+ if (client == NULL || client->trace_handler == NULL)
+ return false;
+
+ snprintf(prefix, sizeof prefix, "%s:" SIZE_FMT_SPECIFIER " [%s]:", filename, lineno, funcname);
+
+ va_start(va, format);
+ errlen = vsnprintf(NULL, 0, format, va);
+ va_end(va);
+
+ if (errlen < 0)
+ return false;
+
+ errlen++;
+ errbuf = calloc(1, errlen);
+ if (errbuf == NULL)
+ return false;
+
+ va_start(va, format);
+ vsnprintf(errbuf, errlen, format, va);
+ va_end(va);
+
+ finallen = snprintf(NULL, 0, "%s %s\n", prefix, errbuf);
+ if (finallen < 0)
+ return false;
+
+ finallen++;
+ finalbuf = calloc(1, finallen);
+ if (finalbuf == NULL)
+ return false;
+
+ snprintf(finalbuf, finallen, "%s %s\n", prefix, errbuf);
+ ret = client->trace_handler(finalbuf, client, client->trace_handler_data);
+ free(errbuf);
+ free(finalbuf);
+
+ return ret;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_default_error_handler(const char *msg, const pkgconf_client_t *client, const void *data)
+ *
+ * The default pkgconf error handler.
+ *
+ * :param char* msg: The error message to handle.
+ * :param pkgconf_client_t* client: The client object the error originated from.
+ * :param void* data: An opaque pointer to extra data associated with the client for error handling.
+ * :return: true (the function does nothing to process the message)
+ * :rtype: bool
+ */
+bool
+pkgconf_default_error_handler(const char *msg, const pkgconf_client_t *client, void *data)
+{
+ (void) msg;
+ (void) client;
+ (void) data;
+
+ return true;
+}
+
+static void
+default_unveil_handler(const pkgconf_client_t *client, const char *path, const char *permissions)
+{
+ (void) client;
+ (void) path;
+ (void) permissions;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: unsigned int pkgconf_client_get_flags(const pkgconf_client_t *client)
+ *
+ * Retrieves resolver-specific flags associated with a client object.
+ *
+ * :param pkgconf_client_t* client: The client object to retrieve the resolver-specific flags from.
+ * :return: a bitfield of resolver-specific flags
+ * :rtype: uint
+ */
+unsigned int
+pkgconf_client_get_flags(const pkgconf_client_t *client)
+{
+ return client->flags;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_client_set_flags(pkgconf_client_t *client, unsigned int flags)
+ *
+ * Sets resolver-specific flags associated with a client object.
+ *
+ * :param pkgconf_client_t* client: The client object to set the resolver-specific flags on.
+ * :return: nothing
+ */
+void
+pkgconf_client_set_flags(pkgconf_client_t *client, unsigned int flags)
+{
+ client->flags = flags;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: const char *pkgconf_client_get_prefix_varname(const pkgconf_client_t *client)
+ *
+ * Retrieves the name of the variable that should contain a module's prefix.
+ * In some cases, it is necessary to override this variable to allow proper path relocation.
+ *
+ * :param pkgconf_client_t* client: The client object to retrieve the prefix variable name from.
+ * :return: the prefix variable name as a string
+ * :rtype: const char *
+ */
+const char *
+pkgconf_client_get_prefix_varname(const pkgconf_client_t *client)
+{
+ return client->prefix_varname;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_client_set_prefix_varname(pkgconf_client_t *client, const char *prefix_varname)
+ *
+ * Sets the name of the variable that should contain a module's prefix.
+ * If the variable name is ``NULL``, then the default variable name (``prefix``) is used.
+ *
+ * :param pkgconf_client_t* client: The client object to set the prefix variable name on.
+ * :param char* prefix_varname: The prefix variable name to set.
+ * :return: nothing
+ */
+void
+pkgconf_client_set_prefix_varname(pkgconf_client_t *client, const char *prefix_varname)
+{
+ if (prefix_varname == NULL)
+ prefix_varname = "prefix";
+
+ if (client->prefix_varname != NULL)
+ free(client->prefix_varname);
+
+ client->prefix_varname = strdup(prefix_varname);
+
+ PKGCONF_TRACE(client, "set prefix_varname to: %s", client->prefix_varname);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_client_get_warn_handler(const pkgconf_client_t *client)
+ *
+ * Returns the warning handler if one is set, else ``NULL``.
+ *
+ * :param pkgconf_client_t* client: The client object to get the warn handler from.
+ * :return: a function pointer to the warn handler or ``NULL``
+ */
+pkgconf_error_handler_func_t
+pkgconf_client_get_warn_handler(const pkgconf_client_t *client)
+{
+ return client->warn_handler;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_client_set_warn_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t warn_handler, void *warn_handler_data)
+ *
+ * Sets a warn handler on a client object or uninstalls one if set to ``NULL``.
+ *
+ * :param pkgconf_client_t* client: The client object to set the warn handler on.
+ * :param pkgconf_error_handler_func_t warn_handler: The warn handler to set.
+ * :param void* warn_handler_data: Optional data to associate with the warn handler.
+ * :return: nothing
+ */
+void
+pkgconf_client_set_warn_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t warn_handler, void *warn_handler_data)
+{
+ client->warn_handler = warn_handler;
+ client->warn_handler_data = warn_handler_data;
+
+ if (client->warn_handler == NULL)
+ {
+ PKGCONF_TRACE(client, "installing default warn handler");
+ client->warn_handler = pkgconf_default_error_handler;
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_client_get_error_handler(const pkgconf_client_t *client)
+ *
+ * Returns the error handler if one is set, else ``NULL``.
+ *
+ * :param pkgconf_client_t* client: The client object to get the error handler from.
+ * :return: a function pointer to the error handler or ``NULL``
+ */
+pkgconf_error_handler_func_t
+pkgconf_client_get_error_handler(const pkgconf_client_t *client)
+{
+ return client->error_handler;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_client_set_error_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data)
+ *
+ * Sets a warn handler on a client object or uninstalls one if set to ``NULL``.
+ *
+ * :param pkgconf_client_t* client: The client object to set the error handler on.
+ * :param pkgconf_error_handler_func_t error_handler: The error handler to set.
+ * :param void* error_handler_data: Optional data to associate with the error handler.
+ * :return: nothing
+ */
+void
+pkgconf_client_set_error_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data)
+{
+ client->error_handler = error_handler;
+ client->error_handler_data = error_handler_data;
+
+ if (client->error_handler == NULL)
+ {
+ PKGCONF_TRACE(client, "installing default error handler");
+ client->error_handler = pkgconf_default_error_handler;
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_client_get_unveil_handler(const pkgconf_client_t *client)
+ *
+ * Returns the unveil handler if one is set, else ``NULL``.
+ *
+ * :param pkgconf_client_t* client: The client object to get the unveil handler from.
+ * :return: a function pointer to the error handler or ``NULL``
+ */
+pkgconf_unveil_handler_func_t
+pkgconf_client_get_unveil_handler(const pkgconf_client_t *client)
+{
+ return client->unveil_handler;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_client_set_unveil_handler(pkgconf_client_t *client, pkgconf_unveil_handler_func_t unveil_handler)
+ *
+ * Sets an unveil handler on a client object or uninstalls one if set to ``NULL``.
+ *
+ * :param pkgconf_client_t* client: The client object to set the error handler on.
+ * :param pkgconf_unveil_handler_func_t unveil_handler: The unveil handler to set.
+ * :return: nothing
+ */
+void
+pkgconf_client_set_unveil_handler(pkgconf_client_t *client, pkgconf_unveil_handler_func_t unveil_handler)
+{
+ client->unveil_handler = unveil_handler;
+
+ if (client->unveil_handler == NULL)
+ {
+ PKGCONF_TRACE(client, "installing default unveil handler");
+ client->unveil_handler = default_unveil_handler;
+ }
+}
+
+#ifndef PKGCONF_LITE
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_client_get_trace_handler(const pkgconf_client_t *client)
+ *
+ * Returns the error handler if one is set, else ``NULL``.
+ *
+ * :param pkgconf_client_t* client: The client object to get the error handler from.
+ * :return: a function pointer to the error handler or ``NULL``
+ */
+pkgconf_error_handler_func_t
+pkgconf_client_get_trace_handler(const pkgconf_client_t *client)
+{
+ return client->trace_handler;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t trace_handler, void *trace_handler_data)
+ *
+ * Sets a warn handler on a client object or uninstalls one if set to ``NULL``.
+ *
+ * :param pkgconf_client_t* client: The client object to set the error handler on.
+ * :param pkgconf_error_handler_func_t trace_handler: The error handler to set.
+ * :param void* trace_handler_data: Optional data to associate with the error handler.
+ * :return: nothing
+ */
+void
+pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t trace_handler, void *trace_handler_data)
+{
+ client->trace_handler = trace_handler;
+ client->trace_handler_data = trace_handler_data;
+
+ if (client->trace_handler == NULL)
+ {
+ client->trace_handler = pkgconf_default_error_handler;
+ PKGCONF_TRACE(client, "installing default trace handler");
+ }
+}
+#endif
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_client_preload_path(pkgconf_client_t *client, const char *path)
+ *
+ * Loads a pkg-config file into the preloaded packages set.
+ *
+ * :param pkgconf_client_t* client: The client object for preloading.
+ * :param char* path: The path to the pkg-config file to preload.
+ * :return: true on success, false on error
+ * :rtype: bool
+ */
+bool
+pkgconf_client_preload_path(pkgconf_client_t *client, const char *path)
+{
+ pkgconf_pkg_t *pkg = pkgconf_pkg_new_from_path(client, path, PKGCONF_PKG_PROPF_PRELOADED);
+ if (pkg == NULL)
+ return false;
+
+ pkgconf_pkg_ref(client, pkg);
+ pkgconf_node_insert_tail(&pkg->preload_node, pkg, &client->preloaded_pkgs);
+
+ return true;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_client_preload_from_environ(pkgconf_client_t *client, const char *env)
+ *
+ * Loads zero or more pkg-config files specified in the given environmental
+ * variable.
+ *
+ * :param pkgconf_client_t* client: The client object for preloading.
+ * :param char* environ: The environment variable to use for preloading.
+ * :return: true on success, false on error
+ * :rtype: bool
+ */
+bool
+pkgconf_client_preload_from_environ(pkgconf_client_t *client, const char *env)
+{
+ const char *data;
+ pkgconf_list_t pathlist = PKGCONF_LIST_INITIALIZER;
+ pkgconf_node_t *n;
+ bool ret;
+
+ data = getenv(env);
+ if (data == NULL)
+ return true;
+
+ pkgconf_path_split(data, &pathlist, true);
+
+ PKGCONF_FOREACH_LIST_ENTRY(pathlist.head, n)
+ {
+ pkgconf_path_t *pn = n->data;
+
+ ret = pkgconf_client_preload_path(client, pn->path);
+ if (!ret)
+ break;
+ }
+
+ pkgconf_path_free(&pathlist);
+
+ return ret;
+}
diff --git a/libpkgconf/config.h.meson b/libpkgconf/config.h.meson
new file mode 100644
index 000000000000..2ea7db2e20de
--- /dev/null
+++ b/libpkgconf/config.h.meson
@@ -0,0 +1,79 @@
+/* libpkgconf/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 if you have the `strlcat' function. */
+#mesondefine HAVE_STRLCAT
+
+/* Define to 1 if you have the `strlcpy' function. */
+#mesondefine HAVE_STRLCPY
+
+/* Define to 1 if you have the `strndup' function. */
+#mesondefine HAVE_STRNDUP
+
+/* Define to 1 if you have the `reallocarray' function. */
+#mesondefine HAVE_REALLOCARRAY
+
+/* Define to 1 if you have the `strlcat' function. */
+#mesondefine HAVE_DECL_STRLCAT
+
+/* Define to 1 if you have the `strlcpy' function. */
+#mesondefine HAVE_DECL_STRLCPY
+
+/* Define to 1 if you have the `strndup' function. */
+#mesondefine HAVE_DECL_STRNDUP
+
+/* Define to 1 if you have the `reallocarray' function. */
+#mesondefine HAVE_DECL_REALLOCARRAY
+
+/* Define to 1 if you have the `pledge' function. */
+#mesondefine HAVE_DECL_PLEDGE
+
+/* Define to 1 if you have the `unveil' function. */
+#mesondefine HAVE_DECL_UNVEIL
+
+/* Name of package */
+#mesondefine PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#mesondefine PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#mesondefine PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#mesondefine PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#mesondefine PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#mesondefine PACKAGE_URL
+
+/* Define to the version of this package. */
+#mesondefine PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#mesondefine STDC_HEADERS
+
+/* Version number of package */
+#mesondefine VERSION
+
+/* Enable large inode numbers on Mac OS X 10.5. */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#mesondefine _FILE_OFFSET_BITS
+
+/* Define for large files, on AIX-style hosts. */
+#mesondefine _LARGE_FILES
+
+#mesondefine PKG_DEFAULT_PATH
+#mesondefine SYSTEM_INCLUDEDIR
+#mesondefine SYSTEM_LIBDIR
+#mesondefine PERSONALITY_PATH
+
+/* Enable Solaris extensions. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
diff --git a/libpkgconf/dependency.c b/libpkgconf/dependency.c
new file mode 100644
index 000000000000..9b64809a4114
--- /dev/null
+++ b/libpkgconf/dependency.c
@@ -0,0 +1,506 @@
+/*
+ * dependency.c
+ * dependency parsing and management
+ *
+ * Copyright (c) 2011, 2012, 2013 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !doc
+ *
+ * libpkgconf `dependency` module
+ * ==============================
+ *
+ * The `dependency` module provides support for building `dependency lists` (the basic component of the overall `dependency graph`) and
+ * `dependency nodes` which store dependency information.
+ */
+
+typedef enum {
+ OUTSIDE_MODULE = 0,
+ INSIDE_MODULE_NAME = 1,
+ BEFORE_OPERATOR = 2,
+ INSIDE_OPERATOR = 3,
+ AFTER_OPERATOR = 4,
+ INSIDE_VERSION = 5
+} parse_state_t;
+
+#define DEBUG_PARSE 0
+
+static const char *
+dependency_to_str(const pkgconf_dependency_t *dep, char *buf, size_t buflen)
+{
+ pkgconf_strlcpy(buf, dep->package, buflen);
+ if (dep->version != NULL)
+ {
+ pkgconf_strlcat(buf, " ", buflen);
+ pkgconf_strlcat(buf, pkgconf_pkg_get_comparator(dep), buflen);
+ pkgconf_strlcat(buf, " ", buflen);
+ pkgconf_strlcat(buf, dep->version, buflen);
+ }
+
+ return buf;
+}
+
+/* find a colliding dependency that is coloured differently */
+static inline pkgconf_dependency_t *
+find_colliding_dependency(const pkgconf_dependency_t *dep, const pkgconf_list_t *list)
+{
+ const pkgconf_node_t *n;
+
+ PKGCONF_FOREACH_LIST_ENTRY(list->head, n)
+ {
+ pkgconf_dependency_t *dep2 = n->data;
+
+ if (strcmp(dep->package, dep2->package))
+ continue;
+
+ if (dep->flags != dep2->flags)
+ return dep2;
+ }
+
+ return NULL;
+}
+
+static inline pkgconf_dependency_t *
+add_or_replace_dependency_node(pkgconf_client_t *client, pkgconf_dependency_t *dep, pkgconf_list_t *list)
+{
+ char depbuf[PKGCONF_ITEM_SIZE];
+ pkgconf_dependency_t *dep2 = find_colliding_dependency(dep, list);
+
+ /* there is already a node in the graph which describes this dependency */
+ if (dep2 != NULL)
+ {
+ char depbuf2[PKGCONF_ITEM_SIZE];
+
+ PKGCONF_TRACE(client, "dependency collision: [%s/%x] -- [%s/%x]",
+ dependency_to_str(dep, depbuf, sizeof depbuf), dep->flags,
+ dependency_to_str(dep2, depbuf2, sizeof depbuf2), dep2->flags);
+
+ /* prefer the uncoloured node, either dep or dep2 */
+ if (dep->flags && dep2->flags == 0)
+ {
+ PKGCONF_TRACE(client, "dropping dependency [%s]@%p because of collision", depbuf, dep);
+
+ pkgconf_dependency_unref(dep->owner, dep);
+ return NULL;
+ }
+ else if (dep2->flags && dep->flags == 0)
+ {
+ PKGCONF_TRACE(client, "dropping dependency [%s]@%p because of collision", depbuf2, dep2);
+
+ pkgconf_node_delete(&dep2->iter, list);
+ pkgconf_dependency_unref(dep2->owner, dep2);
+ }
+ else
+ /* If both dependencies have equal strength, we keep both, because of situations like:
+ * Requires: foo > 1, foo < 3
+ *
+ * If the situation is that both dependencies are literally equal, it is still harmless because
+ * fragment deduplication will handle the excessive fragments.
+ */
+ PKGCONF_TRACE(client, "keeping both dependencies (harmless)");
+ }
+
+ PKGCONF_TRACE(client, "added dependency [%s] to list @%p; flags=%x", dependency_to_str(dep, depbuf, sizeof depbuf), list, dep->flags);
+ pkgconf_node_insert_tail(&dep->iter, pkgconf_dependency_ref(dep->owner, dep), list);
+
+ /* This dependency is intentionally unowned.
+ *
+ * Internally we have no use for the returned type, and usually just
+ * discard it. However, there is a publig pkgconf_dependency_add
+ * function, which references this return value before returning it,
+ * giving ownership at that point.
+ */
+ return dep;
+}
+
+static inline pkgconf_dependency_t *
+pkgconf_dependency_addraw(pkgconf_client_t *client, pkgconf_list_t *list, const char *package, size_t package_sz, const char *version, size_t version_sz, pkgconf_pkg_comparator_t compare, unsigned int flags)
+{
+ pkgconf_dependency_t *dep;
+
+ dep = calloc(1, sizeof(pkgconf_dependency_t));
+ if (dep == NULL)
+ return NULL;
+
+ dep->package = pkgconf_strndup(package, package_sz);
+
+ if (version_sz != 0)
+ dep->version = pkgconf_strndup(version, version_sz);
+
+ dep->compare = compare;
+ dep->flags = flags;
+ dep->owner = client;
+ dep->refcount = 0;
+
+ return add_or_replace_dependency_node(client, dep, list);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_dependency_t *pkgconf_dependency_add(pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare)
+ *
+ * Adds a parsed dependency to a dependency list as a dependency node.
+ *
+ * :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to.
+ * :param pkgconf_list_t* list: The dependency list to add a dependency node to.
+ * :param char* package: The package `atom` to set on the dependency node.
+ * :param char* version: The package `version` to set on the dependency node.
+ * :param pkgconf_pkg_comparator_t compare: The comparison operator to set on the dependency node.
+ * :param uint flags: Any flags to attach to the dependency node.
+ * :return: A dependency node.
+ * :rtype: pkgconf_dependency_t *
+ */
+pkgconf_dependency_t *
+pkgconf_dependency_add(pkgconf_client_t *client, pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare, unsigned int flags)
+{
+ pkgconf_dependency_t *dep;
+ dep = pkgconf_dependency_addraw(client, list, package, strlen(package), version,
+ version != NULL ? strlen(version) : 0, compare, flags);
+ return pkgconf_dependency_ref(dep->owner, dep);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail)
+ *
+ * Adds a dependency node to a pre-existing dependency list.
+ *
+ * :param pkgconf_list_t* list: The dependency list to add a dependency node to.
+ * :param pkgconf_dependency_t* tail: The dependency node to add to the tail of the dependency list.
+ * :return: nothing
+ */
+void
+pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail)
+{
+ pkgconf_node_insert_tail(&tail->iter, tail, list);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_dependency_free_one(pkgconf_dependency_t *dep)
+ *
+ * Frees a dependency node.
+ *
+ * :param pkgconf_dependency_t* dep: The dependency node to free.
+ * :return: nothing
+ */
+void
+pkgconf_dependency_free_one(pkgconf_dependency_t *dep)
+{
+ if (dep->match != NULL)
+ pkgconf_pkg_unref(dep->match->owner, dep->match);
+
+ if (dep->package != NULL)
+ free(dep->package);
+
+ if (dep->version != NULL)
+ free(dep->version);
+
+ free(dep);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_dependency_t *pkgconf_dependency_ref(pkgconf_client_t *owner, pkgconf_dependency_t *dep)
+ *
+ * Increases a dependency node's refcount.
+ *
+ * :param pkgconf_client_t* owner: The client object which owns the memory of this dependency node.
+ * :param pkgconf_dependency_t* dep: The dependency to increase the refcount of.
+ * :return: the dependency node on success, else NULL
+ */
+pkgconf_dependency_t *
+pkgconf_dependency_ref(pkgconf_client_t *client, pkgconf_dependency_t *dep)
+{
+ if (client != dep->owner)
+ return NULL;
+
+ dep->refcount++;
+ PKGCONF_TRACE(client, "%s refcount@%p: %d", dep->package, dep, dep->refcount);
+ return dep;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_dependency_unref(pkgconf_client_t *owner, pkgconf_dependency_t *dep)
+ *
+ * Decreases a dependency node's refcount and frees it if necessary.
+ *
+ * :param pkgconf_client_t* owner: The client object which owns the memory of this dependency node.
+ * :param pkgconf_dependency_t* dep: The dependency to decrease the refcount of.
+ * :return: nothing
+ */
+void
+pkgconf_dependency_unref(pkgconf_client_t *client, pkgconf_dependency_t *dep)
+{
+ if (client != dep->owner)
+ return;
+
+ --dep->refcount;
+ PKGCONF_TRACE(client, "%s refcount@%p: %d", dep->package, dep, dep->refcount);
+
+ if (dep->refcount <= 0)
+ pkgconf_dependency_free_one(dep);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_dependency_free(pkgconf_list_t *list)
+ *
+ * Release a dependency list and its child dependency nodes.
+ *
+ * :param pkgconf_list_t* list: The dependency list to release.
+ * :return: nothing
+ */
+void
+pkgconf_dependency_free(pkgconf_list_t *list)
+{
+ pkgconf_node_t *node, *next;
+
+ PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+
+ pkgconf_node_delete(&dep->iter, list);
+ pkgconf_dependency_unref(dep->owner, dep);
+ }
+
+ pkgconf_list_zero(list);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_dependency_parse_str(pkgconf_list_t *deplist_head, const char *depends)
+ *
+ * Parse a dependency declaration into a dependency list.
+ * Commas are counted as whitespace to allow for constructs such as ``@SUBSTVAR@, zlib`` being processed
+ * into ``, zlib``.
+ *
+ * :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to.
+ * :param pkgconf_list_t* deplist_head: The dependency list to populate with dependency nodes.
+ * :param char* depends: The dependency data to parse.
+ * :param uint flags: Any flags to attach to the dependency nodes.
+ * :return: nothing
+ */
+void
+pkgconf_dependency_parse_str(pkgconf_client_t *client, pkgconf_list_t *deplist_head, const char *depends, unsigned int flags)
+{
+ parse_state_t state = OUTSIDE_MODULE;
+ pkgconf_pkg_comparator_t compare = PKGCONF_CMP_ANY;
+ char cmpname[PKGCONF_ITEM_SIZE];
+ size_t package_sz = 0, version_sz = 0, buf_sz = 0;
+ char *buf;
+ char *start = NULL;
+ char *ptr = NULL;
+ char *vstart = NULL;
+ char *package = NULL, *version = NULL;
+ char *cnameptr = cmpname;
+ char *cnameend = cmpname + PKGCONF_ITEM_SIZE - 1;
+
+ if (!*depends)
+ return;
+
+ memset(cmpname, '\0', sizeof cmpname);
+
+ buf_sz = strlen(depends) * 2;
+ buf = calloc(1, buf_sz);
+ if (buf == NULL)
+ return;
+
+ pkgconf_strlcpy(buf, depends, buf_sz);
+ pkgconf_strlcat(buf, " ", buf_sz);
+
+ start = ptr = buf;
+
+ while (*ptr)
+ {
+ switch (state)
+ {
+ case OUTSIDE_MODULE:
+ if (!PKGCONF_IS_MODULE_SEPARATOR(*ptr))
+ state = INSIDE_MODULE_NAME;
+
+ break;
+
+ case INSIDE_MODULE_NAME:
+ if (isspace((unsigned char)*ptr))
+ {
+ const char *sptr = ptr;
+
+ while (*sptr && isspace((unsigned char)*sptr))
+ sptr++;
+
+ if (*sptr == '\0')
+ state = OUTSIDE_MODULE;
+ else if (PKGCONF_IS_MODULE_SEPARATOR(*sptr))
+ state = OUTSIDE_MODULE;
+ else if (PKGCONF_IS_OPERATOR_CHAR(*sptr))
+ state = BEFORE_OPERATOR;
+ else
+ state = OUTSIDE_MODULE;
+ }
+ else if (PKGCONF_IS_MODULE_SEPARATOR(*ptr))
+ state = OUTSIDE_MODULE;
+ else if (*(ptr + 1) == '\0')
+ {
+ ptr++;
+ state = OUTSIDE_MODULE;
+ }
+
+ if (state != INSIDE_MODULE_NAME && start != ptr)
+ {
+ char *iter = start;
+
+ while (PKGCONF_IS_MODULE_SEPARATOR(*iter))
+ iter++;
+
+ package = iter;
+ package_sz = ptr - iter;
+ start = ptr;
+ }
+
+ if (state == OUTSIDE_MODULE)
+ {
+ pkgconf_dependency_addraw(client, deplist_head, package, package_sz, NULL, 0, compare, flags);
+
+ compare = PKGCONF_CMP_ANY;
+ package_sz = 0;
+ }
+
+ break;
+
+ case BEFORE_OPERATOR:
+ if (PKGCONF_IS_OPERATOR_CHAR(*ptr))
+ {
+ state = INSIDE_OPERATOR;
+ if (cnameptr < cnameend)
+ *cnameptr++ = *ptr;
+ }
+
+ break;
+
+ case INSIDE_OPERATOR:
+ if (PKGCONF_IS_OPERATOR_CHAR(*ptr))
+ {
+ if (cnameptr < cnameend)
+ *cnameptr++ = *ptr;
+ break;
+ }
+
+ state = AFTER_OPERATOR;
+ compare = pkgconf_pkg_comparator_lookup_by_name(cmpname);
+ // fallthrough
+
+ case AFTER_OPERATOR:
+ if (!isspace((unsigned char)*ptr))
+ {
+ vstart = ptr;
+ state = INSIDE_VERSION;
+ }
+ break;
+
+ case INSIDE_VERSION:
+ if (PKGCONF_IS_MODULE_SEPARATOR(*ptr) || *(ptr + 1) == '\0')
+ {
+ version = vstart;
+ version_sz = ptr - vstart;
+ state = OUTSIDE_MODULE;
+
+ pkgconf_dependency_addraw(client, deplist_head, package, package_sz, version, version_sz, compare, flags);
+
+ compare = PKGCONF_CMP_ANY;
+ cnameptr = cmpname;
+ memset(cmpname, 0, sizeof cmpname);
+ package_sz = 0;
+ }
+
+ if (state == OUTSIDE_MODULE)
+ start = ptr;
+ break;
+ }
+
+ ptr++;
+ }
+
+ free(buf);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_dependency_parse(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist, const char *depends)
+ *
+ * Preprocess dependency data and then process that dependency declaration into a dependency list.
+ * Commas are counted as whitespace to allow for constructs such as ``@SUBSTVAR@, zlib`` being processed
+ * into ``, zlib``.
+ *
+ * :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to.
+ * :param pkgconf_pkg_t* pkg: The package object that owns this dependency list.
+ * :param pkgconf_list_t* deplist: The dependency list to populate with dependency nodes.
+ * :param char* depends: The dependency data to parse.
+ * :param uint flags: Any flags to attach to the dependency nodes.
+ * :return: nothing
+ */
+void
+pkgconf_dependency_parse(pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist, const char *depends, unsigned int flags)
+{
+ char *kvdepends = pkgconf_tuple_parse(client, &pkg->vars, depends, pkg->flags);
+
+ pkgconf_dependency_parse_str(client, deplist, kvdepends, flags);
+ free(kvdepends);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_dependency_t *pkgconf_dependency_copy(pkgconf_client_t *client, const pkgconf_dependency_t *dep)
+ *
+ * Copies a dependency node to a new one.
+ *
+ * :param pkgconf_client_t* client: The client object that will own this dependency.
+ * :param pkgconf_dependency_t* dep: The dependency node to copy.
+ * :return: a pointer to a new dependency node, else NULL
+ */
+pkgconf_dependency_t *
+pkgconf_dependency_copy(pkgconf_client_t *client, const pkgconf_dependency_t *dep)
+{
+ pkgconf_dependency_t *new_dep;
+
+ new_dep = calloc(1, sizeof(pkgconf_dependency_t));
+ if (new_dep == NULL)
+ return NULL;
+
+ new_dep->package = strdup(dep->package);
+
+ if (dep->version != NULL)
+ new_dep->version = strdup(dep->version);
+
+ new_dep->compare = dep->compare;
+ new_dep->flags = dep->flags;
+ new_dep->owner = client;
+ new_dep->refcount = 0;
+
+ if (dep->match != NULL)
+ new_dep->match = pkgconf_pkg_ref(client, dep->match);
+
+ return pkgconf_dependency_ref(client, new_dep);
+}
diff --git a/libpkgconf/fileio.c b/libpkgconf/fileio.c
new file mode 100644
index 000000000000..d4f001b9c3e3
--- /dev/null
+++ b/libpkgconf/fileio.c
@@ -0,0 +1,113 @@
+/*
+ * fileio.c
+ * File reading utilities
+ *
+ * Copyright (c) 2012, 2025 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+bool
+pkgconf_fgetline(pkgconf_buffer_t *buffer, FILE *stream)
+{
+ bool quoted = false;
+ int c = '\0', c2;
+
+ while ((c = getc(stream)) != EOF)
+ {
+ if (c == '\\' && !quoted)
+ {
+ quoted = true;
+ continue;
+ }
+ else if (c == '#')
+ {
+ if (!quoted) {
+ /* Skip the rest of the line */
+ do {
+ c = getc(stream);
+ } while (c != '\n' && c != EOF);
+ pkgconf_buffer_push_byte(buffer, c);
+ break;
+ }
+ else
+ pkgconf_buffer_push_byte(buffer, c);
+
+ quoted = false;
+ continue;
+ }
+ else if (c == '\n')
+ {
+ if (quoted)
+ {
+ /* Trim spaces */
+ do {
+ c2 = getc(stream);
+ } while (c2 == '\t' || c2 == ' ');
+
+ ungetc(c2, stream);
+
+ quoted = false;
+ continue;
+ }
+ else
+ {
+ pkgconf_buffer_push_byte(buffer, c);
+ }
+
+ break;
+ }
+ else if (c == '\r')
+ {
+ pkgconf_buffer_push_byte(buffer, '\n');
+
+ if ((c2 = getc(stream)) == '\n')
+ {
+ if (quoted)
+ {
+ quoted = false;
+ continue;
+ }
+
+ break;
+ }
+
+ ungetc(c2, stream);
+
+ if (quoted)
+ {
+ quoted = false;
+ continue;
+ }
+
+ break;
+ }
+ else
+ {
+ if (quoted) {
+ pkgconf_buffer_push_byte(buffer, '\\');
+ quoted = false;
+ }
+ pkgconf_buffer_push_byte(buffer, c);
+ }
+
+ }
+
+ /* Remove newline character. */
+ if (pkgconf_buffer_lastc(buffer) == '\n')
+ pkgconf_buffer_trim_byte(buffer);
+
+ if (pkgconf_buffer_lastc(buffer) == '\r')
+ pkgconf_buffer_trim_byte(buffer);
+
+ return !(c == EOF || ferror(stream));
+}
diff --git a/libpkgconf/fragment.c b/libpkgconf/fragment.c
new file mode 100644
index 000000000000..2b6109039b47
--- /dev/null
+++ b/libpkgconf/fragment.c
@@ -0,0 +1,803 @@
+/*
+ * fragment.c
+ * Management of fragment lists.
+ *
+ * Copyright (c) 2012, 2013, 2014 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !doc
+ *
+ * libpkgconf `fragment` module
+ * ============================
+ *
+ * The `fragment` module provides low-level management and rendering of fragment lists. A
+ * `fragment list` contains various `fragments` of text (such as ``-I /usr/include``) in a matter
+ * which is composable, mergeable and reorderable.
+ */
+
+struct pkgconf_fragment_check {
+ char *token;
+ size_t len;
+};
+
+static inline bool
+pkgconf_fragment_is_unmergeable(const char *string)
+{
+ static const struct pkgconf_fragment_check check_fragments[] = {
+ {"-framework", 10},
+ {"-isystem", 8},
+ {"-idirafter", 10},
+ {"-pthread", 8},
+ {"-Wa,", 4},
+ {"-Wl,", 4},
+ {"-Wp,", 4},
+ {"-trigraphs", 10},
+ {"-pedantic", 9},
+ {"-ansi", 5},
+ {"-std=", 5},
+ {"-stdlib=", 8},
+ {"-include", 8},
+ {"-nostdinc", 9},
+ {"-nostdlibinc", 12},
+ {"-nobuiltininc", 13},
+ {"-nodefaultlibs", 14},
+ };
+
+ if (*string != '-')
+ return true;
+
+ for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++)
+ if (!strncmp(string, check_fragments[i].token, check_fragments[i].len))
+ return true;
+
+ /* only one pair of {-flag, arg} may be merged together */
+ if (strchr(string, ' ') != NULL)
+ return false;
+
+ return false;
+}
+
+static inline bool
+pkgconf_fragment_should_munge(const char *string, const char *sysroot_dir)
+{
+ if (*string != '/')
+ return false;
+
+ if (sysroot_dir != NULL && strncmp(sysroot_dir, string, strlen(sysroot_dir)))
+ return true;
+
+ return false;
+}
+
+static inline bool
+pkgconf_fragment_is_groupable(const char *string)
+{
+ static const struct pkgconf_fragment_check check_fragments[] = {
+ {"-Wl,--start-group", 17},
+ {"-framework", 10},
+ {"-isystem", 8},
+ {"-idirafter", 10},
+ {"-include", 8},
+ };
+
+ for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++)
+ if (!strncmp(string, check_fragments[i].token, check_fragments[i].len))
+ return true;
+
+ return false;
+}
+
+static inline bool
+pkgconf_fragment_is_terminus(const char *string)
+{
+ static const struct pkgconf_fragment_check check_fragments[] = {
+ {"-Wl,--end-group", 15},
+ };
+
+ for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++)
+ if (!strncmp(string, check_fragments[i].token, check_fragments[i].len))
+ return true;
+
+ return false;
+}
+
+static inline bool
+pkgconf_fragment_is_special(const char *string)
+{
+ if (*string != '-')
+ return true;
+
+ if (!strncmp(string, "-lib:", 5))
+ return true;
+
+ return pkgconf_fragment_is_unmergeable(string);
+}
+
+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)
+{
+ *buf = '\0';
+
+ if (!(flags & PKGCONF_PKG_PROPF_UNINSTALLED) || (client->flags & PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES))
+ {
+ if (sysroot_dir == NULL)
+ sysroot_dir = pkgconf_tuple_find_global(client, "pc_sysrootdir");
+
+ if (sysroot_dir != NULL && pkgconf_fragment_should_munge(source, sysroot_dir))
+ pkgconf_strlcat(buf, sysroot_dir, buflen);
+ }
+
+ pkgconf_strlcat(buf, source, buflen);
+
+ if (*buf == '/' && !(client->flags & PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS))
+ pkgconf_path_relocate(buf, buflen);
+}
+
+static inline char *
+pkgconf_fragment_copy_munged(const pkgconf_client_t *client, const char *source, unsigned int flags)
+{
+ char mungebuf[PKGCONF_ITEM_SIZE];
+ pkgconf_fragment_munge(client, mungebuf, sizeof mungebuf, source, client->sysroot_dir, flags);
+ return strdup(mungebuf);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_fragment_insert(const pkgconf_client_t *client, pkgconf_list_t *list, char type, const char *data, bool tail)
+ *
+ * Adds a `fragment` of text to a `fragment list` directly without interpreting it.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client being accessed.
+ * :param pkgconf_list_t* list: The fragment list.
+ * :param char type: The type of the fragment.
+ * :param char* data: The data of the fragment.
+ * :param bool tail: Whether to place the fragment at the beginning of the list or the end.
+ * :return: nothing
+ */
+void
+pkgconf_fragment_insert(const pkgconf_client_t *client, pkgconf_list_t *list, char type, const char *data, bool tail)
+{
+ pkgconf_fragment_t *frag;
+
+ frag = calloc(1, sizeof(pkgconf_fragment_t));
+ frag->type = type;
+ frag->data = pkgconf_fragment_copy_munged(client, data, 0);
+
+ if (tail)
+ {
+ pkgconf_node_insert_tail(&frag->iter, frag, list);
+ return;
+ }
+
+ pkgconf_node_insert(&frag->iter, frag, list);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string, unsigned int flags)
+ *
+ * Adds a `fragment` of text to a `fragment list`, possibly modifying the fragment if a sysroot is set.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client being accessed.
+ * :param pkgconf_list_t* list: The fragment list.
+ * :param char* string: The string of text to add as a fragment to the fragment list.
+ * :param uint flags: Parsing-related flags for the package.
+ * :return: nothing
+ */
+void
+pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string, unsigned int flags)
+{
+ pkgconf_list_t *target = list;
+ pkgconf_fragment_t *frag;
+
+ if (*string == '\0')
+ return;
+
+ if (list->tail != NULL && list->tail->data != NULL &&
+ !(client->flags & PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS))
+ {
+ pkgconf_fragment_t *parent = list->tail->data;
+
+ /* only attempt to merge 'special' fragments together */
+ if (!parent->type && parent->data != NULL &&
+ pkgconf_fragment_is_unmergeable(parent->data) &&
+ !(parent->flags & PKGCONF_PKG_FRAGF_TERMINATED))
+ {
+ if (pkgconf_fragment_is_groupable(parent->data))
+ target = &parent->children;
+
+ if (pkgconf_fragment_is_terminus(string))
+ parent->flags |= PKGCONF_PKG_FRAGF_TERMINATED;
+
+ PKGCONF_TRACE(client, "adding fragment as child to list @%p", target);
+ }
+ }
+
+ frag = calloc(1, sizeof(pkgconf_fragment_t));
+ if (frag == NULL)
+ {
+ PKGCONF_TRACE(client, "failed to add new fragment due to allocation failure to list @%p", target);
+ return;
+ }
+
+ if (strlen(string) > 1 && !pkgconf_fragment_is_special(string))
+ {
+ frag->type = *(string + 1);
+ frag->data = pkgconf_fragment_copy_munged(client, string + 2, flags);
+
+ PKGCONF_TRACE(client, "added fragment {%c, '%s'} to list @%p", frag->type, frag->data, list);
+ }
+ else
+ {
+ frag->type = 0;
+ frag->data = pkgconf_fragment_copy_munged(client, string, flags);
+
+ PKGCONF_TRACE(client, "created special fragment {'%s'} in list @%p", frag->data, target);
+ }
+
+ pkgconf_node_insert_tail(&frag->iter, frag, target);
+}
+
+static inline pkgconf_fragment_t *
+pkgconf_fragment_lookup(pkgconf_list_t *list, const pkgconf_fragment_t *base)
+{
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY_REVERSE(list->tail, node)
+ {
+ pkgconf_fragment_t *frag = node->data;
+
+ if (base->type != frag->type)
+ continue;
+
+ if (!strcmp(base->data, frag->data))
+ return frag;
+ }
+
+ return NULL;
+}
+
+static inline bool
+pkgconf_fragment_can_merge_back(const pkgconf_fragment_t *base, unsigned int flags, bool is_private)
+{
+ (void) flags;
+
+ if (base->type == 'l')
+ {
+ if (is_private)
+ return false;
+
+ return true;
+ }
+
+ if (base->type == 'F')
+ return false;
+ if (base->type == 'L')
+ return false;
+ if (base->type == 'I')
+ return false;
+
+ return true;
+}
+
+static inline bool
+pkgconf_fragment_can_merge(const pkgconf_fragment_t *base, unsigned int flags, bool is_private)
+{
+ (void) flags;
+
+ if (is_private)
+ return false;
+
+ if (base->children.head != NULL)
+ return false;
+
+ return pkgconf_fragment_is_unmergeable(base->data);
+}
+
+static inline pkgconf_fragment_t *
+pkgconf_fragment_exists(pkgconf_list_t *list, const pkgconf_fragment_t *base, unsigned int flags, bool is_private)
+{
+ if (!pkgconf_fragment_can_merge_back(base, flags, is_private))
+ return NULL;
+
+ if (!pkgconf_fragment_can_merge(base, flags, is_private))
+ return NULL;
+
+ return pkgconf_fragment_lookup(list, base);
+}
+
+static inline bool
+pkgconf_fragment_should_merge(const pkgconf_fragment_t *base)
+{
+ const pkgconf_fragment_t *parent;
+
+ /* if we are the first fragment, that means the next fragment is the same, so it's always safe. */
+ if (base->iter.prev == NULL)
+ return true;
+
+ /* this really shouldn't ever happen, but handle it */
+ parent = base->iter.prev->data;
+ if (parent == NULL)
+ return true;
+
+ switch (parent->type)
+ {
+ case 'l':
+ case 'L':
+ case 'I':
+ return true;
+ default:
+ return !base->type || parent->type == base->type;
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag)
+ *
+ * Checks if a `fragment` contains a `system path`. System paths are detected at compile time and optionally overridden by
+ * the ``PKG_CONFIG_SYSTEM_INCLUDE_PATH`` and ``PKG_CONFIG_SYSTEM_LIBRARY_PATH`` environment variables.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object the fragment belongs to.
+ * :param pkgconf_fragment_t* frag: The fragment being checked.
+ * :return: true if the fragment contains a system path, else false
+ * :rtype: bool
+ */
+bool
+pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag)
+{
+ const pkgconf_list_t *check_paths = NULL;
+
+ switch (frag->type)
+ {
+ case 'L':
+ check_paths = &client->filter_libdirs;
+ break;
+ case 'I':
+ check_paths = &client->filter_includedirs;
+ break;
+ default:
+ return false;
+ }
+
+ return pkgconf_path_match_list(frag->data, check_paths);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private)
+ *
+ * Copies a `fragment` to another `fragment list`, possibly removing a previous copy of the `fragment`
+ * in a process known as `mergeback`.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client being accessed.
+ * :param pkgconf_list_t* list: The list the fragment is being added to.
+ * :param pkgconf_fragment_t* base: The fragment being copied.
+ * :param bool is_private: Whether the fragment list is a `private` fragment list (static linking).
+ * :return: nothing
+ */
+void
+pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private)
+{
+ pkgconf_fragment_t *frag;
+
+ if ((frag = pkgconf_fragment_exists(list, base, client->flags, is_private)) != NULL)
+ {
+ if (pkgconf_fragment_should_merge(frag))
+ pkgconf_fragment_delete(list, frag);
+ }
+ else if (!is_private && !pkgconf_fragment_can_merge_back(base, client->flags, is_private) && (pkgconf_fragment_lookup(list, base) != NULL))
+ return;
+
+ frag = calloc(1, sizeof(pkgconf_fragment_t));
+
+ frag->type = base->type;
+ pkgconf_fragment_copy_list(client, &frag->children, &base->children);
+ if (base->data != NULL)
+ frag->data = strdup(base->data);
+
+ pkgconf_node_insert_tail(&frag->iter, frag, list);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base)
+ *
+ * Copies a `fragment list` to another `fragment list`, possibly removing a previous copy of the fragments
+ * in a process known as `mergeback`.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client being accessed.
+ * :param pkgconf_list_t* list: The list the fragments are being added to.
+ * :param pkgconf_list_t* base: The list the fragments are being copied from.
+ * :return: nothing
+ */
+void
+pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base)
+{
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(base->head, node)
+ {
+ pkgconf_fragment_t *frag = node->data;
+
+ pkgconf_fragment_copy(client, list, frag, true);
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. 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)
+ *
+ * Copies a `fragment list` to another `fragment list` which match a user-specified filtering function.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client being accessed.
+ * :param pkgconf_list_t* dest: The destination list.
+ * :param pkgconf_list_t* src: The source list.
+ * :param pkgconf_fragment_filter_func_t filter_func: The filter function to use.
+ * :param void* data: Optional data to pass to the filter function.
+ * :return: nothing
+ */
+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)
+{
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(src->head, node)
+ {
+ pkgconf_fragment_t *frag = node->data;
+
+ if (filter_func(client, frag, data))
+ pkgconf_fragment_copy(client, dest, frag, true);
+ }
+}
+
+static inline char *
+fragment_quote(const pkgconf_fragment_t *frag)
+{
+ const char *src = frag->data;
+ ssize_t outlen = strlen(src) + 10;
+ char *out, *dst;
+
+ if (frag->data == NULL)
+ return NULL;
+
+ out = dst = calloc(1, outlen);
+ if (out == NULL)
+ return NULL;
+
+ for (; *src; src++)
+ {
+ if (((*src < ' ') ||
+ (*src >= (' ' + (frag->children.head != NULL ? 1 : 0)) && *src < '$') ||
+ (*src > '$' && *src < '(') ||
+ (*src > ')' && *src < '+') ||
+ (*src > ':' && *src < '=') ||
+ (*src > '=' && *src < '@') ||
+ (*src > 'Z' && *src < '\\') ||
+#ifndef _WIN32
+ (*src == '\\') ||
+#endif
+ (*src > '\\' && *src < '^') ||
+ (*src == '`') ||
+ (*src > 'z' && *src < '~') ||
+ (*src > '~')))
+ *dst++ = '\\';
+
+ *dst++ = *src;
+
+ if ((ptrdiff_t)(dst - out) + 2 > outlen)
+ {
+ ptrdiff_t offset = dst - out;
+ outlen *= 2;
+
+ char *newout = realloc(out, outlen);
+ if (newout == NULL)
+ {
+ free(out);
+ return NULL;
+ }
+
+ out = newout;
+ dst = out + offset;
+ }
+ }
+
+ *dst = 0;
+ return out;
+}
+
+static inline size_t
+pkgconf_fragment_len(const pkgconf_fragment_t *frag)
+{
+ size_t len = 1;
+
+ if (frag->type)
+ len += 2;
+
+ if (frag->data != NULL)
+ {
+ pkgconf_node_t *iter;
+
+ char *quoted = fragment_quote(frag);
+ len += strlen(quoted);
+ free(quoted);
+
+ PKGCONF_FOREACH_LIST_ENTRY(frag->children.head, iter)
+ {
+ const pkgconf_fragment_t *child_frag = iter->data;
+ len += pkgconf_fragment_len(child_frag) + 1;
+ }
+ }
+
+ return len;
+}
+
+static size_t
+fragment_render_len(const pkgconf_list_t *list, bool escape)
+{
+ (void) escape;
+
+ size_t out = 1; /* trailing nul */
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
+ {
+ const pkgconf_fragment_t *frag = node->data;
+ out += pkgconf_fragment_len(frag);
+ }
+
+ return out;
+}
+
+static inline size_t
+fragment_render_item(const pkgconf_fragment_t *frag, char *bptr, size_t bufremain)
+{
+ const pkgconf_node_t *iter;
+ char *base = bptr;
+
+ char *quoted = fragment_quote(frag);
+ if (quoted == NULL)
+ return 0;
+
+ if (strlen(quoted) > bufremain)
+ {
+ free(quoted);
+ return 0;
+ }
+
+ if (frag->type)
+ {
+ *bptr++ = '-';
+ *bptr++ = frag->type;
+ }
+
+ if (quoted != NULL)
+ {
+ bptr += pkgconf_strlcpy(bptr, quoted, bufremain - (bptr - base));
+ free(quoted);
+ }
+
+ PKGCONF_FOREACH_LIST_ENTRY(frag->children.head, iter)
+ {
+ const pkgconf_fragment_t *child_frag = iter->data;
+
+ *bptr++ = ' ';
+ bptr += fragment_render_item(child_frag, bptr, bufremain - (bptr - base));
+ }
+
+ return bptr - base;
+}
+
+static void
+fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape)
+{
+ (void) escape;
+
+ pkgconf_node_t *node;
+ char *bptr = buf;
+
+ memset(buf, 0, buflen);
+
+ PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
+ {
+ const pkgconf_fragment_t *frag = node->data;
+ size_t buf_remaining = buflen - (bptr - buf);
+ size_t written = fragment_render_item(frag, bptr, buf_remaining);
+
+ bptr += written;
+
+ if (node->next != NULL)
+ *bptr++ = ' ';
+ }
+}
+
+static const pkgconf_fragment_render_ops_t default_render_ops = {
+ .render_len = fragment_render_len,
+ .render_buf = fragment_render_buf
+};
+
+/*
+ * !doc
+ *
+ * .. c:function:: size_t pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops)
+ *
+ * Calculates the required memory to store a `fragment list` when rendered as a string.
+ *
+ * :param pkgconf_list_t* list: The `fragment list` being rendered.
+ * :param bool escape: Whether or not to escape special shell characters (deprecated).
+ * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
+ * :return: the amount of bytes required to represent the `fragment list` when rendered
+ * :rtype: size_t
+ */
+size_t
+pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops)
+{
+ (void) escape;
+
+ ops = ops != NULL ? ops : &default_render_ops;
+ return ops->render_len(list, true);
+}
+
+/*
+ * !doc
+ *
+ * .. 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)
+ *
+ * Renders a `fragment list` into a buffer.
+ *
+ * :param pkgconf_list_t* list: The `fragment list` being rendered.
+ * :param char* buf: The buffer to render the fragment list into.
+ * :param size_t buflen: The length of the buffer.
+ * :param bool escape: Whether or not to escape special shell characters (deprecated).
+ * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
+ * :return: nothing
+ */
+void
+pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape, const pkgconf_fragment_render_ops_t *ops)
+{
+ (void) escape;
+
+ ops = ops != NULL ? ops : &default_render_ops;
+ ops->render_buf(list, buf, buflen, true);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: char *pkgconf_fragment_render(const pkgconf_list_t *list)
+ *
+ * Allocate memory and render a `fragment list` into it.
+ *
+ * :param pkgconf_list_t* list: The `fragment list` being rendered.
+ * :param bool escape: Whether or not to escape special shell characters (deprecated).
+ * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``.
+ * :return: An allocated string containing the rendered `fragment list`.
+ * :rtype: char *
+ */
+char *
+pkgconf_fragment_render(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops)
+{
+ (void) escape;
+
+ size_t buflen = pkgconf_fragment_render_len(list, true, ops);
+ char *buf = calloc(1, buflen);
+
+ pkgconf_fragment_render_buf(list, buf, buflen, true, ops);
+
+ return buf;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node)
+ *
+ * Delete a `fragment node` from a `fragment list`.
+ *
+ * :param pkgconf_list_t* list: The `fragment list` to delete from.
+ * :param pkgconf_fragment_t* node: The `fragment node` to delete.
+ * :return: nothing
+ */
+void
+pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node)
+{
+ pkgconf_node_delete(&node->iter, list);
+
+ free(node->data);
+ free(node);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_fragment_free(pkgconf_list_t *list)
+ *
+ * Delete an entire `fragment list`.
+ *
+ * :param pkgconf_list_t* list: The `fragment list` to delete.
+ * :return: nothing
+ */
+void
+pkgconf_fragment_free(pkgconf_list_t *list)
+{
+ pkgconf_node_t *node, *next;
+
+ PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
+ {
+ pkgconf_fragment_t *frag = node->data;
+
+ pkgconf_fragment_free(&frag->children);
+ free(frag->data);
+ free(frag);
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value)
+ *
+ * Parse a string into a `fragment list`.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client being accessed.
+ * :param pkgconf_list_t* list: The `fragment list` to add the fragment entries to.
+ * :param pkgconf_list_t* vars: A list of variables to use for variable substitution.
+ * :param uint flags: Any parsing flags to be aware of.
+ * :param char* value: The string to parse into fragments.
+ * :return: true on success, false on parse error
+ */
+bool
+pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value, unsigned int flags)
+{
+ int i, ret, argc;
+ char **argv;
+ char *repstr = pkgconf_tuple_parse(client, vars, value, flags);
+
+ PKGCONF_TRACE(client, "post-subst: [%s] -> [%s]", value, repstr);
+
+ ret = pkgconf_argv_split(repstr, &argc, &argv);
+ if (ret < 0)
+ {
+ PKGCONF_TRACE(client, "unable to parse fragment string [%s]", repstr);
+ free(repstr);
+ return false;
+ }
+
+ for (i = 0; i < argc; i++)
+ {
+ PKGCONF_TRACE(client, "processing %s", argv[i]);
+
+ if (argv[i] == NULL)
+ {
+ PKGCONF_TRACE(client, "parsed fragment string is inconsistent: argc = %d while argv[%d] == NULL", argc, i);
+ pkgconf_argv_free(argv);
+ free(repstr);
+ return false;
+ }
+
+ pkgconf_fragment_add(client, list, argv[i], flags);
+ }
+
+ pkgconf_argv_free(argv);
+ free(repstr);
+
+ return true;
+}
diff --git a/libpkgconf/iter.h b/libpkgconf/iter.h
new file mode 100644
index 000000000000..199d299f6bed
--- /dev/null
+++ b/libpkgconf/iter.h
@@ -0,0 +1,113 @@
+/*
+ * iter.h
+ * Linked lists and iterators.
+ *
+ * Copyright (c) 2013 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#ifndef LIBPKGCONF_ITER_H
+#define LIBPKGCONF_ITER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct pkgconf_node_ pkgconf_node_t;
+
+struct pkgconf_node_ {
+ pkgconf_node_t *prev, *next;
+ void *data;
+};
+
+typedef struct {
+ pkgconf_node_t *head, *tail;
+ size_t length;
+} pkgconf_list_t;
+
+#define PKGCONF_LIST_INITIALIZER { NULL, NULL, 0 }
+
+static inline void
+pkgconf_list_zero(pkgconf_list_t *list)
+{
+ list->head = NULL;
+ list->tail = NULL;
+ list->length = 0;
+}
+
+static inline void
+pkgconf_node_insert(pkgconf_node_t *node, void *data, pkgconf_list_t *list)
+{
+ pkgconf_node_t *tnode;
+
+ node->data = data;
+
+ if (list->head == NULL)
+ {
+ list->head = node;
+ list->tail = node;
+ list->length = 1;
+ return;
+ }
+
+ tnode = list->head;
+
+ node->next = tnode;
+ tnode->prev = node;
+
+ list->head = node;
+ list->length++;
+}
+
+static inline void
+pkgconf_node_insert_tail(pkgconf_node_t *node, void *data, pkgconf_list_t *list)
+{
+ pkgconf_node_t *tnode;
+
+ node->data = data;
+
+ if (list->tail == NULL)
+ {
+ list->head = node;
+ list->tail = node;
+ list->length = 1;
+ return;
+ }
+
+ tnode = list->tail;
+
+ node->prev = tnode;
+ tnode->next = node;
+
+ list->tail = node;
+ list->length++;
+}
+
+static inline void
+pkgconf_node_delete(pkgconf_node_t *node, pkgconf_list_t *list)
+{
+ list->length--;
+
+ if (node->prev == NULL)
+ list->head = node->next;
+ else
+ node->prev->next = node->next;
+
+ if (node->next == NULL)
+ list->tail = node->prev;
+ else
+ node->next->prev = node->prev;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libpkgconf/libpkgconf-api.h b/libpkgconf/libpkgconf-api.h
new file mode 100644
index 000000000000..a924c80ce833
--- /dev/null
+++ b/libpkgconf/libpkgconf-api.h
@@ -0,0 +1,19 @@
+#ifndef LIBPKGCONF_LIBPKGCONF_API_H
+#define LIBPKGCONF_LIBPKGCONF_API_H
+
+/* Makefile.am specifies visibility using the libtool option -export-symbols-regex '^pkgconf_'
+ * Unfortunately, that is not available when building with meson, so use attributes instead.
+ */
+#if defined(PKGCONFIG_IS_STATIC)
+# define PKGCONF_API
+#elif defined(_WIN32) || defined(_WIN64)
+# if defined(LIBPKGCONF_EXPORT) || defined(DLL_EXPORT)
+# define PKGCONF_API __declspec(dllexport)
+# else
+# define PKGCONF_API __declspec(dllimport)
+# endif
+#else
+# define PKGCONF_API __attribute__((visibility("default")))
+#endif
+
+#endif
diff --git a/libpkgconf/libpkgconf.h b/libpkgconf/libpkgconf.h
new file mode 100644
index 000000000000..ed0f1f996ce4
--- /dev/null
+++ b/libpkgconf/libpkgconf.h
@@ -0,0 +1,493 @@
+/*
+ * libpkgconf.h
+ * Global include file for everything in libpkgconf.
+ *
+ * Copyright (c) 2011, 2015 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#ifndef LIBPKGCONF__LIBPKGCONF_H
+#define LIBPKGCONF__LIBPKGCONF_H
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <libpkgconf/libpkgconf-api.h>
+#include <libpkgconf/iter.h>
+#include <libpkgconf/bsdstubs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* pkg-config uses ';' on win32 as ':' is part of path */
+#ifdef _WIN32
+#define PKG_CONFIG_PATH_SEP_S ";"
+#else
+#define PKG_CONFIG_PATH_SEP_S ":"
+#endif
+
+#ifdef _WIN32
+#define PKG_DIR_SEP_S '\\'
+#else
+#define PKG_DIR_SEP_S '/'
+#endif
+
+#ifdef _WIN32
+#define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
+#endif
+
+#define PKGCONF_BUFSIZE (65535)
+
+typedef enum {
+ PKGCONF_CMP_NOT_EQUAL,
+ PKGCONF_CMP_ANY,
+ PKGCONF_CMP_LESS_THAN,
+ PKGCONF_CMP_LESS_THAN_EQUAL,
+ PKGCONF_CMP_EQUAL,
+ PKGCONF_CMP_GREATER_THAN,
+ PKGCONF_CMP_GREATER_THAN_EQUAL
+} pkgconf_pkg_comparator_t;
+
+#define PKGCONF_CMP_COUNT 7
+
+typedef struct pkgconf_pkg_ pkgconf_pkg_t;
+typedef struct pkgconf_dependency_ pkgconf_dependency_t;
+typedef struct pkgconf_tuple_ pkgconf_tuple_t;
+typedef struct pkgconf_fragment_ pkgconf_fragment_t;
+typedef struct pkgconf_path_ pkgconf_path_t;
+typedef struct pkgconf_client_ pkgconf_client_t;
+typedef struct pkgconf_cross_personality_ pkgconf_cross_personality_t;
+typedef struct pkgconf_queue_ pkgconf_queue_t;
+
+#define PKGCONF_ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+
+#define PKGCONF_FOREACH_LIST_ENTRY(head, value) \
+ for ((value) = (head); (value) != NULL; (value) = (value)->next)
+
+#define PKGCONF_FOREACH_LIST_ENTRY_SAFE(head, nextiter, value) \
+ for ((value) = (head), (nextiter) = (head) != NULL ? (head)->next : NULL; (value) != NULL; (value) = (nextiter), (nextiter) = (nextiter) != NULL ? (nextiter)->next : NULL)
+
+#define PKGCONF_FOREACH_LIST_ENTRY_REVERSE(tail, value) \
+ for ((value) = (tail); (value) != NULL; (value) = (value)->prev)
+
+#define LIBPKGCONF_VERSION 20501
+#define LIBPKGCONF_VERSION_STR "2.5.1"
+
+struct pkgconf_queue_ {
+ pkgconf_node_t iter;
+ char *package;
+
+ unsigned int flags;
+};
+
+struct pkgconf_fragment_ {
+ pkgconf_node_t iter;
+
+ char type;
+ char *data;
+
+ pkgconf_list_t children;
+ unsigned int flags;
+};
+
+#define PKGCONF_PKG_FRAGF_TERMINATED 0x1
+
+struct pkgconf_dependency_ {
+ pkgconf_node_t iter;
+
+ char *package;
+ pkgconf_pkg_comparator_t compare;
+ char *version;
+ pkgconf_pkg_t *parent;
+ pkgconf_pkg_t *match;
+
+ unsigned int flags;
+
+ int refcount;
+ pkgconf_client_t *owner;
+};
+
+struct pkgconf_tuple_ {
+ pkgconf_node_t iter;
+
+ char *key;
+ char *value;
+
+ unsigned int flags;
+};
+
+#define PKGCONF_PKG_TUPLEF_OVERRIDE 0x1
+
+struct pkgconf_path_ {
+ pkgconf_node_t lnode;
+
+ char *path;
+ void *handle_path;
+ void *handle_device;
+
+ unsigned int flags;
+};
+
+#define PKGCONF_PKG_PROPF_NONE 0x00
+#define PKGCONF_PKG_PROPF_STATIC 0x01
+#define PKGCONF_PKG_PROPF_CACHED 0x02
+#define PKGCONF_PKG_PROPF_UNINSTALLED 0x08
+#define PKGCONF_PKG_PROPF_VIRTUAL 0x10
+#define PKGCONF_PKG_PROPF_ANCESTOR 0x20
+#define PKGCONF_PKG_PROPF_VISITED_PRIVATE 0x40
+#define PKGCONF_PKG_PROPF_PRELOADED 0x80
+
+struct pkgconf_pkg_ {
+ int refcount;
+ char *id;
+ char *filename;
+ char *realname;
+ char *version;
+ char *description;
+ char *url;
+ char *pc_filedir;
+ char *license;
+ char *maintainer;
+ char *copyright;
+ char *why;
+
+ pkgconf_list_t libs;
+ pkgconf_list_t libs_private;
+ pkgconf_list_t cflags;
+ pkgconf_list_t cflags_private;
+
+ pkgconf_list_t required; /* this used to be requires but that is now a reserved keyword */
+ pkgconf_list_t requires_private;
+ pkgconf_list_t conflicts;
+ pkgconf_list_t provides;
+
+ pkgconf_list_t vars;
+
+ unsigned int flags;
+
+ pkgconf_client_t *owner;
+
+ /* these resources are owned by the package and do not need special management,
+ * under no circumstance attempt to allocate or free objects belonging to these pointers
+ */
+ pkgconf_tuple_t *orig_prefix;
+ pkgconf_tuple_t *prefix;
+
+ uint64_t serial;
+ uint64_t identifier;
+
+ pkgconf_node_t preload_node;
+};
+
+typedef bool (*pkgconf_pkg_iteration_func_t)(const pkgconf_pkg_t *pkg, void *data);
+typedef void (*pkgconf_pkg_traverse_func_t)(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data);
+typedef bool (*pkgconf_queue_apply_func_t)(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth);
+typedef bool (*pkgconf_error_handler_func_t)(const char *msg, const pkgconf_client_t *client, void *data);
+typedef void (*pkgconf_unveil_handler_func_t)(const pkgconf_client_t *client, const char *path, const char *permissions);
+
+struct pkgconf_client_ {
+ pkgconf_list_t dir_list;
+
+ pkgconf_list_t filter_libdirs;
+ pkgconf_list_t filter_includedirs;
+
+ pkgconf_list_t global_vars;
+
+ void *error_handler_data;
+ void *warn_handler_data;
+ void *trace_handler_data;
+
+ pkgconf_error_handler_func_t error_handler;
+ pkgconf_error_handler_func_t warn_handler;
+ pkgconf_error_handler_func_t trace_handler;
+
+ FILE *auditf;
+
+ char *sysroot_dir;
+ char *buildroot_dir;
+
+ unsigned int flags;
+
+ char *prefix_varname;
+
+ bool already_sent_notice;
+
+ uint64_t serial;
+ uint64_t identifier;
+
+ pkgconf_pkg_t **cache_table;
+ size_t cache_count;
+
+ pkgconf_unveil_handler_func_t unveil_handler;
+
+ pkgconf_list_t preloaded_pkgs;
+};
+
+struct pkgconf_cross_personality_ {
+ char *name;
+
+ pkgconf_list_t dir_list;
+
+ pkgconf_list_t filter_libdirs;
+ pkgconf_list_t filter_includedirs;
+
+ char *sysroot_dir;
+
+ bool want_default_static;
+ bool want_default_pure;
+};
+
+/* client.c */
+PKGCONF_API void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality);
+PKGCONF_API pkgconf_client_t * pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality);
+PKGCONF_API void pkgconf_client_deinit(pkgconf_client_t *client);
+PKGCONF_API void pkgconf_client_free(pkgconf_client_t *client);
+PKGCONF_API const char *pkgconf_client_get_sysroot_dir(const pkgconf_client_t *client);
+PKGCONF_API void pkgconf_client_set_sysroot_dir(pkgconf_client_t *client, const char *sysroot_dir);
+PKGCONF_API const char *pkgconf_client_get_buildroot_dir(const pkgconf_client_t *client);
+PKGCONF_API void pkgconf_client_set_buildroot_dir(pkgconf_client_t *client, const char *buildroot_dir);
+PKGCONF_API unsigned int pkgconf_client_get_flags(const pkgconf_client_t *client);
+PKGCONF_API void pkgconf_client_set_flags(pkgconf_client_t *client, unsigned int flags);
+PKGCONF_API const char *pkgconf_client_get_prefix_varname(const pkgconf_client_t *client);
+PKGCONF_API void pkgconf_client_set_prefix_varname(pkgconf_client_t *client, const char *prefix_varname);
+PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_warn_handler(const pkgconf_client_t *client);
+PKGCONF_API void pkgconf_client_set_warn_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t warn_handler, void *warn_handler_data);
+PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_error_handler(const pkgconf_client_t *client);
+PKGCONF_API void pkgconf_client_set_error_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data);
+PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_trace_handler(const pkgconf_client_t *client);
+PKGCONF_API void pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t trace_handler, void *trace_handler_data);
+PKGCONF_API pkgconf_unveil_handler_func_t pkgconf_client_get_unveil_handler(const pkgconf_client_t *client);
+PKGCONF_API void pkgconf_client_set_unveil_handler(pkgconf_client_t *client, pkgconf_unveil_handler_func_t unveil_handler);
+PKGCONF_API void pkgconf_client_dir_list_build(pkgconf_client_t *client, const pkgconf_cross_personality_t *personality);
+PKGCONF_API bool pkgconf_client_preload_path(pkgconf_client_t *client, const char *path);
+PKGCONF_API bool pkgconf_client_preload_from_environ(pkgconf_client_t *client, const char *env);
+
+/* personality.c */
+PKGCONF_API pkgconf_cross_personality_t *pkgconf_cross_personality_default(void);
+PKGCONF_API pkgconf_cross_personality_t *pkgconf_cross_personality_find(const char *triplet);
+PKGCONF_API void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *personality);
+
+#define PKGCONF_IS_MODULE_SEPARATOR(c) ((c) == ',' || isspace ((unsigned char)(c)))
+#define PKGCONF_IS_OPERATOR_CHAR(c) ((c) == '<' || (c) == '>' || (c) == '!' || (c) == '=')
+
+#define PKGCONF_PKG_PKGF_NONE 0x0000
+#define PKGCONF_PKG_PKGF_SEARCH_PRIVATE 0x0001
+#define PKGCONF_PKG_PKGF_ENV_ONLY 0x0002
+#define PKGCONF_PKG_PKGF_NO_UNINSTALLED 0x0004
+#define PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL 0x0008
+#define PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS 0x0010
+#define PKGCONF_PKG_PKGF_SKIP_CONFLICTS 0x0020
+#define PKGCONF_PKG_PKGF_NO_CACHE 0x0040
+#define PKGCONF_PKG_PKGF_SKIP_ERRORS 0x0080
+#define PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE 0x0100
+#define PKGCONF_PKG_PKGF_SKIP_PROVIDES 0x0200
+#define PKGCONF_PKG_PKGF_REDEFINE_PREFIX 0x0400
+#define PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS 0x0800
+#define PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS 0x1000
+#define PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS 0x2000
+#define PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS 0x4000
+#define PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES 0x8000
+#define PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES 0x10000
+
+#define PKGCONF_PKG_DEPF_INTERNAL 0x1
+#define PKGCONF_PKG_DEPF_PRIVATE 0x2
+#define PKGCONF_PKG_DEPF_QUERY 0x4
+
+#define PKGCONF_PKG_ERRF_OK 0x0
+#define PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND 0x1
+#define PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH 0x2
+#define PKGCONF_PKG_ERRF_PACKAGE_CONFLICT 0x4
+#define PKGCONF_PKG_ERRF_DEPGRAPH_BREAK 0x8
+
+#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
+# define PRINTFLIKE(fmtarg, firstvararg) \
+ __attribute__((__format__ (gnu_printf, fmtarg, firstvararg)))
+#elif defined(__clang__) || defined(__INTEL_COMPILER) || __GNUC__ > 2 || (_GNUC__ == 2 && __GNUC_MINOR__ >= 5)
+# define PRINTFLIKE(fmtarg, firstvararg) \
+ __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
+#else
+# define PRINTFLIKE(fmtarg, firstvararg)
+#endif
+
+#if defined(__clang__) || defined(__INTEL_COMPILER) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+# define DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+# define DEPRECATED __declspec(deprecated)
+#else
+# define DEPRECATED
+#endif
+
+/* parser.c */
+typedef void (*pkgconf_parser_operand_func_t)(void *data, const size_t lineno, const char *key, const char *value);
+typedef void (*pkgconf_parser_warn_func_t)(void *data, const char *fmt, ...);
+
+PKGCONF_API void pkgconf_parser_parse(FILE *f, void *data, const pkgconf_parser_operand_func_t *ops, const pkgconf_parser_warn_func_t warnfunc, const char *filename);
+
+/* pkg.c */
+PKGCONF_API bool pkgconf_error(const pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3);
+PKGCONF_API bool pkgconf_warn(const pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3);
+PKGCONF_API bool pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t lineno, const char *funcname, const char *format, ...) PRINTFLIKE(5, 6);
+PKGCONF_API bool pkgconf_default_error_handler(const char *msg, const pkgconf_client_t *client, void *data);
+
+#ifndef PKGCONF_LITE
+#if defined(__GNUC__) || defined(__INTEL_COMPILER)
+#define PKGCONF_TRACE(client, ...) do { \
+ pkgconf_trace(client, __FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__); \
+ } while (0)
+#else
+#define PKGCONF_TRACE(client, ...) do { \
+ pkgconf_trace(client, __FILE__, __LINE__, __func__, __VA_ARGS__); \
+ } while (0)
+#endif
+#else
+#define PKGCONF_TRACE(client, ...)
+#endif
+
+PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_pkg_t *pkg);
+PKGCONF_API void pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg);
+PKGCONF_API void pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg);
+PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_find(pkgconf_client_t *client, const char *name);
+PKGCONF_API 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);
+PKGCONF_API unsigned int pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth);
+PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags);
+PKGCONF_API const char *pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep);
+PKGCONF_API unsigned int pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth);
+PKGCONF_API unsigned int pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth);
+PKGCONF_API pkgconf_pkg_comparator_t pkgconf_pkg_comparator_lookup_by_name(const char *name);
+PKGCONF_API pkgconf_pkg_t *pkgconf_builtin_pkg_get(const char *name);
+
+PKGCONF_API int pkgconf_compare_version(const char *a, const char *b);
+PKGCONF_API pkgconf_pkg_t *pkgconf_scan_all(pkgconf_client_t *client, void *ptr, pkgconf_pkg_iteration_func_t func);
+
+/* parse.c */
+PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_new_from_path(pkgconf_client_t *client, const char *path, unsigned int flags);
+PKGCONF_API void pkgconf_dependency_parse_str(pkgconf_client_t *client, pkgconf_list_t *deplist_head, const char *depends, unsigned int flags);
+PKGCONF_API void pkgconf_dependency_parse(pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist_head, const char *depends, unsigned int flags);
+PKGCONF_API void pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail);
+PKGCONF_API void pkgconf_dependency_free(pkgconf_list_t *list);
+PKGCONF_API void pkgconf_dependency_free_one(pkgconf_dependency_t *dep);
+PKGCONF_API pkgconf_dependency_t *pkgconf_dependency_add(pkgconf_client_t *client, pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare, unsigned int flags);
+PKGCONF_API pkgconf_dependency_t *pkgconf_dependency_ref(pkgconf_client_t *client, pkgconf_dependency_t *dep);
+PKGCONF_API void pkgconf_dependency_unref(pkgconf_client_t *client, pkgconf_dependency_t *dep);
+PKGCONF_API pkgconf_dependency_t *pkgconf_dependency_copy(pkgconf_client_t *client, const pkgconf_dependency_t *dep);
+
+/* argvsplit.c */
+PKGCONF_API int pkgconf_argv_split(const char *src, int *argc, char ***argv);
+PKGCONF_API void pkgconf_argv_free(char **argv);
+
+/* fragment.c */
+typedef struct pkgconf_fragment_render_ops_ {
+ size_t (*render_len)(const pkgconf_list_t *list, bool escape);
+ void (*render_buf)(const pkgconf_list_t *list, char *buf, size_t len, bool escape);
+} pkgconf_fragment_render_ops_t;
+
+typedef bool (*pkgconf_fragment_filter_func_t)(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data);
+PKGCONF_API bool pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value, unsigned int flags);
+PKGCONF_API void pkgconf_fragment_insert(const pkgconf_client_t *client, pkgconf_list_t *list, char type, const char *data, bool tail);
+PKGCONF_API void pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string, unsigned int flags);
+PKGCONF_API void pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private);
+PKGCONF_API void pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base);
+PKGCONF_API void pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node);
+PKGCONF_API void pkgconf_fragment_free(pkgconf_list_t *list);
+PKGCONF_API 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);
+PKGCONF_API size_t pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops);
+PKGCONF_API void pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t len, bool escape, const pkgconf_fragment_render_ops_t *ops);
+PKGCONF_API char *pkgconf_fragment_render(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops);
+PKGCONF_API bool pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag);
+
+/* tuple.c */
+PKGCONF_API pkgconf_tuple_t *pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *parent, const char *key, const char *value, bool parse, unsigned int flags);
+PKGCONF_API char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key);
+PKGCONF_API char *pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *list, const char *value, unsigned int flags);
+PKGCONF_API void pkgconf_tuple_free(pkgconf_list_t *list);
+PKGCONF_API void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list);
+PKGCONF_API void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value);
+PKGCONF_API char *pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key);
+PKGCONF_API void pkgconf_tuple_free_global(pkgconf_client_t *client);
+PKGCONF_API void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv);
+
+/* queue.c */
+PKGCONF_API void pkgconf_queue_push(pkgconf_list_t *list, const char *package);
+PKGCONF_API bool pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list);
+PKGCONF_API bool pkgconf_queue_solve(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_pkg_t *world, int maxdepth);
+PKGCONF_API void pkgconf_queue_free(pkgconf_list_t *list);
+PKGCONF_API bool pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data);
+PKGCONF_API bool pkgconf_queue_validate(pkgconf_client_t *client, pkgconf_list_t *list, int maxdepth);
+PKGCONF_API void pkgconf_solution_free(pkgconf_client_t *client, pkgconf_pkg_t *world);
+
+/* cache.c */
+PKGCONF_API pkgconf_pkg_t *pkgconf_cache_lookup(pkgconf_client_t *client, const char *id);
+PKGCONF_API void pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg);
+PKGCONF_API void pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg);
+PKGCONF_API void pkgconf_cache_free(pkgconf_client_t *client);
+
+/* audit.c */
+PKGCONF_API void pkgconf_audit_set_log(pkgconf_client_t *client, FILE *auditf);
+PKGCONF_API void pkgconf_audit_log(pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3);
+PKGCONF_API void pkgconf_audit_log_dependency(pkgconf_client_t *client, const pkgconf_pkg_t *dep, const pkgconf_dependency_t *depnode);
+
+/* path.c */
+PKGCONF_API void pkgconf_path_add(const char *text, pkgconf_list_t *dirlist, bool filter);
+PKGCONF_API void pkgconf_path_prepend(const char *text, pkgconf_list_t *dirlist, bool filter);
+PKGCONF_API size_t pkgconf_path_split(const char *text, pkgconf_list_t *dirlist, bool filter);
+PKGCONF_API size_t pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist, bool filter);
+#ifdef _WIN32
+PKGCONF_API size_t pkgconf_path_build_from_registry(/* HKEY -> HANDLE -> PVOID */ void *hKey, pkgconf_list_t *dirlist, bool filter);
+#endif
+PKGCONF_API bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist);
+PKGCONF_API void pkgconf_path_free(pkgconf_list_t *dirlist);
+PKGCONF_API bool pkgconf_path_relocate(char *buf, size_t buflen);
+PKGCONF_API void pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src);
+PKGCONF_API void pkgconf_path_prepend_list(pkgconf_list_t *dst, const pkgconf_list_t *src);
+
+/* buffer.c */
+typedef struct pkgconf_buffer_ {
+ char *base;
+ char *end;
+} pkgconf_buffer_t;
+
+PKGCONF_API void pkgconf_buffer_append(pkgconf_buffer_t *buffer, const char *text);
+PKGCONF_API void pkgconf_buffer_push_byte(pkgconf_buffer_t *buffer, char byte);
+PKGCONF_API void pkgconf_buffer_trim_byte(pkgconf_buffer_t *buffer);
+PKGCONF_API void pkgconf_buffer_finalize(pkgconf_buffer_t *buffer);
+static inline const char *pkgconf_buffer_str(const pkgconf_buffer_t *buffer) {
+ return buffer->base;
+}
+
+static inline size_t pkgconf_buffer_len(const pkgconf_buffer_t *buffer) {
+ return (size_t)(ptrdiff_t)(buffer->end - buffer->base);
+}
+
+static inline char pkgconf_buffer_lastc(const pkgconf_buffer_t *buffer) {
+ if (buffer->base == buffer->end)
+ return '\0';
+
+ return *(buffer->end - 1);
+}
+
+#define PKGCONF_BUFFER_INITIALIZER { NULL, NULL }
+
+static inline void pkgconf_buffer_reset(pkgconf_buffer_t *buffer) {
+ pkgconf_buffer_finalize(buffer);
+ buffer->base = buffer->end = NULL;
+}
+
+/* fileio.c */
+PKGCONF_API bool pkgconf_fgetline(pkgconf_buffer_t *buffer, FILE *stream);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libpkgconf/meson.build b/libpkgconf/meson.build
new file mode 100644
index 000000000000..5c393b0ef73c
--- /dev/null
+++ b/libpkgconf/meson.build
@@ -0,0 +1,12 @@
+configure_file(input : 'config.h.meson',
+ output : 'config.h',
+ configuration : cdata)
+
+
+install_headers('libpkgconf.h',
+ 'stdinc.h',
+ 'iter.h',
+ 'bsdstubs.h',
+ 'libpkgconf-api.h',
+ subdir : 'pkgconf/libpkgconf')
+
diff --git a/libpkgconf/parser.c b/libpkgconf/parser.c
new file mode 100644
index 000000000000..3a66b8af2a8d
--- /dev/null
+++ b/libpkgconf/parser.c
@@ -0,0 +1,115 @@
+/*
+ * parser.c
+ * rfc822 message parser
+ *
+ * Copyright (c) 2018 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/config.h>
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_new_from_file(const pkgconf_client_t *client, const char *filename, FILE *f)
+ *
+ * Parse a .pc file into a pkgconf_pkg_t object structure.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param char* filename: The filename of the package file (including full path).
+ * :param FILE* f: The file object to read from.
+ * :returns: A ``pkgconf_pkg_t`` object which contains the package data.
+ * :rtype: pkgconf_pkg_t *
+ */
+void
+pkgconf_parser_parse(FILE *f, void *data, const pkgconf_parser_operand_func_t *ops, const pkgconf_parser_warn_func_t warnfunc, const char *filename)
+{
+ pkgconf_buffer_t readbuf = PKGCONF_BUFFER_INITIALIZER;
+ size_t lineno = 0;
+ bool continue_reading = true;
+
+ while (continue_reading)
+ {
+ char op, *p, *key, *value;
+ bool warned_key_whitespace = false, warned_value_whitespace = false;
+
+ continue_reading = pkgconf_fgetline(&readbuf, f);
+ lineno++;
+
+ p = readbuf.base;
+ if (p == NULL)
+ continue;
+ while (*p && isspace((unsigned char)*p))
+ p++;
+ if (*p && p != readbuf.base)
+ {
+ warnfunc(data, "%s:" SIZE_FMT_SPECIFIER ": warning: whitespace encountered while parsing key section\n",
+ filename, lineno);
+ warned_key_whitespace = true;
+ }
+ key = p;
+ while (*p && (isalpha((unsigned char)*p) || isdigit((unsigned char)*p) || *p == '_' || *p == '.'))
+ p++;
+
+ if (!isalpha((unsigned char)*key) &&
+ !isdigit((unsigned char)*p))
+ {
+ pkgconf_buffer_reset(&readbuf);
+ continue;
+ }
+
+ while (*p && isspace((unsigned char)*p))
+ {
+ if (!warned_key_whitespace)
+ {
+ warnfunc(data, "%s:" SIZE_FMT_SPECIFIER ": warning: whitespace encountered while parsing key section\n",
+ filename, lineno);
+ warned_key_whitespace = true;
+ }
+
+ /* set to null to avoid trailing spaces in key */
+ *p = '\0';
+ p++;
+ }
+
+ op = *p;
+ if (*p != '\0')
+ {
+ *p = '\0';
+ p++;
+ }
+
+ while (*p && isspace((unsigned char)*p))
+ p++;
+
+ value = p;
+ p = value + (strlen(value) - 1);
+ while (*p && isspace((unsigned char) *p) && p > value)
+ {
+ if (!warned_value_whitespace && op == '=')
+ {
+ warnfunc(data, "%s:" SIZE_FMT_SPECIFIER ": warning: trailing whitespace encountered while parsing value section\n",
+ filename, lineno);
+ warned_value_whitespace = true;
+ }
+
+ *p = '\0';
+ p--;
+ }
+ if (ops[(unsigned char) op])
+ ops[(unsigned char) op](data, lineno, key, value);
+
+ pkgconf_buffer_reset(&readbuf);
+ }
+
+ pkgconf_buffer_finalize(&readbuf);
+}
diff --git a/libpkgconf/path.c b/libpkgconf/path.c
new file mode 100644
index 000000000000..dda77f5664ab
--- /dev/null
+++ b/libpkgconf/path.c
@@ -0,0 +1,459 @@
+/*
+ * path.c
+ * filesystem path management
+ *
+ * Copyright (c) 2016 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/config.h>
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+#if defined(HAVE_SYS_STAT_H) && ! defined(_WIN32)
+# include <sys/stat.h>
+# define PKGCONF_CACHE_INODES
+#endif
+
+#ifdef _WIN32
+# define PKG_CONFIG_REG_KEY "Software\\pkgconfig\\PKG_CONFIG_PATH"
+#endif
+
+static bool
+#ifdef PKGCONF_CACHE_INODES
+path_list_contains_entry(const char *text, pkgconf_list_t *dirlist, struct stat *st)
+#else
+path_list_contains_entry(const char *text, pkgconf_list_t *dirlist)
+#endif
+{
+ pkgconf_node_t *n;
+
+ PKGCONF_FOREACH_LIST_ENTRY(dirlist->head, n)
+ {
+ pkgconf_path_t *pn = n->data;
+
+#ifdef PKGCONF_CACHE_INODES
+ if (pn->handle_device == (void *)(intptr_t)st->st_dev && pn->handle_path == (void *)(intptr_t)st->st_ino)
+ return true;
+#endif
+
+ if (!strcmp(text, pn->path))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * !doc
+ *
+ * libpkgconf `path` module
+ * ========================
+ *
+ * The `path` module provides functions for manipulating lists of paths in a cross-platform manner. Notably,
+ * it is used by the `pkgconf client` to parse the ``PKG_CONFIG_PATH``, ``PKG_CONFIG_LIBDIR`` and related environment
+ * variables.
+ */
+
+static pkgconf_path_t *
+prepare_path_node(const char *text, pkgconf_list_t *dirlist, bool filter)
+{
+ pkgconf_path_t *node;
+ char path[PKGCONF_ITEM_SIZE];
+
+ pkgconf_strlcpy(path, text, sizeof path);
+ pkgconf_path_relocate(path, sizeof path);
+
+#ifdef PKGCONF_CACHE_INODES
+ struct stat st;
+
+ if (filter)
+ {
+ if (lstat(path, &st) == -1)
+ return NULL;
+ if (S_ISLNK(st.st_mode))
+ {
+ char pathbuf[PKGCONF_ITEM_SIZE * 4];
+ char *linkdest = realpath(path, pathbuf);
+
+ if (linkdest != NULL && stat(linkdest, &st) == -1)
+ return NULL;
+ }
+ if (path_list_contains_entry(path, dirlist, &st))
+ return NULL;
+ }
+#else
+ if (filter && path_list_contains_entry(path, dirlist))
+ return NULL;
+#endif
+
+ node = calloc(1, sizeof(pkgconf_path_t));
+ if (node == NULL)
+ return NULL;
+
+ node->path = strdup(path);
+
+#ifdef PKGCONF_CACHE_INODES
+ if (filter) {
+ node->handle_path = (void *)(intptr_t) st.st_ino;
+ node->handle_device = (void *)(intptr_t) st.st_dev;
+ }
+#endif
+
+ return node;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_path_add(const char *text, pkgconf_list_t *dirlist)
+ *
+ * Adds a path node to a path list. If the path is already in the list, do nothing.
+ *
+ * :param char* text: The path text to add as a path node.
+ * :param pkgconf_list_t* dirlist: The path list to add the path node to.
+ * :param bool filter: Whether to perform duplicate filtering.
+ * :return: nothing
+ */
+void
+pkgconf_path_add(const char *text, pkgconf_list_t *dirlist, bool filter)
+{
+ pkgconf_path_t *node = prepare_path_node(text, dirlist, filter);
+ if (node == NULL)
+ return;
+
+ pkgconf_node_insert_tail(&node->lnode, node, dirlist);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_path_prepend(const char *text, pkgconf_list_t *dirlist)
+ *
+ * Prepends a path node to a path list. If the path is already in the list, do nothing.
+ *
+ * :param char* text: The path text to add as a path node.
+ * :param pkgconf_list_t* dirlist: The path list to add the path node to.
+ * :param bool filter: Whether to perform duplicate filtering.
+ * :return: nothing
+ */
+void
+pkgconf_path_prepend(const char *text, pkgconf_list_t *dirlist, bool filter)
+{
+ pkgconf_path_t *node = prepare_path_node(text, dirlist, filter);
+ if (node == NULL)
+ return;
+
+ pkgconf_node_insert(&node->lnode, node, dirlist);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: size_t pkgconf_path_split(const char *text, pkgconf_list_t *dirlist)
+ *
+ * Splits a given text input and inserts paths into a path list.
+ *
+ * :param char* text: The path text to split and add as path nodes.
+ * :param pkgconf_list_t* dirlist: The path list to have the path nodes added to.
+ * :param bool filter: Whether to perform duplicate filtering.
+ * :return: number of path nodes added to the path list
+ * :rtype: size_t
+ */
+size_t
+pkgconf_path_split(const char *text, pkgconf_list_t *dirlist, bool filter)
+{
+ size_t count = 0;
+ char *workbuf, *p, *iter;
+
+ if (text == NULL)
+ return 0;
+
+ iter = workbuf = strdup(text);
+ while ((p = strtok(iter, PKG_CONFIG_PATH_SEP_S)) != NULL)
+ {
+ pkgconf_path_add(p, dirlist, filter);
+
+ count++, iter = NULL;
+ }
+ free(workbuf);
+
+ return count;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: size_t pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist)
+ *
+ * Adds the paths specified in an environment variable to a path list. If the environment variable is not set,
+ * an optional default set of paths is added.
+ *
+ * :param char* envvarname: The environment variable to look up.
+ * :param char* fallback: The fallback paths to use if the environment variable is not set.
+ * :param pkgconf_list_t* dirlist: The path list to add the path nodes to.
+ * :param bool filter: Whether to perform duplicate filtering.
+ * :return: number of path nodes added to the path list
+ * :rtype: size_t
+ */
+size_t
+pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist, bool filter)
+{
+ const char *data;
+
+ data = getenv(envvarname);
+ if (data != NULL)
+ return pkgconf_path_split(data, dirlist, filter);
+
+ if (fallback != NULL)
+ return pkgconf_path_split(fallback, dirlist, filter);
+
+ /* no fallback and no environment variable, thusly no nodes added */
+ return 0;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist)
+ *
+ * Checks whether a path has a matching prefix in a path list.
+ *
+ * :param char* path: The path to check against a path list.
+ * :param pkgconf_list_t* dirlist: The path list to check the path against.
+ * :return: true if the path list has a matching prefix, otherwise false
+ * :rtype: bool
+ */
+bool
+pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist)
+{
+ pkgconf_node_t *n = NULL;
+ char relocated[PKGCONF_ITEM_SIZE];
+ const char *cpath = path;
+
+ pkgconf_strlcpy(relocated, path, sizeof relocated);
+ if (pkgconf_path_relocate(relocated, sizeof relocated))
+ cpath = relocated;
+
+ PKGCONF_FOREACH_LIST_ENTRY(dirlist->head, n)
+ {
+ pkgconf_path_t *pnode = n->data;
+
+ if (!strcmp(pnode->path, cpath))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
+ *
+ * Copies a path list to another path list.
+ *
+ * :param pkgconf_list_t* dst: The path list to copy to.
+ * :param pkgconf_list_t* src: The path list to copy from.
+ * :return: nothing
+ */
+void
+pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
+{
+ pkgconf_node_t *n;
+
+ PKGCONF_FOREACH_LIST_ENTRY(src->head, n)
+ {
+ pkgconf_path_t *srcpath = n->data, *path;
+
+ path = calloc(1, sizeof(pkgconf_path_t));
+ if (path == NULL)
+ continue;
+
+ path->path = strdup(srcpath->path);
+
+#ifdef PKGCONF_CACHE_INODES
+ path->handle_path = srcpath->handle_path;
+ path->handle_device = srcpath->handle_device;
+#endif
+
+ pkgconf_node_insert_tail(&path->lnode, path, dst);
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_path_prepend_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
+ *
+ * Copies a path list to another path list.
+ *
+ * :param pkgconf_list_t* dst: The path list to copy to.
+ * :param pkgconf_list_t* src: The path list to copy from.
+ * :return: nothing
+ */
+void
+pkgconf_path_prepend_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
+{
+ pkgconf_node_t *n;
+
+ PKGCONF_FOREACH_LIST_ENTRY(src->head, n)
+ {
+ pkgconf_path_t *srcpath = n->data, *path;
+
+ path = calloc(1, sizeof(pkgconf_path_t));
+ if (path == NULL)
+ continue;
+
+ path->path = strdup(srcpath->path);
+
+#ifdef PKGCONF_CACHE_INODES
+ path->handle_path = srcpath->handle_path;
+ path->handle_device = srcpath->handle_device;
+#endif
+
+ pkgconf_node_insert(&path->lnode, path, dst);
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_path_free(pkgconf_list_t *dirlist)
+ *
+ * Releases any path nodes attached to the given path list.
+ *
+ * :param pkgconf_list_t* dirlist: The path list to clean up.
+ * :return: nothing
+ */
+void
+pkgconf_path_free(pkgconf_list_t *dirlist)
+{
+ pkgconf_node_t *n, *tn;
+
+ PKGCONF_FOREACH_LIST_ENTRY_SAFE(dirlist->head, tn, n)
+ {
+ pkgconf_path_t *pnode = n->data;
+
+ free(pnode->path);
+ free(pnode);
+ }
+
+ pkgconf_list_zero(dirlist);
+}
+
+static char *
+normpath(const char *path)
+{
+ if (!path)
+ return NULL;
+
+ char *copy = strdup(path);
+ if (NULL == copy)
+ return NULL;
+ char *ptr = copy;
+
+ for (int ii = 0; copy[ii]; ii++)
+ {
+ *ptr++ = path[ii];
+ if ('/' == path[ii])
+ {
+ ii++;
+ while ('/' == path[ii])
+ ii++;
+ ii--;
+ }
+ }
+ *ptr = '\0';
+
+ return copy;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_path_relocate(char *buf, size_t buflen)
+ *
+ * Relocates a path, possibly calling normpath() on it.
+ *
+ * :param char* buf: The path to relocate.
+ * :param size_t buflen: The buffer length the path is contained in.
+ * :return: true on success, false on error
+ * :rtype: bool
+ */
+bool
+pkgconf_path_relocate(char *buf, size_t buflen)
+{
+ char *tmpbuf;
+
+ if ((tmpbuf = normpath(buf)) != NULL)
+ {
+ size_t tmpbuflen = strlen(tmpbuf);
+ if (tmpbuflen > buflen)
+ {
+ free(tmpbuf);
+ return false;
+ }
+
+ pkgconf_strlcpy(buf, tmpbuf, buflen);
+ free(tmpbuf);
+ }
+
+ return true;
+}
+
+#ifdef _WIN32
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_path_build_from_registry(HKEY hKey, pkgconf_list_t *dir_list, bool filter)
+ *
+ * Adds paths to a directory list discovered from a given registry key.
+ *
+ * :param HKEY hKey: The registry key to enumerate.
+ * :param pkgconf_list_t* dir_list: The directory list to append enumerated paths to.
+ * :param bool filter: Whether duplicate paths should be filtered.
+ * :return: number of path nodes added to the list
+ * :rtype: size_t
+ */
+size_t
+pkgconf_path_build_from_registry(void *hKey, pkgconf_list_t *dir_list, bool filter)
+{
+ HKEY key;
+ int i = 0;
+ size_t added = 0;
+
+ char buf[16384]; /* per registry limits */
+ DWORD bufsize = sizeof buf;
+ if (RegOpenKeyEx(hKey, PKG_CONFIG_REG_KEY,
+ 0, KEY_READ, &key) != ERROR_SUCCESS)
+ return 0;
+
+ while (RegEnumValue(key, i++, buf, &bufsize, NULL, NULL, NULL, NULL)
+ == ERROR_SUCCESS)
+ {
+ char pathbuf[PKGCONF_ITEM_SIZE];
+ DWORD type;
+ DWORD pathbuflen = sizeof pathbuf;
+
+ if (RegQueryValueEx(key, buf, NULL, &type, (LPBYTE) pathbuf, &pathbuflen)
+ == ERROR_SUCCESS && type == REG_SZ)
+ {
+ pkgconf_path_add(pathbuf, dir_list, filter);
+ added++;
+ }
+
+ bufsize = sizeof buf;
+ }
+
+ RegCloseKey(key);
+ return added;
+}
+#endif
diff --git a/libpkgconf/personality.c b/libpkgconf/personality.c
new file mode 100644
index 000000000000..6c017e912776
--- /dev/null
+++ b/libpkgconf/personality.c
@@ -0,0 +1,359 @@
+/*
+ * personality.c
+ * libpkgconf cross-compile personality database
+ *
+ * Copyright (c) 2018 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/config.h>
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !doc
+ *
+ * libpkgconf `personality` module
+ * =========================
+ */
+
+#ifdef _WIN32
+# define strcasecmp _stricmp
+#endif
+
+/*
+ * Increment each time the default personality is inited, decrement each time
+ * it's deinited. Whenever it is 0, then the deinit frees the personality. In
+ * that case an additional call to init will create it anew.
+ */
+static unsigned default_personality_init = 0;
+
+static pkgconf_cross_personality_t default_personality = {
+ .name = "default",
+};
+
+static inline void
+build_default_search_path(pkgconf_list_t* dirlist)
+{
+#ifdef _WIN32
+ char namebuf[MAX_PATH];
+ char outbuf[MAX_PATH];
+ char *p;
+
+ int sizepath = GetModuleFileName(NULL, namebuf, sizeof namebuf);
+ char * winslash;
+ namebuf[sizepath] = '\0';
+ while ((winslash = strchr (namebuf, '\\')) != NULL)
+ {
+ *winslash = '/';
+ }
+ p = strrchr(namebuf, '/');
+ if (p == NULL)
+ pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, true);
+
+ *p = '\0';
+ pkgconf_strlcpy(outbuf, namebuf, sizeof outbuf);
+ pkgconf_strlcat(outbuf, "/", sizeof outbuf);
+ pkgconf_strlcat(outbuf, "../lib/pkgconfig", sizeof outbuf);
+ pkgconf_path_add(outbuf, dirlist, true);
+ pkgconf_strlcpy(outbuf, namebuf, sizeof outbuf);
+ pkgconf_strlcat(outbuf, "/", sizeof outbuf);
+ pkgconf_strlcat(outbuf, "../share/pkgconfig", sizeof outbuf);
+ pkgconf_path_add(outbuf, dirlist, true);
+#elif __HAIKU__
+ char **paths;
+ size_t count;
+ if (find_paths(B_FIND_PATH_DEVELOP_LIB_DIRECTORY, "pkgconfig", &paths, &count) == B_OK) {
+ for (size_t i = 0; i < count; i++)
+ pkgconf_path_add(paths[i], dirlist, true);
+ free(paths);
+ paths = NULL;
+ }
+ if (find_paths(B_FIND_PATH_DATA_DIRECTORY, "pkgconfig", &paths, &count) == B_OK) {
+ for (size_t i = 0; i < count; i++)
+ pkgconf_path_add(paths[i], dirlist, true);
+ free(paths);
+ paths = NULL;
+ }
+#else
+ pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, true);
+#endif
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: const pkgconf_cross_personality_t *pkgconf_cross_personality_default(void)
+ *
+ * Returns the default cross-compile personality.
+ *
+ * Not thread safe.
+ *
+ * :rtype: pkgconf_cross_personality_t*
+ * :return: the default cross-compile personality
+ */
+pkgconf_cross_personality_t *
+pkgconf_cross_personality_default(void)
+{
+ if (default_personality_init) {
+ ++default_personality_init;
+ return &default_personality;
+ }
+
+ build_default_search_path(&default_personality.dir_list);
+
+ pkgconf_path_split(SYSTEM_LIBDIR, &default_personality.filter_libdirs, false);
+ pkgconf_path_split(SYSTEM_INCLUDEDIR, &default_personality.filter_includedirs, false);
+
+ ++default_personality_init;
+ return &default_personality;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *)
+ *
+ * Destroys a cross personality object and/or decreases the reference count on the
+ * default cross personality object.
+ *
+ * Not thread safe.
+ *
+ * :rtype: void
+ */
+void
+pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *personality)
+{
+ /* allow NULL parameter for API backwards compatibility */
+ if (personality == NULL)
+ return;
+
+ /* XXX: this hack is rather ugly, but it works for now... */
+ if (personality == &default_personality && --default_personality_init > 0)
+ return;
+
+ pkgconf_path_free(&personality->dir_list);
+ pkgconf_path_free(&personality->filter_libdirs);
+ pkgconf_path_free(&personality->filter_includedirs);
+
+ if (personality->sysroot_dir != NULL)
+ free(personality->sysroot_dir);
+
+ if (personality == &default_personality)
+ return;
+
+ if (personality->name != NULL)
+ free(personality->name);
+
+ free(personality);
+}
+
+#ifndef PKGCONF_LITE
+static bool
+valid_triplet(const char *triplet)
+{
+ const char *c = triplet;
+
+ for (; *c; c++)
+ if (!isalnum((unsigned char)*c) && *c != '-' && *c != '_')
+ return false;
+
+ return true;
+}
+
+typedef void (*personality_keyword_func_t)(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value);
+typedef struct {
+ const char *keyword;
+ const personality_keyword_func_t func;
+ const ptrdiff_t offset;
+} personality_keyword_pair_t;
+
+static void
+personality_bool_func(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value)
+{
+ (void) keyword;
+ (void) lineno;
+
+ bool *dest = (bool *)((char *) p + offset);
+ *dest = strcasecmp(value, "true") || strcasecmp(value, "yes") || *value == '1';
+}
+
+static void
+personality_copy_func(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value)
+{
+ (void) keyword;
+ (void) lineno;
+
+ char **dest = (char **)((char *) p + offset);
+ *dest = strdup(value);
+}
+
+static void
+personality_fragment_func(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value)
+{
+ (void) keyword;
+ (void) lineno;
+
+ pkgconf_list_t *dest = (pkgconf_list_t *)((char *) p + offset);
+ pkgconf_path_split(value, dest, false);
+}
+
+/* keep in alphabetical order! */
+static const personality_keyword_pair_t personality_keyword_pairs[] = {
+ {"DefaultSearchPaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, dir_list)},
+ {"SysrootDir", personality_copy_func, offsetof(pkgconf_cross_personality_t, sysroot_dir)},
+ {"SystemIncludePaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, filter_includedirs)},
+ {"SystemLibraryPaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, filter_libdirs)},
+ {"Triplet", personality_copy_func, offsetof(pkgconf_cross_personality_t, name)},
+ {"WantDefaultPure", personality_bool_func, offsetof(pkgconf_cross_personality_t, want_default_pure)},
+ {"WantDefaultStatic", personality_bool_func, offsetof(pkgconf_cross_personality_t, want_default_static)},
+};
+
+static int
+personality_keyword_pair_cmp(const void *key, const void *ptr)
+{
+ const personality_keyword_pair_t *pair = ptr;
+ return strcasecmp(key, pair->keyword);
+}
+
+static void
+personality_keyword_set(pkgconf_cross_personality_t *p, const size_t lineno, const char *keyword, char *value)
+{
+ const personality_keyword_pair_t *pair = bsearch(keyword,
+ personality_keyword_pairs, PKGCONF_ARRAY_SIZE(personality_keyword_pairs),
+ sizeof(personality_keyword_pair_t), personality_keyword_pair_cmp);
+
+ if (pair == NULL || pair->func == NULL)
+ return;
+
+ pair->func(p, keyword, lineno, pair->offset, value);
+}
+
+static const pkgconf_parser_operand_func_t personality_parser_ops[256] = {
+ [':'] = (pkgconf_parser_operand_func_t) personality_keyword_set
+};
+
+static void personality_warn_func(void *p, const char *fmt, ...) PRINTFLIKE(2, 3);
+
+static void
+personality_warn_func(void *p, const char *fmt, ...)
+{
+ va_list va;
+
+ (void) p;
+
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+}
+
+static pkgconf_cross_personality_t *
+load_personality_with_path(const char *path, const char *triplet, bool datadir)
+{
+ char pathbuf[PKGCONF_ITEM_SIZE];
+ FILE *f;
+ pkgconf_cross_personality_t *p;
+
+ /* if triplet is null, assume that path is a direct path to the personality file */
+ if (triplet == NULL)
+ pkgconf_strlcpy(pathbuf, path, sizeof pathbuf);
+ else if (datadir)
+ snprintf(pathbuf, sizeof pathbuf, "%s/pkgconfig/personality.d/%s.personality", path, triplet);
+ else
+ snprintf(pathbuf, sizeof pathbuf, "%s/%s.personality", path, triplet);
+
+ p = calloc(1, sizeof(pkgconf_cross_personality_t));
+ if (p == NULL)
+ return NULL;
+
+ if (triplet != NULL)
+ p->name = strdup(triplet);
+
+ f = fopen(pathbuf, "r");
+ if (f == NULL) {
+ pkgconf_cross_personality_deinit(p);
+ return NULL;
+ }
+
+ pkgconf_parser_parse(f, p, personality_parser_ops, personality_warn_func, pathbuf);
+
+ return p;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_cross_personality_t *pkgconf_cross_personality_find(const char *triplet)
+ *
+ * Attempts to find a cross-compile personality given a triplet.
+ *
+ * :rtype: pkgconf_cross_personality_t*
+ * :return: the default cross-compile personality
+ */
+pkgconf_cross_personality_t *
+pkgconf_cross_personality_find(const char *triplet)
+{
+ pkgconf_list_t plist = PKGCONF_LIST_INITIALIZER;
+ pkgconf_node_t *n;
+ pkgconf_cross_personality_t *out = NULL;
+#if ! defined(_WIN32) && ! defined(__HAIKU__)
+ char pathbuf[PKGCONF_ITEM_SIZE];
+ const char *envvar;
+#endif
+
+ out = load_personality_with_path(triplet, NULL, false);
+ if (out != NULL)
+ return out;
+
+ if (!valid_triplet(triplet))
+ return NULL;
+
+#if ! defined(_WIN32) && ! defined(__HAIKU__)
+ envvar = getenv("XDG_DATA_HOME");
+ if (envvar != NULL)
+ pkgconf_path_add(envvar, &plist, true);
+ else {
+ envvar = getenv("HOME");
+ if (envvar != NULL) {
+ pkgconf_strlcpy(pathbuf, envvar, sizeof pathbuf);
+ pkgconf_strlcat(pathbuf, "/.local/share", sizeof pathbuf);
+ pkgconf_path_add(pathbuf, &plist, true);
+ }
+ }
+
+ pkgconf_path_build_from_environ("XDG_DATA_DIRS", "/usr/local/share" PKG_CONFIG_PATH_SEP_S "/usr/share", &plist, true);
+
+ PKGCONF_FOREACH_LIST_ENTRY(plist.head, n)
+ {
+ pkgconf_path_t *pn = n->data;
+
+ out = load_personality_with_path(pn->path, triplet, true);
+ if (out != NULL)
+ goto finish;
+ }
+ pkgconf_path_free(&plist);
+#endif
+
+ pkgconf_path_split(PERSONALITY_PATH, &plist, true);
+
+ PKGCONF_FOREACH_LIST_ENTRY(plist.head, n)
+ {
+ pkgconf_path_t *pn = n->data;
+
+ out = load_personality_with_path(pn->path, triplet, false);
+ if (out != NULL)
+ goto finish;
+ }
+
+finish:
+ pkgconf_path_free(&plist);
+ return out != NULL ? out : pkgconf_cross_personality_default();
+}
+#endif
diff --git a/libpkgconf/pkg.c b/libpkgconf/pkg.c
new file mode 100644
index 000000000000..491c0defcd65
--- /dev/null
+++ b/libpkgconf/pkg.c
@@ -0,0 +1,1935 @@
+/*
+ * pkg.c
+ * higher-level dependency graph compilation, management and manipulation
+ *
+ * Copyright (c) 2011, 2012, 2013 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/config.h>
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+#ifndef _WIN32
+#include <fcntl.h> // open
+#include <libgen.h> // basename/dirname
+#include <sys/stat.h> // lstat, S_ISLNK
+#include <unistd.h> // close, readlinkat
+
+#include <string.h>
+#endif
+
+/*
+ * !doc
+ *
+ * libpkgconf `pkg` module
+ * =======================
+ *
+ * The `pkg` module provides dependency resolution services and the overall `.pc` file parsing
+ * routines.
+ */
+
+#ifdef _WIN32
+# undef PKG_DEFAULT_PATH
+# define PKG_DEFAULT_PATH "../lib/pkgconfig;../share/pkgconfig"
+# define strncasecmp _strnicmp
+# define strcasecmp _stricmp
+#endif
+
+#define PKG_CONFIG_EXT ".pc"
+
+static unsigned int
+pkgconf_pkg_traverse_main(pkgconf_client_t *client,
+ pkgconf_pkg_t *root,
+ pkgconf_pkg_traverse_func_t func,
+ void *data,
+ int maxdepth,
+ unsigned int skip_flags);
+
+static inline bool
+str_has_suffix(const char *str, const char *suffix)
+{
+ size_t str_len = strlen(str);
+ size_t suf_len = strlen(suffix);
+
+ if (str_len < suf_len)
+ return false;
+
+ return !strncasecmp(str + str_len - suf_len, suffix, suf_len);
+}
+
+static char *
+pkg_get_parent_dir(pkgconf_pkg_t *pkg)
+{
+ char buf[PKGCONF_ITEM_SIZE], *pathbuf;
+
+ pkgconf_strlcpy(buf, pkg->filename, sizeof buf);
+#ifndef _WIN32
+ /*
+ * We want to resolve symlinks, since ${pcfiledir} should point to the
+ * parent of the file symlinked to.
+ */
+ struct stat path_stat;
+ while (!lstat(buf, &path_stat) && S_ISLNK(path_stat.st_mode))
+ {
+ /*
+ * Have to split the path into the dir + file components,
+ * in order to extract the directory file descriptor.
+ *
+ * The nomenclature here uses the
+ *
+ * ln <source> <target>
+ *
+ * model.
+ */
+ char basenamebuf[PKGCONF_ITEM_SIZE];
+ pkgconf_strlcpy(basenamebuf, buf, sizeof(basenamebuf));
+ const char* targetfilename = basename(basenamebuf);
+
+ char dirnamebuf[PKGCONF_ITEM_SIZE];
+ pkgconf_strlcpy(dirnamebuf, buf, sizeof(dirnamebuf));
+ const char* targetdir = dirname(dirnamebuf);
+
+ const int dirfd = open(targetdir, O_DIRECTORY);
+ if (dirfd == -1)
+ break;
+
+ char sourcebuf[PKGCONF_ITEM_SIZE];
+ ssize_t len = readlinkat(dirfd, targetfilename, sourcebuf, sizeof(sourcebuf) - 1);
+ close(dirfd);
+
+ if (len == -1)
+ break;
+ sourcebuf[len] = '\0';
+
+ memset(buf, '\0', sizeof buf);
+ /*
+ * The logic here can be a bit tricky, so here's a table:
+ *
+ * <source> | <target> | result
+ * -----------------------------------------------------------------------
+ * /bar (absolute) | foo/link (relative) | /bar (absolute)
+ * ../bar (relative) | foo/link (relative) | foo/../bar (relative)
+ * /bar (absolute) | /foo/link (absolute) | /bar (absolute)
+ * ../bar (relative) | /foo/link (absolute) | /foo/../bar (relative)
+ */
+ if ((sourcebuf[0] != '/') /* absolute path in <source> wins */
+ && (strcmp(targetdir, "."))) /* do not prepend "." */
+ {
+ pkgconf_strlcat(buf, targetdir, sizeof buf);
+ pkgconf_strlcat(buf, "/", sizeof buf);
+ }
+ pkgconf_strlcat(buf, sourcebuf, sizeof buf);
+ }
+#endif
+
+ pathbuf = strrchr(buf, PKG_DIR_SEP_S);
+ if (pathbuf == NULL)
+ pathbuf = strrchr(buf, '/');
+ if (pathbuf != NULL)
+ pathbuf[0] = '\0';
+
+ return strdup(buf);
+}
+
+typedef void (*pkgconf_pkg_parser_keyword_func_t)(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value);
+typedef struct {
+ const char *keyword;
+ const pkgconf_pkg_parser_keyword_func_t func;
+ const ptrdiff_t offset;
+} pkgconf_pkg_parser_keyword_pair_t;
+
+static int pkgconf_pkg_parser_keyword_pair_cmp(const void *key, const void *ptr)
+{
+ const pkgconf_pkg_parser_keyword_pair_t *pair = ptr;
+ return strcasecmp(key, pair->keyword);
+}
+
+static void
+pkgconf_pkg_parser_tuple_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
+{
+ (void) keyword;
+ (void) lineno;
+
+ char **dest = (char **)((char *) pkg + offset);
+ *dest = pkgconf_tuple_parse(client, &pkg->vars, value, pkg->flags);
+}
+
+static void
+pkgconf_pkg_parser_version_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
+{
+ (void) keyword;
+ (void) lineno;
+ char *p, *i;
+ size_t len;
+ char **dest = (char **)((char *) pkg + offset);
+
+ /* cut at any detected whitespace */
+ p = pkgconf_tuple_parse(client, &pkg->vars, value, pkg->flags);
+
+ len = strcspn(p, " \t");
+ if (len != strlen(p))
+ {
+ i = p + (ptrdiff_t) len;
+ *i = '\0';
+
+ pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: malformed version field with whitespace, trimming to [%s]\n", pkg->filename,
+ lineno, p);
+ }
+
+ *dest = p;
+}
+
+static void
+pkgconf_pkg_parser_fragment_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
+{
+ pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
+
+ /* we patch client-wide sysroot dir and then patch it back when it is overridden */
+ char *sysroot_dir = client->sysroot_dir;
+ char *pkg_sysroot_dir = pkgconf_tuple_find(client, &pkg->vars, "pc_sysrootdir");
+ if (pkg_sysroot_dir != NULL)
+ client->sysroot_dir = pkg_sysroot_dir;
+
+ bool ret = pkgconf_fragment_parse(client, dest, &pkg->vars, value, pkg->flags);
+ client->sysroot_dir = sysroot_dir;
+
+ if (!ret)
+ {
+ pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: unable to parse field '%s' into an argument vector, value [%s]\n", pkg->filename,
+ lineno, keyword, value);
+ }
+}
+
+static void
+pkgconf_pkg_parser_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
+{
+ (void) keyword;
+ (void) lineno;
+
+ pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
+ pkgconf_dependency_parse(client, pkg, dest, value, 0);
+}
+
+/* a variant of pkgconf_pkg_parser_dependency_func which colors the dependency node as an "internal" dependency. */
+static void
+pkgconf_pkg_parser_internal_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
+{
+ (void) keyword;
+ (void) lineno;
+
+ pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
+ pkgconf_dependency_parse(client, pkg, dest, value, PKGCONF_PKG_DEPF_INTERNAL);
+}
+
+/* a variant of pkgconf_pkg_parser_dependency_func which colors the dependency node as a "private" dependency. */
+static void
+pkgconf_pkg_parser_private_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
+{
+ (void) keyword;
+ (void) lineno;
+
+ pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
+ pkgconf_dependency_parse(client, pkg, dest, value, PKGCONF_PKG_DEPF_PRIVATE);
+}
+
+/* keep this in alphabetical order */
+static const pkgconf_pkg_parser_keyword_pair_t pkgconf_pkg_parser_keyword_funcs[] = {
+ {"CFLAGS", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, cflags)},
+ {"CFLAGS.private", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, cflags_private)},
+ {"Conflicts", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, conflicts)},
+ {"Copyright", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, copyright)},
+ {"Description", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, description)},
+ {"LIBS", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, libs)},
+ {"LIBS.private", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, libs_private)},
+ {"License", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, license)},
+ {"Maintainer", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, maintainer)},
+ {"Name", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, realname)},
+ {"Provides", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, provides)},
+ {"Requires", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, required)},
+ {"Requires.internal", pkgconf_pkg_parser_internal_dependency_func, offsetof(pkgconf_pkg_t, requires_private)},
+ {"Requires.private", pkgconf_pkg_parser_private_dependency_func, offsetof(pkgconf_pkg_t, requires_private)},
+ {"URL", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, url)},
+ {"Version", pkgconf_pkg_parser_version_func, offsetof(pkgconf_pkg_t, version)},
+};
+
+static void
+pkgconf_pkg_parser_keyword_set(void *opaque, const size_t lineno, const char *keyword, const char *value)
+{
+ pkgconf_pkg_t *pkg = opaque;
+
+ const pkgconf_pkg_parser_keyword_pair_t *pair = bsearch(keyword,
+ pkgconf_pkg_parser_keyword_funcs, PKGCONF_ARRAY_SIZE(pkgconf_pkg_parser_keyword_funcs),
+ sizeof(pkgconf_pkg_parser_keyword_pair_t), pkgconf_pkg_parser_keyword_pair_cmp);
+
+ if (pair == NULL || pair->func == NULL)
+ return;
+
+ pair->func(pkg->owner, pkg, keyword, lineno, pair->offset, value);
+}
+
+static const char *
+determine_prefix(const pkgconf_pkg_t *pkg, char *buf, size_t buflen)
+{
+ char *pathiter;
+
+ pkgconf_strlcpy(buf, pkg->filename, buflen);
+ pkgconf_path_relocate(buf, buflen);
+
+ pathiter = strrchr(buf, PKG_DIR_SEP_S);
+ if (pathiter == NULL)
+ pathiter = strrchr(buf, '/');
+ if (pathiter != NULL)
+ pathiter[0] = '\0';
+
+ pathiter = strrchr(buf, PKG_DIR_SEP_S);
+ if (pathiter == NULL)
+ pathiter = strrchr(buf, '/');
+ if (pathiter == NULL)
+ return NULL;
+
+ /* parent dir is not pkgconfig, can't relocate then */
+ if (strcmp(pathiter + 1, "pkgconfig"))
+ return NULL;
+
+ /* okay, work backwards and do it again. */
+ pathiter[0] = '\0';
+ pathiter = strrchr(buf, PKG_DIR_SEP_S);
+ if (pathiter == NULL)
+ pathiter = strrchr(buf, '/');
+ if (pathiter == NULL)
+ return NULL;
+
+ pathiter[0] = '\0';
+
+ return buf;
+}
+
+/*
+ * Takes a real path and converts it to a pkgconf value. This means normalizing
+ * directory separators and escaping things (only spaces covered atm).
+ *
+ * This is useful for things like prefix/pcfiledir which might get injected
+ * at runtime and are not sourced from the .pc file.
+ *
+ * "C:\foo bar\baz" -> "C:/foo\ bar/baz"
+ * "/foo bar/baz" -> "/foo\ bar/baz"
+ */
+static char *
+convert_path_to_value(const char *path)
+{
+ char *buf = calloc(1, (strlen(path) + 1) * 2);
+ if (buf == NULL)
+ return NULL;
+
+ char *bptr = buf;
+ const char *i;
+
+ for (i = path; *i != '\0'; i++)
+ {
+ if (*i == PKG_DIR_SEP_S)
+ *bptr++ = '/';
+ else if (*i == ' ') {
+ *bptr++ = '\\';
+ *bptr++ = *i;
+ } else
+ *bptr++ = *i;
+ }
+
+ return buf;
+}
+
+static void
+remove_additional_separators(char *buf)
+{
+ char *p = buf;
+
+ while (*p) {
+ if (*p == '/') {
+ char *q;
+
+ q = ++p;
+ while (*q && *q == '/')
+ q++;
+
+ if (p != q)
+ memmove (p, q, strlen (q) + 1);
+ } else {
+ p++;
+ }
+ }
+}
+
+static void
+canonicalize_path(char *buf)
+{
+ remove_additional_separators(buf);
+}
+
+static bool
+is_path_prefix_equal(const char *path1, const char *path2, size_t path2_len)
+{
+#ifdef _WIN32
+ return !_strnicmp(path1, path2, path2_len);
+#else
+ return !strncmp(path1, path2, path2_len);
+#endif
+}
+
+static void
+pkgconf_pkg_parser_value_set(void *opaque, const size_t lineno, const char *keyword, const char *value)
+{
+ char canonicalized_value[PKGCONF_ITEM_SIZE];
+ pkgconf_pkg_t *pkg = opaque;
+
+ (void) lineno;
+
+ pkgconf_strlcpy(canonicalized_value, value, sizeof canonicalized_value);
+ canonicalize_path(canonicalized_value);
+
+ /* Some pc files will use absolute paths for all of their directories
+ * which is broken when redefining the prefix. We try to outsmart the
+ * file and rewrite any directory that starts with the same prefix.
+ */
+ if (pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX && pkg->orig_prefix
+ && is_path_prefix_equal(canonicalized_value, pkg->orig_prefix->value, strlen(pkg->orig_prefix->value)))
+ {
+ char newvalue[PKGCONF_ITEM_SIZE];
+
+ pkgconf_strlcpy(newvalue, pkg->prefix->value, sizeof newvalue);
+ pkgconf_strlcat(newvalue, canonicalized_value + strlen(pkg->orig_prefix->value), sizeof newvalue);
+ pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, newvalue, false, pkg->flags);
+ }
+ else if (strcmp(keyword, pkg->owner->prefix_varname) || !(pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX))
+ pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true, pkg->flags);
+ else
+ {
+ char pathbuf[PKGCONF_ITEM_SIZE];
+ const char *relvalue = determine_prefix(pkg, pathbuf, sizeof pathbuf);
+
+ if (relvalue != NULL)
+ {
+ char *prefix_value = convert_path_to_value(relvalue);
+ pkg->orig_prefix = pkgconf_tuple_add(pkg->owner, &pkg->vars, "orig_prefix", canonicalized_value, true, pkg->flags);
+ pkg->prefix = pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, prefix_value, false, pkg->flags);
+ free(prefix_value);
+ }
+ else
+ pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true, pkg->flags);
+ }
+}
+
+typedef struct {
+ const char *field;
+ const ptrdiff_t offset;
+} pkgconf_pkg_validity_check_t;
+
+static const pkgconf_pkg_validity_check_t pkgconf_pkg_validations[] = {
+ {"Name", offsetof(pkgconf_pkg_t, realname)},
+ {"Description", offsetof(pkgconf_pkg_t, description)},
+ {"Version", offsetof(pkgconf_pkg_t, version)},
+};
+
+static const pkgconf_parser_operand_func_t pkg_parser_funcs[256] = {
+ [':'] = pkgconf_pkg_parser_keyword_set,
+ ['='] = pkgconf_pkg_parser_value_set
+};
+
+static void pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...) PRINTFLIKE(2, 3);
+
+static void
+pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...)
+{
+ char buf[PKGCONF_ITEM_SIZE];
+ va_list va;
+
+ va_start(va, fmt);
+ vsnprintf(buf, sizeof buf, fmt, va);
+ va_end(va);
+
+ pkgconf_warn(pkg->owner, "%s", buf);
+}
+
+static bool
+pkgconf_pkg_validate(const pkgconf_client_t *client, const pkgconf_pkg_t *pkg)
+{
+ size_t i;
+ bool valid = true;
+
+ for (i = 0; i < PKGCONF_ARRAY_SIZE(pkgconf_pkg_validations); i++)
+ {
+ char **p = (char **)((char *) pkg + pkgconf_pkg_validations[i].offset);
+
+ if (*p != NULL)
+ continue;
+
+ pkgconf_warn(client, "%s: warning: file does not declare a `%s' field\n", pkg->filename, pkgconf_pkg_validations[i].field);
+ valid = false;
+ }
+
+ return valid;
+}
+
+static void
+pkg_free_object(pkgconf_pkg_t *pkg)
+{
+ if (pkg->flags & PKGCONF_PKG_PROPF_PRELOADED)
+ pkgconf_node_delete(&pkg->preload_node, &pkg->owner->preloaded_pkgs);
+
+ if (pkg->id != NULL)
+ free(pkg->id);
+
+ if (pkg->filename != NULL)
+ free(pkg->filename);
+
+ if (pkg->realname != NULL)
+ free(pkg->realname);
+
+ if (pkg->version != NULL)
+ free(pkg->version);
+
+ if (pkg->description != NULL)
+ free(pkg->description);
+
+ if (pkg->url != NULL)
+ free(pkg->url);
+
+ if (pkg->pc_filedir != NULL)
+ free(pkg->pc_filedir);
+
+ if (pkg->license != NULL)
+ free(pkg->license);
+
+ if (pkg->maintainer != NULL)
+ free(pkg->maintainer);
+
+ if (pkg->copyright != NULL)
+ free(pkg->copyright);
+
+ if (pkg->why != NULL)
+ free(pkg->why);
+
+ free(pkg);
+}
+
+static void
+pkg_free_lists(pkgconf_pkg_t *pkg)
+{
+ pkgconf_dependency_free(&pkg->required);
+ pkgconf_dependency_free(&pkg->requires_private);
+ pkgconf_dependency_free(&pkg->conflicts);
+ pkgconf_dependency_free(&pkg->provides);
+
+ pkgconf_fragment_free(&pkg->cflags);
+ pkgconf_fragment_free(&pkg->cflags_private);
+ pkgconf_fragment_free(&pkg->libs);
+ pkgconf_fragment_free(&pkg->libs_private);
+
+ pkgconf_tuple_free(&pkg->vars);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_new_from_path(const pkgconf_client_t *client, const char *filename, unsigned int flags)
+ *
+ * Parse a .pc file into a pkgconf_pkg_t object structure.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param char* filename: The filename of the package file (including full path).
+ * :param FILE* f: The file object to read from.
+ * :param uint flags: The flags to use when parsing.
+ * :returns: A ``pkgconf_pkg_t`` object which contains the package data.
+ * :rtype: pkgconf_pkg_t *
+ */
+pkgconf_pkg_t *
+pkgconf_pkg_new_from_path(pkgconf_client_t *client, const char *filename, unsigned int flags)
+{
+ pkgconf_pkg_t *pkg;
+ char *idptr;
+ FILE *f;
+
+ /* make sure we only load .pc files */
+ if (!str_has_suffix(filename, PKG_CONFIG_EXT))
+ return NULL;
+
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return NULL;
+
+ pkg = calloc(1, sizeof(pkgconf_pkg_t));
+ if (pkg == NULL)
+ {
+ fclose(f);
+ return NULL;
+ }
+
+ pkg->owner = client;
+ pkg->flags = flags;
+
+ pkg->filename = strdup(filename);
+ if (pkg->filename == NULL)
+ {
+ fclose(f);
+ pkg_free_object(pkg);
+ return NULL;
+ }
+
+ pkg->pc_filedir = pkg_get_parent_dir(pkg);
+ if (pkg->pc_filedir == NULL)
+ {
+ fclose(f);
+ pkg_free_object(pkg);
+ return NULL;
+ }
+
+ char *pc_filedir_value = convert_path_to_value(pkg->pc_filedir);
+ pkgconf_tuple_add(client, &pkg->vars, "pcfiledir", pc_filedir_value, true, pkg->flags);
+ free(pc_filedir_value);
+
+ /* If pc_filedir is outside of sysroot_dir, override sysroot_dir for this
+ * package.
+ * See https://github.com/pkgconf/pkgconf/issues/213
+ */
+ if (client->sysroot_dir && strncmp(pkg->pc_filedir, client->sysroot_dir, strlen(client->sysroot_dir)))
+ pkgconf_tuple_add(client, &pkg->vars, "pc_sysrootdir", "", false, pkg->flags);
+
+ /* make module id */
+ if ((idptr = strrchr(pkg->filename, PKG_DIR_SEP_S)) != NULL)
+ idptr++;
+ else
+ idptr = pkg->filename;
+
+#ifdef _WIN32
+ /* On Windows, both \ and / are allowed in paths, so we have to chop both.
+ * strrchr() took us to the last \ in that case, so we just have to see if
+ * it is followed by a /. If so, lop it off.
+ */
+ char *mungeptr;
+ if ((mungeptr = strrchr(idptr, '/')) != NULL)
+ idptr = ++mungeptr;
+#endif
+
+ pkg->id = strdup(idptr);
+ if (pkg->id == NULL)
+ {
+ fclose(f);
+ pkg_free_lists(pkg);
+ pkg_free_object(pkg);
+ return NULL;
+ }
+
+ idptr = strrchr(pkg->id, '.');
+ if (idptr)
+ *idptr = '\0';
+
+ if (pkg->flags & PKGCONF_PKG_PROPF_UNINSTALLED)
+ {
+ idptr = strrchr(pkg->id, '-');
+ if (idptr)
+ *idptr = '\0';
+ }
+
+ pkgconf_parser_parse(f, pkg, pkg_parser_funcs, (pkgconf_parser_warn_func_t) pkg_warn_func, pkg->filename);
+ fclose(f);
+
+ if (!pkgconf_pkg_validate(client, pkg))
+ {
+ pkgconf_warn(client, "%s: warning: skipping invalid file\n", pkg->filename);
+ pkgconf_pkg_free(client, pkg);
+ return NULL;
+ }
+
+ pkgconf_dependency_t *dep = pkgconf_dependency_add(client, &pkg->provides, pkg->id, pkg->version, PKGCONF_CMP_EQUAL, 0);
+ pkgconf_dependency_unref(dep->owner, dep);
+
+ return pkgconf_pkg_ref(client, pkg);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+ *
+ * Releases all releases for a given ``pkgconf_pkg_t`` object.
+ *
+ * :param pkgconf_client_t* client: The client which owns the ``pkgconf_pkg_t`` object, `pkg`.
+ * :param pkgconf_pkg_t* pkg: The package to free.
+ * :return: nothing
+ */
+void
+pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+{
+ if (pkg == NULL)
+ return;
+
+ if (pkg->flags & PKGCONF_PKG_PROPF_STATIC && !(pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL))
+ return;
+
+ pkgconf_cache_remove(client, pkg);
+
+ pkg_free_lists(pkg);
+
+ if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
+ return;
+
+ pkg_free_object(pkg);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_ref(const pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+ *
+ * Adds an additional reference to the package object.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object which owns the package being referenced.
+ * :param pkgconf_pkg_t* pkg: The package object being referenced.
+ * :return: The package itself with an incremented reference count.
+ * :rtype: pkgconf_pkg_t *
+ */
+pkgconf_pkg_t *
+pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+{
+ if (pkg->owner != NULL && pkg->owner != client)
+ PKGCONF_TRACE(client, "WTF: client %p refers to package %p owned by other client %p", client, pkg, pkg->owner);
+
+ pkg->refcount++;
+ PKGCONF_TRACE(client, "%s refcount@%p: %d", pkg->id, pkg, pkg->refcount);
+
+ return pkg;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+ *
+ * Releases a reference on the package object. If the reference count is 0, then also free the package.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object which owns the package being dereferenced.
+ * :param pkgconf_pkg_t* pkg: The package object being dereferenced.
+ * :return: nothing
+ */
+void
+pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
+{
+ if (pkg == NULL) {
+ PKGCONF_TRACE(client, "WTF: client %p unrefs a NULL package", client);
+ return;
+ }
+
+ if (pkg->owner != NULL && pkg->owner != client)
+ PKGCONF_TRACE(client, "WTF: client %p unrefs package %p owned by other client %p", client, pkg, pkg->owner);
+
+ pkg->refcount--;
+ PKGCONF_TRACE(pkg->owner, "%s refcount@%p: %d", pkg->id, pkg, pkg->refcount);
+
+ if (pkg->refcount <= 0)
+ pkgconf_pkg_free(pkg->owner, pkg);
+}
+
+static inline pkgconf_pkg_t *
+pkgconf_pkg_try_specific_path(pkgconf_client_t *client, const char *path, const char *name)
+{
+ pkgconf_pkg_t *pkg = NULL;
+ char locbuf[PKGCONF_ITEM_SIZE];
+ char uninst_locbuf[PKGCONF_ITEM_SIZE];
+
+ PKGCONF_TRACE(client, "trying path: %s for %s", path, name);
+
+ snprintf(locbuf, sizeof locbuf, "%s%c%s" PKG_CONFIG_EXT, path, PKG_DIR_SEP_S, name);
+ snprintf(uninst_locbuf, sizeof uninst_locbuf, "%s%c%s-uninstalled" PKG_CONFIG_EXT, path, PKG_DIR_SEP_S, name);
+
+ if (!(client->flags & PKGCONF_PKG_PKGF_NO_UNINSTALLED))
+ pkg = pkgconf_pkg_new_from_path(client, uninst_locbuf, PKGCONF_PKG_PROPF_UNINSTALLED);
+
+ if (pkg == NULL)
+ pkg = pkgconf_pkg_new_from_path(client, locbuf, 0);
+
+ if (pkg != NULL)
+ PKGCONF_TRACE(client, "found%s: %s", pkg->flags & PKGCONF_PKG_PROPF_UNINSTALLED ? " (uninstalled)" : "", uninst_locbuf);
+
+ return pkg;
+}
+
+static pkgconf_pkg_t *
+pkgconf_pkg_scan_dir(pkgconf_client_t *client, const char *path, void *data, pkgconf_pkg_iteration_func_t func)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ pkgconf_pkg_t *outpkg = NULL;
+
+ dir = opendir(path);
+ if (dir == NULL)
+ return NULL;
+
+ PKGCONF_TRACE(client, "scanning dir [%s]", path);
+
+ for (dirent = readdir(dir); dirent != NULL; dirent = readdir(dir))
+ {
+ char filebuf[PKGCONF_ITEM_SIZE];
+ pkgconf_pkg_t *pkg;
+
+ pkgconf_strlcpy(filebuf, path, sizeof filebuf);
+ pkgconf_strlcat(filebuf, "/", sizeof filebuf);
+ pkgconf_strlcat(filebuf, dirent->d_name, sizeof filebuf);
+
+ if (!str_has_suffix(filebuf, PKG_CONFIG_EXT))
+ continue;
+
+ PKGCONF_TRACE(client, "trying file [%s]", filebuf);
+
+ pkg = pkgconf_pkg_new_from_path(client, filebuf, 0);
+ if (pkg != NULL)
+ {
+ if (func(pkg, data))
+ {
+ outpkg = pkg;
+ goto out;
+ }
+
+ pkgconf_pkg_unref(client, pkg);
+ }
+ }
+
+out:
+ closedir(dir);
+ return outpkg;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_pkg_t *pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func)
+ *
+ * Iterates over all packages found in the `package directory list`, running ``func`` on them. If ``func`` returns true,
+ * then stop iteration and return the last iterated package.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param void* data: An opaque pointer to data to provide the iteration function with.
+ * :param pkgconf_pkg_iteration_func_t func: A function which is called for each package to determine if the package matches,
+ * always return ``false`` to iterate over all packages.
+ * :return: A package object reference if one is found by the scan function, else ``NULL``.
+ * :rtype: pkgconf_pkg_t *
+ */
+pkgconf_pkg_t *
+pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func)
+{
+ pkgconf_node_t *n;
+ pkgconf_pkg_t *pkg;
+
+ PKGCONF_TRACE(client, "scanning preloaded list");
+ PKGCONF_FOREACH_LIST_ENTRY(client->preloaded_pkgs.head, n)
+ {
+ pkg = n->data;
+
+ /* add an additional reference to ensure preloaded packages have the same
+ * object ownership semantics as non-preloaded packages
+ */
+ pkgconf_pkg_ref(client, pkg);
+
+ if (func(pkg, data))
+ return pkg;
+
+ pkgconf_pkg_unref(client, pkg);
+ }
+
+ PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n)
+ {
+ pkgconf_path_t *pnode = n->data;
+
+ PKGCONF_TRACE(client, "scanning directory: %s", pnode->path);
+
+ if ((pkg = pkgconf_pkg_scan_dir(client, pnode->path, data, func)) != NULL)
+ return pkg;
+ }
+
+ return NULL;
+}
+
+static pkgconf_pkg_t *
+search_preload_list(pkgconf_client_t *client, const char *name)
+{
+ pkgconf_node_t *n;
+
+ PKGCONF_FOREACH_LIST_ENTRY(client->preloaded_pkgs.head, n)
+ {
+ pkgconf_pkg_t *pkg = n->data;
+
+ if (!strcmp(pkg->id, name))
+ {
+ pkgconf_pkg_ref(client, pkg);
+ return pkg;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
+ *
+ * Search for a package.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param char* name: The name of the package `atom` to use for searching.
+ * :return: A package object reference if the package was found, else ``NULL``.
+ * :rtype: pkgconf_pkg_t *
+ */
+pkgconf_pkg_t *
+pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
+{
+ pkgconf_pkg_t *pkg = NULL;
+ pkgconf_node_t *n;
+
+ PKGCONF_TRACE(client, "looking for: %s", name);
+
+ /* name might actually be a filename. */
+ if (str_has_suffix(name, PKG_CONFIG_EXT))
+ {
+ if (client->unveil_handler != NULL)
+ client->unveil_handler(client, name, "r");
+
+ pkg = pkgconf_pkg_new_from_path(client, name, 0);
+ if (pkg != NULL)
+ {
+ PKGCONF_TRACE(client, "%s is a file", name);
+
+ if (client->unveil_handler != NULL)
+ client->unveil_handler(client, pkg->pc_filedir, "r");
+
+ pkgconf_path_add(pkg->pc_filedir, &client->dir_list, true);
+ goto out;
+ }
+ }
+
+ /* check builtins */
+ if ((pkg = pkgconf_builtin_pkg_get(name)) != NULL)
+ {
+ PKGCONF_TRACE(client, "%s is a builtin", name);
+ return pkg;
+ }
+
+ /* check cache */
+ if (!(client->flags & PKGCONF_PKG_PKGF_NO_CACHE))
+ {
+ if ((pkg = pkgconf_cache_lookup(client, name)) != NULL)
+ {
+ PKGCONF_TRACE(client, "%s is cached", name);
+ return pkg;
+ }
+ }
+
+ /* check preload list */
+ if ((pkg = search_preload_list(client, name)) != NULL)
+ {
+ PKGCONF_TRACE(client, "%s is preloaded", name);
+ return pkg;
+ }
+
+ PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n)
+ {
+ pkgconf_path_t *pnode = n->data;
+
+ pkg = pkgconf_pkg_try_specific_path(client, pnode->path, name);
+ if (pkg != NULL)
+ goto out;
+ }
+
+out:
+ pkgconf_cache_add(client, pkg);
+
+ return pkg;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: int pkgconf_compare_version(const char *a, const char *b)
+ *
+ * Compare versions using RPM version comparison rules as described in the LSB.
+ *
+ * :param char* a: The first version to compare in the pair.
+ * :param char* b: The second version to compare in the pair.
+ * :return: -1 if the first version is less than, 0 if both versions are equal, 1 if the second version is less than.
+ * :rtype: int
+ */
+int
+pkgconf_compare_version(const char *a, const char *b)
+{
+ char oldch1, oldch2;
+ char buf1[PKGCONF_ITEM_SIZE], buf2[PKGCONF_ITEM_SIZE];
+ char *str1, *str2;
+ char *one, *two;
+ int ret;
+ bool isnum;
+
+ /* optimization: if version matches then it's the same version. */
+ if (a == NULL)
+ return -1;
+
+ if (b == NULL)
+ return 1;
+
+ if (!strcasecmp(a, b))
+ return 0;
+
+ pkgconf_strlcpy(buf1, a, sizeof buf1);
+ pkgconf_strlcpy(buf2, b, sizeof buf2);
+
+ one = buf1;
+ two = buf2;
+
+ while (*one || *two)
+ {
+ while (*one && !isalnum((unsigned char)*one) && *one != '~')
+ one++;
+ while (*two && !isalnum((unsigned char)*two) && *two != '~')
+ two++;
+
+ if (*one == '~' || *two == '~')
+ {
+ if (*one != '~')
+ return 1;
+ if (*two != '~')
+ return -1;
+
+ one++;
+ two++;
+ continue;
+ }
+
+ if (!(*one && *two))
+ break;
+
+ str1 = one;
+ str2 = two;
+
+ if (isdigit((unsigned char)*str1))
+ {
+ while (*str1 && isdigit((unsigned char)*str1))
+ str1++;
+
+ while (*str2 && isdigit((unsigned char)*str2))
+ str2++;
+
+ isnum = true;
+ }
+ else
+ {
+ while (*str1 && isalpha((unsigned char)*str1))
+ str1++;
+
+ while (*str2 && isalpha((unsigned char)*str2))
+ str2++;
+
+ isnum = false;
+ }
+
+ oldch1 = *str1;
+ oldch2 = *str2;
+
+ *str1 = '\0';
+ *str2 = '\0';
+
+ if (one == str1)
+ return -1;
+
+ if (two == str2)
+ return (isnum ? 1 : -1);
+
+ if (isnum)
+ {
+ int onelen, twolen;
+
+ while (*one == '0')
+ one++;
+
+ while (*two == '0')
+ two++;
+
+ onelen = strlen(one);
+ twolen = strlen(two);
+
+ if (onelen > twolen)
+ return 1;
+ else if (twolen > onelen)
+ return -1;
+ }
+
+ ret = strcmp(one, two);
+ if (ret != 0)
+ return ret < 0 ? -1 : 1;
+
+ *str1 = oldch1;
+ *str2 = oldch2;
+
+ one = str1;
+ two = str2;
+ }
+
+ if ((!*one) && (!*two))
+ return 0;
+
+ if (!*one)
+ return -1;
+
+ return 1;
+}
+
+static pkgconf_pkg_t pkg_config_virtual = {
+ .id = "pkg-config",
+ .realname = "pkg-config",
+ .description = "virtual package defining pkg-config API version supported",
+ .url = PACKAGE_BUGREPORT,
+ .version = PACKAGE_VERSION,
+ .flags = PKGCONF_PKG_PROPF_STATIC,
+ .vars = {
+ .head = &(pkgconf_node_t){
+ .next = &(pkgconf_node_t){
+ .next = &(pkgconf_node_t){
+ .data = &(pkgconf_tuple_t){
+ .key = "pc_system_libdirs",
+ .value = SYSTEM_LIBDIR,
+ }
+ },
+ .data = &(pkgconf_tuple_t){
+ .key = "pc_system_includedirs",
+ .value = SYSTEM_INCLUDEDIR,
+ }
+ },
+ .data = &(pkgconf_tuple_t){
+ .key = "pc_path",
+ .value = PKG_DEFAULT_PATH,
+ },
+ },
+ .tail = NULL,
+ }
+};
+
+static pkgconf_pkg_t pkgconf_virtual = {
+ .id = "pkgconf",
+ .realname = "pkgconf",
+ .description = "virtual package defining pkgconf API version supported",
+ .url = PACKAGE_BUGREPORT,
+ .version = PACKAGE_VERSION,
+ .license = "ISC",
+ .flags = PKGCONF_PKG_PROPF_STATIC,
+ .vars = {
+ .head = &(pkgconf_node_t){
+ .next = &(pkgconf_node_t){
+ .next = &(pkgconf_node_t){
+ .data = &(pkgconf_tuple_t){
+ .key = "pc_system_libdirs",
+ .value = SYSTEM_LIBDIR,
+ }
+ },
+ .data = &(pkgconf_tuple_t){
+ .key = "pc_system_includedirs",
+ .value = SYSTEM_INCLUDEDIR,
+ }
+ },
+ .data = &(pkgconf_tuple_t){
+ .key = "pc_path",
+ .value = PKG_DEFAULT_PATH,
+ },
+ },
+ .tail = NULL,
+ },
+};
+
+typedef struct {
+ const char *name;
+ pkgconf_pkg_t *pkg;
+} pkgconf_builtin_pkg_pair_t;
+
+/* keep these in alphabetical order */
+static const pkgconf_builtin_pkg_pair_t pkgconf_builtin_pkg_pair_set[] = {
+ {"pkg-config", &pkg_config_virtual},
+ {"pkgconf", &pkgconf_virtual},
+};
+
+static int pkgconf_builtin_pkg_pair_cmp(const void *key, const void *ptr)
+{
+ const pkgconf_builtin_pkg_pair_t *pair = ptr;
+ return strcasecmp(key, pair->name);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_pkg_t *pkgconf_builtin_pkg_get(const char *name)
+ *
+ * Looks up a built-in package. The package should not be freed or dereferenced.
+ *
+ * :param char* name: An atom corresponding to a built-in package to search for.
+ * :return: the built-in package if present, else ``NULL``.
+ * :rtype: pkgconf_pkg_t *
+ */
+pkgconf_pkg_t *
+pkgconf_builtin_pkg_get(const char *name)
+{
+ const pkgconf_builtin_pkg_pair_t *pair = bsearch(name, pkgconf_builtin_pkg_pair_set,
+ PKGCONF_ARRAY_SIZE(pkgconf_builtin_pkg_pair_set), sizeof(pkgconf_builtin_pkg_pair_t),
+ pkgconf_builtin_pkg_pair_cmp);
+
+ return (pair != NULL) ? pair->pkg : NULL;
+}
+
+typedef bool (*pkgconf_vercmp_res_func_t)(const char *a, const char *b);
+
+typedef struct {
+ const char *name;
+ pkgconf_pkg_comparator_t compare;
+} pkgconf_pkg_comparator_pair_t;
+
+static const pkgconf_pkg_comparator_pair_t pkgconf_pkg_comparator_names[] = {
+ {"!=", PKGCONF_CMP_NOT_EQUAL},
+ {"(any)", PKGCONF_CMP_ANY},
+ {"<", PKGCONF_CMP_LESS_THAN},
+ {"<=", PKGCONF_CMP_LESS_THAN_EQUAL},
+ {"=", PKGCONF_CMP_EQUAL},
+ {">", PKGCONF_CMP_GREATER_THAN},
+ {">=", PKGCONF_CMP_GREATER_THAN_EQUAL},
+};
+
+static int pkgconf_pkg_comparator_pair_namecmp(const void *key, const void *ptr)
+{
+ const pkgconf_pkg_comparator_pair_t *pair = ptr;
+ return strcmp(key, pair->name);
+}
+
+static bool pkgconf_pkg_comparator_lt(const char *a, const char *b)
+{
+ return (pkgconf_compare_version(a, b) < 0);
+}
+
+static bool pkgconf_pkg_comparator_gt(const char *a, const char *b)
+{
+ return (pkgconf_compare_version(a, b) > 0);
+}
+
+static bool pkgconf_pkg_comparator_lte(const char *a, const char *b)
+{
+ return (pkgconf_compare_version(a, b) <= 0);
+}
+
+static bool pkgconf_pkg_comparator_gte(const char *a, const char *b)
+{
+ return (pkgconf_compare_version(a, b) >= 0);
+}
+
+static bool pkgconf_pkg_comparator_eq(const char *a, const char *b)
+{
+ return (pkgconf_compare_version(a, b) == 0);
+}
+
+static bool pkgconf_pkg_comparator_ne(const char *a, const char *b)
+{
+ return (pkgconf_compare_version(a, b) != 0);
+}
+
+static bool pkgconf_pkg_comparator_any(const char *a, const char *b)
+{
+ (void) a;
+ (void) b;
+
+ return true;
+}
+
+static bool pkgconf_pkg_comparator_none(const char *a, const char *b)
+{
+ (void) a;
+ (void) b;
+
+ return false;
+}
+
+static const pkgconf_vercmp_res_func_t pkgconf_pkg_comparator_impls[] = {
+ [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_any,
+ [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt,
+ [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt,
+ [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte,
+ [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte,
+ [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_eq,
+ [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_ne,
+};
+
+/*
+ * !doc
+ *
+ * .. c:function:: const char *pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep)
+ *
+ * Returns the comparator used in a depgraph dependency node as a string.
+ *
+ * :param pkgconf_dependency_t* pkgdep: The depgraph dependency node to return the comparator for.
+ * :return: A string matching the comparator or ``"???"``.
+ * :rtype: char *
+ */
+const char *
+pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep)
+{
+ if (pkgdep->compare >= PKGCONF_ARRAY_SIZE(pkgconf_pkg_comparator_names))
+ return "???";
+
+ return pkgconf_pkg_comparator_names[pkgdep->compare].name;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_pkg_comparator_t pkgconf_pkg_comparator_lookup_by_name(const char *name)
+ *
+ * Look up the appropriate comparator bytecode in the comparator set (defined
+ * in ``pkg.c``, see ``pkgconf_pkg_comparator_names`` and ``pkgconf_pkg_comparator_impls``).
+ *
+ * :param char* name: The comparator to look up by `name`.
+ * :return: The comparator bytecode if found, else ``PKGCONF_CMP_ANY``.
+ * :rtype: pkgconf_pkg_comparator_t
+ */
+pkgconf_pkg_comparator_t
+pkgconf_pkg_comparator_lookup_by_name(const char *name)
+{
+ const pkgconf_pkg_comparator_pair_t *p = bsearch(name, pkgconf_pkg_comparator_names,
+ PKGCONF_ARRAY_SIZE(pkgconf_pkg_comparator_names), sizeof(pkgconf_pkg_comparator_pair_t),
+ pkgconf_pkg_comparator_pair_namecmp);
+
+ return (p != NULL) ? p->compare : PKGCONF_CMP_ANY;
+}
+
+typedef struct {
+ pkgconf_dependency_t *pkgdep;
+} pkgconf_pkg_scan_providers_ctx_t;
+
+typedef struct {
+ const pkgconf_vercmp_res_func_t rulecmp[PKGCONF_CMP_COUNT];
+ const pkgconf_vercmp_res_func_t depcmp[PKGCONF_CMP_COUNT];
+} pkgconf_pkg_provides_vermatch_rule_t;
+
+static const pkgconf_pkg_provides_vermatch_rule_t pkgconf_pkg_provides_vermatch_rules[] = {
+ [PKGCONF_CMP_ANY] = {
+ .rulecmp = {
+ [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
+ },
+ .depcmp = {
+ [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
+ },
+ },
+ [PKGCONF_CMP_LESS_THAN] = {
+ .rulecmp = {
+ [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
+ [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt,
+ [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt,
+ [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte,
+ [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte,
+ },
+ .depcmp = {
+ [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_lt,
+ [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_lt,
+ [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_lt,
+ [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_gte,
+ },
+ },
+ [PKGCONF_CMP_GREATER_THAN] = {
+ .rulecmp = {
+ [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
+ [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt,
+ [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt,
+ [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte,
+ [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte,
+ },
+ .depcmp = {
+ [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_gt,
+ [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_gt,
+ [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_gt,
+ [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_lte,
+ },
+ },
+ [PKGCONF_CMP_LESS_THAN_EQUAL] = {
+ .rulecmp = {
+ [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
+ [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt,
+ [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt,
+ [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte,
+ [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte,
+ },
+ .depcmp = {
+ [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_lte,
+ [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_lte,
+ [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_lte,
+ [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_gt,
+ },
+ },
+ [PKGCONF_CMP_GREATER_THAN_EQUAL] = {
+ .rulecmp = {
+ [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
+ [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt,
+ [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt,
+ [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte,
+ [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte,
+ },
+ .depcmp = {
+ [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_gte,
+ [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_gte,
+ [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_gte,
+ [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_lt,
+ },
+ },
+ [PKGCONF_CMP_EQUAL] = {
+ .rulecmp = {
+ [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
+ [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt,
+ [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt,
+ [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte,
+ [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte,
+ [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_eq,
+ [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_ne
+ },
+ .depcmp = {
+ [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
+ },
+ },
+ [PKGCONF_CMP_NOT_EQUAL] = {
+ .rulecmp = {
+ [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
+ [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_gte,
+ [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_lte,
+ [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_gt,
+ [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_lt,
+ [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_ne,
+ [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_eq
+ },
+ .depcmp = {
+ [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
+ },
+ },
+};
+
+/*
+ * pkgconf_pkg_scan_provides_vercmp(pkgdep, provider)
+ *
+ * compare a provides node against the requested dependency node.
+ *
+ * XXX: maybe handle PKGCONF_CMP_ANY in a versioned comparison
+ */
+static bool
+pkgconf_pkg_scan_provides_vercmp(const pkgconf_dependency_t *pkgdep, const pkgconf_dependency_t *provider)
+{
+ const pkgconf_pkg_provides_vermatch_rule_t *rule = &pkgconf_pkg_provides_vermatch_rules[pkgdep->compare];
+
+ if (rule->depcmp[provider->compare] != NULL &&
+ !rule->depcmp[provider->compare](provider->version, pkgdep->version))
+ return false;
+
+ if (rule->rulecmp[provider->compare] != NULL &&
+ !rule->rulecmp[provider->compare](pkgdep->version, provider->version))
+ return false;
+
+ return true;
+}
+
+/*
+ * pkgconf_pkg_scan_provides_entry(pkg, ctx)
+ *
+ * attempt to match a single package's Provides rules against the requested dependency node.
+ */
+static bool
+pkgconf_pkg_scan_provides_entry(const pkgconf_pkg_t *pkg, const pkgconf_pkg_scan_providers_ctx_t *ctx)
+{
+ const pkgconf_dependency_t *pkgdep = ctx->pkgdep;
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->provides.head, node)
+ {
+ const pkgconf_dependency_t *provider = node->data;
+ if (!strcmp(provider->package, pkgdep->package))
+ return pkgconf_pkg_scan_provides_vercmp(pkgdep, provider);
+ }
+
+ return false;
+}
+
+/*
+ * pkgconf_pkg_scan_providers(client, pkgdep, eflags)
+ *
+ * scan all available packages to see if a Provides rule matches the pkgdep.
+ */
+static pkgconf_pkg_t *
+pkgconf_pkg_scan_providers(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags)
+{
+ pkgconf_pkg_t *pkg;
+ pkgconf_pkg_scan_providers_ctx_t ctx = {
+ .pkgdep = pkgdep,
+ };
+
+ pkg = pkgconf_scan_all(client, &ctx, (pkgconf_pkg_iteration_func_t) pkgconf_pkg_scan_provides_entry);
+ if (pkg != NULL)
+ {
+ pkgdep->match = pkgconf_pkg_ref(client, pkg);
+ return pkg;
+ }
+
+ if (eflags != NULL)
+ *eflags |= PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND;
+
+ return NULL;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags)
+ *
+ * Verify a pkgconf_dependency_t node in the depgraph. If the dependency is solvable,
+ * return the appropriate ``pkgconf_pkg_t`` object, else ``NULL``.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param pkgconf_dependency_t* pkgdep: The dependency graph node to solve.
+ * :param uint* eflags: An optional pointer that, if set, will be populated with an error code from the resolver.
+ * :return: On success, the appropriate ``pkgconf_pkg_t`` object to solve the dependency, else ``NULL``.
+ * :rtype: pkgconf_pkg_t *
+ */
+pkgconf_pkg_t *
+pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags)
+{
+ pkgconf_pkg_t *pkg = NULL;
+
+ if (eflags != NULL)
+ *eflags = PKGCONF_PKG_ERRF_OK;
+
+ PKGCONF_TRACE(client, "trying to verify dependency: %s", pkgdep->package);
+
+ if (pkgdep->match != NULL)
+ {
+ PKGCONF_TRACE(client, "cached dependency: %s -> %s@%p", pkgdep->package, pkgdep->match->id, pkgdep->match);
+ return pkgconf_pkg_ref(client, pkgdep->match);
+ }
+
+ pkg = pkgconf_pkg_find(client, pkgdep->package);
+ if (pkg == NULL)
+ {
+ if (client->flags & PKGCONF_PKG_PKGF_SKIP_PROVIDES)
+ {
+ if (eflags != NULL)
+ *eflags |= PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND;
+
+ return NULL;
+ }
+
+ pkg = pkgconf_pkg_scan_providers(client, pkgdep, eflags);
+ }
+ else
+ {
+ if (pkg->id == NULL)
+ pkg->id = strdup(pkgdep->package);
+
+ if (pkgconf_pkg_comparator_impls[pkgdep->compare](pkg->version, pkgdep->version) != true)
+ {
+ if (eflags != NULL)
+ *eflags |= PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH;
+ }
+ else
+ pkgdep->match = pkgconf_pkg_ref(client, pkg);
+ }
+
+ if (pkg != NULL && pkg->why == NULL)
+ pkg->why = strdup(pkgdep->package);
+
+ return pkg;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: unsigned int pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth)
+ *
+ * Verify the graph dependency nodes are satisfiable by walking the tree using
+ * ``pkgconf_pkg_traverse()``.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param pkgconf_pkg_t* root: The root entry in the package dependency graph which should contain the top-level dependencies to resolve.
+ * :param int depth: The maximum allowed depth for dependency resolution.
+ * :return: On success, ``PKGCONF_PKG_ERRF_OK`` (0), else an error code.
+ * :rtype: unsigned int
+ */
+unsigned int
+pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth)
+{
+ return pkgconf_pkg_traverse(client, root, NULL, NULL, depth, 0);
+}
+
+static unsigned int
+pkgconf_pkg_report_graph_error(pkgconf_client_t *client, pkgconf_pkg_t *parent, pkgconf_pkg_t *pkg, pkgconf_dependency_t *node, unsigned int eflags)
+{
+ if (eflags & PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND)
+ {
+ if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS) & !client->already_sent_notice)
+ {
+ pkgconf_error(client, "Package %s was not found in the pkg-config search path.\n", node->package);
+ pkgconf_error(client, "Perhaps you should add the directory containing `%s.pc'\n", node->package);
+ pkgconf_error(client, "to the PKG_CONFIG_PATH environment variable\n");
+ client->already_sent_notice = true;
+ }
+
+ if (parent->flags & PKGCONF_PKG_PROPF_VIRTUAL)
+ pkgconf_error(client, "Package '%s' not found\n", node->package);
+ else
+ pkgconf_error(client, "Package '%s', required by '%s', not found\n", node->package, parent->id);
+
+ pkgconf_audit_log(client, "%s NOT-FOUND\n", node->package);
+ }
+ else if (eflags & PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH)
+ {
+ pkgconf_error(client, "Package dependency requirement '%s %s %s' could not be satisfied.\n",
+ node->package, pkgconf_pkg_get_comparator(node), node->version);
+
+ if (pkg != NULL)
+ pkgconf_error(client, "Package '%s' has version '%s', required version is '%s %s'\n",
+ node->package, pkg->version, pkgconf_pkg_get_comparator(node), node->version);
+ }
+
+ if (pkg != NULL)
+ pkgconf_pkg_unref(client, pkg);
+
+ return eflags;
+}
+
+static inline unsigned int
+pkgconf_pkg_walk_list(pkgconf_client_t *client,
+ pkgconf_pkg_t *parent,
+ pkgconf_list_t *deplist,
+ pkgconf_pkg_traverse_func_t func,
+ void *data,
+ int depth,
+ unsigned int skip_flags)
+{
+ unsigned int eflags = PKGCONF_PKG_ERRF_OK;
+ pkgconf_node_t *node, *next;
+
+ parent->flags |= PKGCONF_PKG_PROPF_ANCESTOR;
+
+ PKGCONF_FOREACH_LIST_ENTRY_SAFE(deplist->head, next, node)
+ {
+ unsigned int eflags_local = PKGCONF_PKG_ERRF_OK;
+ pkgconf_dependency_t *depnode = node->data;
+ pkgconf_pkg_t *pkgdep;
+
+ if (*depnode->package == '\0')
+ continue;
+
+ pkgdep = pkgconf_pkg_verify_dependency(client, depnode, &eflags_local);
+
+ eflags |= eflags_local;
+ if (eflags_local != PKGCONF_PKG_ERRF_OK && !(client->flags & PKGCONF_PKG_PKGF_SKIP_ERRORS))
+ {
+ pkgconf_pkg_report_graph_error(client, parent, pkgdep, depnode, eflags_local);
+ continue;
+ }
+ if (pkgdep == NULL)
+ continue;
+
+ if((pkgdep->flags & PKGCONF_PKG_PROPF_ANCESTOR) != 0)
+ {
+ /* In this case we have a circular reference.
+ * We break that by deleteing the circular node from the
+ * the list, so that we dont create a situation where
+ * memory is leaked due to circular ownership.
+ * i.e: A owns B owns A
+ *
+ * TODO(ariadne): Breaking circular references between Requires and Requires.private
+ * lists causes problems. Find a way to refactor the Requires.private list out.
+ */
+ if (!(depnode->flags & PKGCONF_PKG_DEPF_PRIVATE) &&
+ !(parent->flags & PKGCONF_PKG_PROPF_VIRTUAL))
+ {
+ pkgconf_warn(client, "%s: breaking circular reference (%s -> %s -> %s)\n",
+ parent->id, parent->id, pkgdep->id, parent->id);
+
+ pkgconf_node_delete(node, deplist);
+ pkgconf_dependency_unref(client, depnode);
+ }
+
+ goto next;
+ }
+
+ if (skip_flags && (depnode->flags & skip_flags) == skip_flags)
+ goto next;
+
+ pkgconf_audit_log_dependency(client, pkgdep, depnode);
+
+ eflags |= pkgconf_pkg_traverse_main(client, pkgdep, func, data, depth - 1, skip_flags);
+next:
+ pkgconf_pkg_unref(client, pkgdep);
+ }
+
+ parent->flags &= ~PKGCONF_PKG_PROPF_ANCESTOR;
+
+ return eflags;
+}
+
+static inline unsigned int
+pkgconf_pkg_walk_conflicts_list(pkgconf_client_t *client,
+ pkgconf_pkg_t *root, pkgconf_list_t *deplist)
+{
+ unsigned int eflags;
+ pkgconf_node_t *node, *childnode;
+
+ PKGCONF_FOREACH_LIST_ENTRY(deplist->head, node)
+ {
+ pkgconf_dependency_t *parentnode = node->data;
+
+ if (*parentnode->package == '\0')
+ continue;
+
+ PKGCONF_FOREACH_LIST_ENTRY(root->required.head, childnode)
+ {
+ pkgconf_pkg_t *pkgdep;
+ pkgconf_dependency_t *depnode = childnode->data;
+
+ if (*depnode->package == '\0' || strcmp(depnode->package, parentnode->package))
+ continue;
+
+ pkgdep = pkgconf_pkg_verify_dependency(client, parentnode, &eflags);
+ if (eflags == PKGCONF_PKG_ERRF_OK)
+ {
+ pkgconf_error(client, "Version '%s' of '%s' conflicts with '%s' due to satisfying conflict rule '%s %s%s%s'.\n",
+ pkgdep->version, pkgdep->realname, root->realname, parentnode->package, pkgconf_pkg_get_comparator(parentnode),
+ parentnode->version != NULL ? " " : "", parentnode->version != NULL ? parentnode->version : "");
+
+ if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS))
+ {
+ pkgconf_error(client, "It may be possible to ignore this conflict and continue, try the\n");
+ pkgconf_error(client, "PKG_CONFIG_IGNORE_CONFLICTS environment variable.\n");
+ }
+
+ pkgconf_pkg_unref(client, pkgdep);
+
+ return PKGCONF_PKG_ERRF_PACKAGE_CONFLICT;
+ }
+
+ pkgconf_pkg_unref(client, pkgdep);
+ }
+ }
+
+ return PKGCONF_PKG_ERRF_OK;
+}
+
+/*
+ * !doc
+ *
+ * .. 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)
+ *
+ * Walk and resolve the dependency graph up to `maxdepth` levels.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param pkgconf_pkg_t* root: The root of the dependency graph.
+ * :param pkgconf_pkg_traverse_func_t func: A traversal function to call for each resolved node in the dependency graph.
+ * :param void* data: An opaque pointer to data to be passed to the traversal function.
+ * :param int maxdepth: The maximum depth to walk the dependency graph for. -1 means infinite recursion.
+ * :param uint skip_flags: Skip over dependency nodes containing the specified flags. A setting of 0 skips no dependency nodes.
+ * :return: ``PKGCONF_PKG_ERRF_OK`` on success, else an error code.
+ * :rtype: unsigned int
+ */
+static unsigned int
+pkgconf_pkg_traverse_main(pkgconf_client_t *client,
+ pkgconf_pkg_t *root,
+ pkgconf_pkg_traverse_func_t func,
+ void *data,
+ int maxdepth,
+ unsigned int skip_flags)
+{
+ unsigned int eflags = PKGCONF_PKG_ERRF_OK;
+
+ if (maxdepth == 0)
+ return eflags;
+
+ /* Short-circuit if we have already visited this node.
+ */
+ if (root->serial == client->serial)
+ return eflags;
+
+ root->serial = client->serial;
+
+ if (root->identifier == 0)
+ root->identifier = ++client->identifier;
+
+ PKGCONF_TRACE(client, "%s: level %d, serial %"PRIu64, root->id, maxdepth, client->serial);
+
+ 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)
+ {
+ if (func != NULL)
+ func(client, root, data);
+ }
+
+ if (!(client->flags & PKGCONF_PKG_PKGF_SKIP_CONFLICTS) && root->conflicts.head != NULL)
+ {
+ PKGCONF_TRACE(client, "%s: walking 'Conflicts' list", root->id);
+
+ eflags = pkgconf_pkg_walk_conflicts_list(client, root, &root->conflicts);
+ if (eflags != PKGCONF_PKG_ERRF_OK)
+ return eflags;
+ }
+
+ PKGCONF_TRACE(client, "%s: walking 'Requires' list", root->id);
+ eflags = pkgconf_pkg_walk_list(client, root, &root->required, func, data, maxdepth, skip_flags);
+ if (eflags != PKGCONF_PKG_ERRF_OK)
+ return eflags;
+
+ PKGCONF_TRACE(client, "%s: walking 'Requires.private' list", root->id);
+
+ /* XXX: ugly */
+ client->flags |= PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE;
+ eflags = pkgconf_pkg_walk_list(client, root, &root->requires_private, func, data, maxdepth, skip_flags);
+ client->flags &= ~PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE;
+
+ if (eflags != PKGCONF_PKG_ERRF_OK)
+ return eflags;
+
+ return eflags;
+}
+
+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)
+{
+ if (root->flags & PKGCONF_PKG_PROPF_VIRTUAL)
+ client->serial++;
+
+ if ((client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) == 0)
+ skip_flags |= PKGCONF_PKG_DEPF_PRIVATE;
+
+ return pkgconf_pkg_traverse_main(client, root, func, data, maxdepth, skip_flags);
+}
+
+static void
+pkgconf_pkg_cflags_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
+{
+ pkgconf_list_t *list = data;
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->cflags.head, node)
+ {
+ pkgconf_fragment_t *frag = node->data;
+ pkgconf_fragment_copy(client, list, frag, false);
+ }
+}
+
+static void
+pkgconf_pkg_cflags_private_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
+{
+ pkgconf_list_t *list = data;
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->cflags_private.head, node)
+ {
+ pkgconf_fragment_t *frag = node->data;
+ pkgconf_fragment_copy(client, list, frag, true);
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: int pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
+ *
+ * Walks a dependency graph and extracts relevant ``CFLAGS`` fragments.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param pkgconf_pkg_t* root: The root of the dependency graph.
+ * :param pkgconf_list_t* list: The fragment list to add the extracted ``CFLAGS`` fragments to.
+ * :param int maxdepth: The maximum allowed depth for dependency resolution. -1 means infinite recursion.
+ * :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code.
+ * :rtype: unsigned int
+ */
+unsigned int
+pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
+{
+ unsigned int eflag;
+ unsigned int skip_flags = (client->flags & PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS) == 0 ? PKGCONF_PKG_DEPF_INTERNAL : 0;
+ pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
+
+ eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_collect, &frags, maxdepth, skip_flags);
+
+ if (eflag == PKGCONF_PKG_ERRF_OK && client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS)
+ eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_private_collect, &frags, maxdepth, skip_flags);
+
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ {
+ pkgconf_fragment_free(&frags);
+ return eflag;
+ }
+
+ pkgconf_fragment_copy_list(client, list, &frags);
+ pkgconf_fragment_free(&frags);
+
+ return eflag;
+}
+
+static void
+pkgconf_pkg_libs_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
+{
+ pkgconf_list_t *list = data;
+ pkgconf_node_t *node;
+
+ if (!(client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) && pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE)
+ return;
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->libs.head, node)
+ {
+ pkgconf_fragment_t *frag = node->data;
+ pkgconf_fragment_copy(client, list, frag, (client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) != 0);
+ }
+
+ if (client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS)
+ {
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->libs_private.head, node)
+ {
+ pkgconf_fragment_t *frag = node->data;
+ pkgconf_fragment_copy(client, list, frag, true);
+ }
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: int pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
+ *
+ * Walks a dependency graph and extracts relevant ``LIBS`` fragments.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param pkgconf_pkg_t* root: The root of the dependency graph.
+ * :param pkgconf_list_t* list: The fragment list to add the extracted ``LIBS`` fragments to.
+ * :param int maxdepth: The maximum allowed depth for dependency resolution. -1 means infinite recursion.
+ * :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code.
+ * :rtype: unsigned int
+ */
+unsigned int
+pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
+{
+ unsigned int eflag;
+
+ eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_libs_collect, list, maxdepth, 0);
+
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ {
+ pkgconf_fragment_free(list);
+ return eflag;
+ }
+
+ return eflag;
+}
diff --git a/libpkgconf/queue.c b/libpkgconf/queue.c
new file mode 100644
index 000000000000..09c02e368f85
--- /dev/null
+++ b/libpkgconf/queue.c
@@ -0,0 +1,408 @@
+/*
+ * queue.c
+ * compilation of a list of packages into a world dependency set
+ *
+ * Copyright (c) 2012 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !doc
+ *
+ * libpkgconf `queue` module
+ * =========================
+ *
+ * The `queue` module provides an interface that allows easily building a dependency graph from an
+ * arbitrary set of dependencies. It also provides support for doing "preflight" checks on the entire
+ * dependency graph prior to working with it.
+ *
+ * Using the `queue` module functions is the recommended way of working with dependency graphs.
+ */
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_queue_push(pkgconf_list_t *list, const char *package)
+ *
+ * Pushes a requested dependency onto the dependency resolver's queue.
+ *
+ * :param pkgconf_list_t* list: the dependency resolution queue to add the package request to.
+ * :param char* package: the dependency atom requested
+ * :return: nothing
+ */
+void
+pkgconf_queue_push(pkgconf_list_t *list, const char *package)
+{
+ pkgconf_queue_t *pkgq = calloc(1, sizeof(pkgconf_queue_t));
+
+ pkgq->package = strdup(package);
+ pkgconf_node_insert_tail(&pkgq->iter, pkgq, list);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list)
+ *
+ * Compile a dependency resolution queue into a dependency resolution problem if possible, otherwise report an error.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param pkgconf_pkg_t* world: The designated root of the dependency graph.
+ * :param pkgconf_list_t* list: The list of dependency requests to consider.
+ * :return: true if the built dependency resolution problem is consistent, else false
+ * :rtype: bool
+ */
+bool
+pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list)
+{
+ pkgconf_node_t *iter;
+
+ PKGCONF_FOREACH_LIST_ENTRY(list->head, iter)
+ {
+ pkgconf_queue_t *pkgq;
+
+ pkgq = iter->data;
+ pkgconf_dependency_parse(client, world, &world->required, pkgq->package, PKGCONF_PKG_DEPF_QUERY);
+ }
+
+ return (world->required.head != NULL);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_queue_free(pkgconf_list_t *list)
+ *
+ * Release any memory related to a dependency resolution queue.
+ *
+ * :param pkgconf_list_t* list: The dependency resolution queue to release.
+ * :return: nothing
+ */
+void
+pkgconf_queue_free(pkgconf_list_t *list)
+{
+ pkgconf_node_t *node, *tnode;
+
+ PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, tnode, node)
+ {
+ pkgconf_queue_t *pkgq = node->data;
+
+ free(pkgq->package);
+ free(pkgq);
+ }
+}
+
+static void
+pkgconf_queue_mark_public(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
+{
+ if (pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE)
+ {
+ pkgconf_list_t *list = data;
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+ if (dep->match == pkg)
+ dep->flags &= ~PKGCONF_PKG_DEPF_PRIVATE;
+ }
+
+ pkg->flags &= ~PKGCONF_PKG_PROPF_VISITED_PRIVATE;
+
+ PKGCONF_TRACE(client, "%s: updated, public", pkg->id);
+ }
+}
+
+static unsigned int
+pkgconf_queue_collect_dependencies_main(pkgconf_client_t *client,
+ pkgconf_pkg_t *root,
+ void *data,
+ int maxdepth);
+
+static inline unsigned int
+pkgconf_queue_collect_dependencies_walk(pkgconf_client_t *client,
+ pkgconf_list_t *deplist,
+ void *data,
+ int depth)
+{
+ unsigned int eflags = PKGCONF_PKG_ERRF_OK;
+ pkgconf_node_t *node;
+ pkgconf_pkg_t *world = data;
+
+ PKGCONF_FOREACH_LIST_ENTRY_REVERSE(deplist->tail, node)
+ {
+ pkgconf_dependency_t *dep = node->data;
+ pkgconf_dependency_t *flattened_dep;
+ pkgconf_pkg_t *pkg = dep->match;
+
+ if (*dep->package == '\0')
+ continue;
+
+ if (pkg == NULL)
+ {
+ PKGCONF_TRACE(client, "WTF: unmatched dependency %p <%s>", dep, dep->package);
+ continue;
+ }
+
+ if (pkg->serial == client->serial)
+ continue;
+
+ if (client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE)
+ pkg->flags |= PKGCONF_PKG_PROPF_VISITED_PRIVATE;
+ else
+ pkg->flags &= ~PKGCONF_PKG_PROPF_VISITED_PRIVATE;
+
+ eflags |= pkgconf_queue_collect_dependencies_main(client, pkg, data, depth - 1);
+
+ flattened_dep = pkgconf_dependency_copy(client, dep);
+ pkgconf_node_insert(&flattened_dep->iter, flattened_dep, &world->required);
+ }
+
+ return eflags;
+}
+
+static unsigned int
+pkgconf_queue_collect_dependencies_main(pkgconf_client_t *client,
+ pkgconf_pkg_t *root,
+ void *data,
+ int maxdepth)
+{
+ unsigned int eflags = PKGCONF_PKG_ERRF_OK;
+
+ if (maxdepth == 0)
+ return eflags;
+
+ /* Short-circuit if we have already visited this node.
+ */
+ if (root->serial == client->serial)
+ return eflags;
+
+ root->serial = client->serial;
+
+ PKGCONF_TRACE(client, "%s: collecting private dependencies, level %d", root->id, maxdepth);
+
+ /* XXX: ugly */
+ const unsigned int saved_flags = client->flags;
+ client->flags |= PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE;
+ eflags = pkgconf_queue_collect_dependencies_walk(client, &root->requires_private, data, maxdepth);
+ client->flags = saved_flags;
+ if (eflags != PKGCONF_PKG_ERRF_OK)
+ return eflags;
+
+ PKGCONF_TRACE(client, "%s: collecting public dependencies, level %d", root->id, maxdepth);
+
+ eflags = pkgconf_queue_collect_dependencies_walk(client, &root->required, data, maxdepth);
+ if (eflags != PKGCONF_PKG_ERRF_OK)
+ return eflags;
+
+ PKGCONF_TRACE(client, "%s: finished, %s", root->id, (root->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE) ? "private" : "public");
+
+ return eflags;
+}
+
+static inline unsigned int
+pkgconf_queue_collect_dependencies(pkgconf_client_t *client,
+ pkgconf_pkg_t *root,
+ void *data,
+ int maxdepth)
+{
+ ++client->serial;
+ return pkgconf_queue_collect_dependencies_main(client, root, data, maxdepth);
+}
+
+static inline unsigned int
+pkgconf_queue_verify(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list, int maxdepth)
+{
+ unsigned int result;
+ const unsigned int saved_flags = client->flags;
+ pkgconf_pkg_t initial_world = {
+ .id = "user:request",
+ .realname = "virtual world package",
+ .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL,
+ };
+
+ if (!pkgconf_queue_compile(client, &initial_world, list))
+ {
+ pkgconf_solution_free(client, &initial_world);
+ return PKGCONF_PKG_ERRF_DEPGRAPH_BREAK;
+ }
+
+ PKGCONF_TRACE(client, "solving");
+ result = pkgconf_pkg_traverse(client, &initial_world, NULL, NULL, maxdepth, 0);
+ if (result != PKGCONF_PKG_ERRF_OK)
+ {
+ pkgconf_solution_free(client, &initial_world);
+ return result;
+ }
+
+ PKGCONF_TRACE(client, "flattening");
+ result = pkgconf_queue_collect_dependencies(client, &initial_world, world, maxdepth);
+ if (result != PKGCONF_PKG_ERRF_OK)
+ {
+ pkgconf_solution_free(client, &initial_world);
+ return result;
+ }
+
+ if (client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE)
+ {
+ PKGCONF_TRACE(client, "marking public deps");
+ client->flags &= ~PKGCONF_PKG_PKGF_SEARCH_PRIVATE;
+ client->flags |= PKGCONF_PKG_PKGF_SKIP_CONFLICTS;
+ result = pkgconf_pkg_traverse(client, &initial_world, pkgconf_queue_mark_public, &world->required, maxdepth, 0);
+ client->flags = saved_flags;
+ if (result != PKGCONF_PKG_ERRF_OK)
+ {
+ pkgconf_solution_free(client, &initial_world);
+ return result;
+ }
+ }
+
+ /* free the initial solution */
+ pkgconf_solution_free(client, &initial_world);
+
+ return PKGCONF_PKG_ERRF_OK;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_solution_free(pkgconf_client_t *client, pkgconf_pkg_t *world, int maxdepth)
+ *
+ * Removes references to package nodes contained in a solution.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param pkgconf_pkg_t* world: The root for the generated dependency graph. Should have PKGCONF_PKG_PROPF_VIRTUAL flag.
+ * :returns: nothing
+ */
+void
+pkgconf_solution_free(pkgconf_client_t *client, pkgconf_pkg_t *world)
+{
+ (void) client;
+
+ if (world->flags & PKGCONF_PKG_PROPF_VIRTUAL)
+ {
+ pkgconf_dependency_free(&world->required);
+ pkgconf_dependency_free(&world->requires_private);
+ }
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: bool pkgconf_queue_solve(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_pkg_t *world, int maxdepth)
+ *
+ * Solves and flattens the dependency graph for the supplied dependency list.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param pkgconf_list_t* list: The list of dependency requests to consider.
+ * :param pkgconf_pkg_t* world: The root for the generated dependency graph, provided by the caller. Should have PKGCONF_PKG_PROPF_VIRTUAL flag.
+ * :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited.
+ * :returns: true if the dependency resolver found a solution, otherwise false.
+ * :rtype: bool
+ */
+bool
+pkgconf_queue_solve(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_pkg_t *world, int maxdepth)
+{
+ /* if maxdepth is one, then we will not traverse deeper than our virtual package. */
+ if (!maxdepth)
+ maxdepth = -1;
+
+ unsigned int flags = client->flags;
+ client->flags |= PKGCONF_PKG_PKGF_SEARCH_PRIVATE;
+
+ unsigned int ret = pkgconf_queue_verify(client, world, list, maxdepth);
+ client->flags = flags;
+
+ return ret == PKGCONF_PKG_ERRF_OK;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data)
+ *
+ * Attempt to compile a dependency resolution queue into a dependency resolution problem, then attempt to solve the problem and
+ * feed the solution to a callback function if a complete dependency graph is found.
+ *
+ * This function should not be used in new code. Use pkgconf_queue_solve instead.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param pkgconf_list_t* list: The list of dependency requests to consider.
+ * :param pkgconf_queue_apply_func_t func: The callback function to call if a solution is found by the dependency resolver.
+ * :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited.
+ * :param void* data: An opaque pointer which is passed to the callback function.
+ * :returns: true if the dependency resolver found a solution, otherwise false.
+ * :rtype: bool
+ */
+bool
+pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data)
+{
+ bool ret = false;
+ pkgconf_pkg_t world = {
+ .id = "virtual:world",
+ .realname = "virtual world package",
+ .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL,
+ };
+
+ /* if maxdepth is one, then we will not traverse deeper than our virtual package. */
+ if (!maxdepth)
+ maxdepth = -1;
+
+ if (!pkgconf_queue_solve(client, list, &world, maxdepth))
+ goto cleanup;
+
+ /* the world dependency set is flattened after it is returned from pkgconf_queue_verify */
+ if (!func(client, &world, data, maxdepth))
+ goto cleanup;
+
+ ret = true;
+
+cleanup:
+ pkgconf_pkg_free(client, &world);
+ return ret;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_queue_validate(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data)
+ *
+ * Attempt to compile a dependency resolution queue into a dependency resolution problem, then attempt to solve the problem.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param pkgconf_list_t* list: The list of dependency requests to consider.
+ * :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited.
+ * :returns: true if the dependency resolver found a solution, otherwise false.
+ * :rtype: bool
+ */
+bool
+pkgconf_queue_validate(pkgconf_client_t *client, pkgconf_list_t *list, int maxdepth)
+{
+ bool retval = true;
+ pkgconf_pkg_t world = {
+ .id = "virtual:world",
+ .realname = "virtual world package",
+ .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL,
+ };
+
+ /* if maxdepth is one, then we will not traverse deeper than our virtual package. */
+ if (!maxdepth)
+ maxdepth = -1;
+
+ if (pkgconf_queue_verify(client, &world, list, maxdepth) != PKGCONF_PKG_ERRF_OK)
+ retval = false;
+
+ pkgconf_pkg_free(client, &world);
+
+ return retval;
+}
diff --git a/libpkgconf/stdinc.h b/libpkgconf/stdinc.h
new file mode 100644
index 000000000000..31284ed88261
--- /dev/null
+++ b/libpkgconf/stdinc.h
@@ -0,0 +1,75 @@
+/*
+ * stdinc.h
+ * pull in standard headers (including portability hacks)
+ *
+ * Copyright (c) 2012 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#ifndef LIBPKGCONF_STDINC_H
+#define LIBPKGCONF_STDINC_H
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <errno.h>
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# include <malloc.h>
+# define PATH_DEV_NULL "nul"
+# ifdef _WIN64
+# ifndef __MINGW32__
+# define SIZE_FMT_SPECIFIER "%I64u"
+# else
+# define SIZE_FMT_SPECIFIER "%llu"
+# endif
+# else
+# define SIZE_FMT_SPECIFIER "%u"
+# endif
+# ifndef ssize_t
+# ifndef __MINGW32__
+# include <BaseTsd.h>
+# else
+# include <basetsd.h>
+# endif
+# define ssize_t SSIZE_T
+# endif
+# ifndef __MINGW32__
+# include "win-dirent.h"
+# else
+# include <dirent.h>
+# endif
+# define PKGCONF_ITEM_SIZE (_MAX_PATH + 1024)
+#else
+# define PATH_DEV_NULL "/dev/null"
+# define SIZE_FMT_SPECIFIER "%zu"
+# ifdef __HAIKU__
+# include <FindDirectory.h>
+# endif
+# include <dirent.h>
+# include <unistd.h>
+# include <limits.h>
+# include <strings.h>
+# ifdef PATH_MAX
+# define PKGCONF_ITEM_SIZE (PATH_MAX + 1024)
+# else
+# define PKGCONF_ITEM_SIZE (4096 + 1024)
+# endif
+#endif
+
+#endif
diff --git a/libpkgconf/tuple.c b/libpkgconf/tuple.c
new file mode 100644
index 000000000000..83f6a479e772
--- /dev/null
+++ b/libpkgconf/tuple.c
@@ -0,0 +1,476 @@
+/*
+ * tuple.c
+ * management of key->value tuples
+ *
+ * Copyright (c) 2011, 2012 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !doc
+ *
+ * libpkgconf `tuple` module
+ * =========================
+ *
+ * The `tuple` module provides key-value mappings backed by a linked list. The key-value
+ * mapping is mainly used for variable substitution when parsing .pc files.
+ *
+ * There are two sets of mappings: a ``pkgconf_pkg_t`` specific mapping, and a `global` mapping.
+ * The `tuple` module provides convenience wrappers for managing the `global` mapping, which is
+ * attached to a given client object.
+ */
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value)
+ *
+ * Defines a global variable, replacing the previous declaration if one was set.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to modify.
+ * :param char* key: The key for the mapping (variable name).
+ * :param char* value: The value for the mapped entry.
+ * :return: nothing
+ */
+void
+pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value)
+{
+ pkgconf_tuple_add(client, &client->global_vars, key, value, false, 0);
+}
+
+static pkgconf_tuple_t *
+lookup_global_tuple(const pkgconf_client_t *client, const char *key)
+{
+ pkgconf_node_t *node;
+
+ PKGCONF_FOREACH_LIST_ENTRY(client->global_vars.head, node)
+ {
+ pkgconf_tuple_t *tuple = node->data;
+
+ if (!strcmp(tuple->key, key))
+ return tuple;
+ }
+
+ return NULL;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key)
+ *
+ * Looks up a global variable.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to access.
+ * :param char* key: The key or variable name to look up.
+ * :return: the contents of the variable or ``NULL``
+ * :rtype: char *
+ */
+char *
+pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key)
+{
+ pkgconf_tuple_t *tuple;
+
+ tuple = lookup_global_tuple(client, key);
+ if (tuple == NULL)
+ return NULL;
+
+ return tuple->value;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_tuple_free_global(pkgconf_client_t *client)
+ *
+ * Delete all global variables associated with a pkgconf client object.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to modify.
+ * :return: nothing
+ */
+void
+pkgconf_tuple_free_global(pkgconf_client_t *client)
+{
+ pkgconf_tuple_free(&client->global_vars);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv)
+ *
+ * Parse and define a global variable.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to modify.
+ * :param char* kv: The variable in the form of ``key=value``.
+ * :return: nothing
+ */
+void
+pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv)
+{
+ char *workbuf = strdup(kv);
+ char *value;
+ pkgconf_tuple_t *tuple;
+
+ value = strchr(workbuf, '=');
+ if (value == NULL)
+ goto out;
+
+ *value++ = '\0';
+
+ tuple = pkgconf_tuple_add(client, &client->global_vars, workbuf, value, false, 0);
+ if (tuple != NULL)
+ tuple->flags = PKGCONF_PKG_TUPLEF_OVERRIDE;
+
+out:
+ free(workbuf);
+}
+
+static void
+pkgconf_tuple_find_delete(pkgconf_list_t *list, const char *key)
+{
+ pkgconf_node_t *node, *next;
+
+ PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
+ {
+ pkgconf_tuple_t *tuple = node->data;
+
+ if (!strcmp(tuple->key, key))
+ {
+ pkgconf_tuple_free_entry(tuple, list);
+ return;
+ }
+ }
+}
+
+static char *
+dequote(const char *value)
+{
+ char *buf = calloc(1, (strlen(value) + 1) * 2);
+ char *bptr = buf;
+ const char *i;
+ char quote = 0;
+
+ if (*value == '\'' || *value == '"')
+ quote = *value;
+
+ for (i = value; *i != '\0'; i++)
+ {
+ if (*i == '\\' && quote && *(i + 1) == quote)
+ {
+ i++;
+ *bptr++ = *i;
+ }
+ else if (*i != quote)
+ *bptr++ = *i;
+ }
+
+ return buf;
+}
+
+static const char *
+find_sysroot(const pkgconf_client_t *client, pkgconf_list_t *vars)
+{
+ const char *sysroot_dir;
+
+ sysroot_dir = pkgconf_tuple_find(client, vars, "pc_sysrootdir");
+ if (sysroot_dir == NULL)
+ sysroot_dir = client->sysroot_dir;
+
+ return sysroot_dir;
+}
+
+static bool
+should_rewrite_sysroot(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *buf, unsigned int flags)
+{
+ const char *sysroot_dir;
+
+ if (flags & PKGCONF_PKG_PROPF_UNINSTALLED && !(client->flags & PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES))
+ return false;
+
+ sysroot_dir = find_sysroot(client, vars);
+ if (sysroot_dir == NULL)
+ return false;
+
+ if (*buf != '/')
+ return false;
+
+ if (!strcmp(sysroot_dir, "/"))
+ return false;
+
+ if (strlen(buf) <= strlen(sysroot_dir))
+ return false;
+
+ if (strstr(buf + strlen(sysroot_dir), sysroot_dir) == NULL)
+ return false;
+
+ return true;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_tuple_t *pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse)
+ *
+ * Optionally parse and then define a variable.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to access.
+ * :param pkgconf_list_t* list: The variable list to add the new variable to.
+ * :param char* key: The name of the variable being added.
+ * :param char* value: The value of the variable being added.
+ * :param bool parse: Whether or not to parse the value for variable substitution.
+ * :return: a variable object
+ * :rtype: pkgconf_tuple_t *
+ */
+pkgconf_tuple_t *
+pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse, unsigned int flags)
+{
+ char *dequote_value;
+ pkgconf_tuple_t *tuple = calloc(1, sizeof(pkgconf_tuple_t));
+
+ pkgconf_tuple_find_delete(list, key);
+
+ dequote_value = dequote(value);
+
+ tuple->key = strdup(key);
+ if (parse)
+ tuple->value = pkgconf_tuple_parse(client, list, dequote_value, flags);
+ else
+ tuple->value = strdup(dequote_value);
+
+ PKGCONF_TRACE(client, "adding tuple to @%p: %s => %s (parsed? %d)", list, key, tuple->value, parse);
+
+ pkgconf_node_insert(&tuple->iter, tuple, list);
+
+ free(dequote_value);
+
+ return tuple;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key)
+ *
+ * Look up a variable in a variable list.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to access.
+ * :param pkgconf_list_t* list: The variable list to search.
+ * :param char* key: The variable name to search for.
+ * :return: the value of the variable or ``NULL``
+ * :rtype: char *
+ */
+char *
+pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key)
+{
+ pkgconf_node_t *node;
+ pkgconf_tuple_t *global_tuple;
+
+ global_tuple = lookup_global_tuple(client, key);
+ if (global_tuple != NULL && global_tuple->flags & PKGCONF_PKG_TUPLEF_OVERRIDE)
+ return global_tuple->value;
+
+ PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
+ {
+ pkgconf_tuple_t *tuple = node->data;
+
+ if (!strcmp(tuple->key, key))
+ return tuple->value;
+ }
+
+ if (global_tuple != NULL)
+ return global_tuple->value;
+
+ return NULL;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: char *pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value, unsigned int flags)
+ *
+ * Parse an expression for variable substitution.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to access.
+ * :param pkgconf_list_t* list: The variable list to search for variables (along side the global variable list).
+ * :param char* value: The ``key=value`` string to parse.
+ * :param uint flags: Any flags to consider while parsing.
+ * :return: the variable data with any variables substituted
+ * :rtype: char *
+ */
+char *
+pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value, unsigned int flags)
+{
+ char buf[PKGCONF_BUFSIZE];
+ const char *ptr;
+ char *bptr = buf;
+
+ if (!(client->flags & PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES) &&
+ (!(flags & PKGCONF_PKG_PROPF_UNINSTALLED) || (client->flags & PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES)))
+ {
+ if (*value == '/' && client->sysroot_dir != NULL && strncmp(value, client->sysroot_dir, strlen(client->sysroot_dir)))
+ bptr += pkgconf_strlcpy(buf, client->sysroot_dir, sizeof buf);
+ }
+
+ for (ptr = value; *ptr != '\0' && bptr - buf < PKGCONF_BUFSIZE; ptr++)
+ {
+ if (*ptr != '$' || (*ptr == '$' && *(ptr + 1) != '{'))
+ *bptr++ = *ptr;
+ else if (*(ptr + 1) == '{')
+ {
+ char varname[PKGCONF_ITEM_SIZE];
+ char *vend = varname + PKGCONF_ITEM_SIZE - 1;
+ char *vptr = varname;
+ const char *pptr;
+ char *kv, *parsekv;
+
+ *vptr = '\0';
+
+ for (pptr = ptr + 2; *pptr != '\0'; pptr++)
+ {
+ if (*pptr != '}')
+ {
+ if (vptr < vend)
+ *vptr++ = *pptr;
+ else
+ {
+ *vptr = '\0';
+ break;
+ }
+ }
+ else
+ {
+ *vptr = '\0';
+ break;
+ }
+ }
+
+ PKGCONF_TRACE(client, "lookup tuple %s", varname);
+
+ size_t remain = PKGCONF_BUFSIZE - (bptr - buf);
+ ptr += (pptr - ptr);
+ kv = pkgconf_tuple_find_global(client, varname);
+ if (kv != NULL)
+ {
+ size_t nlen = pkgconf_strlcpy(bptr, kv, remain);
+ if (nlen > remain)
+ {
+ pkgconf_warn(client, "warning: truncating very long variable to 64KB\n");
+
+ bptr = buf + (PKGCONF_BUFSIZE - 1);
+ break;
+ }
+
+ bptr += nlen;
+ }
+ else
+ {
+ kv = pkgconf_tuple_find(client, vars, varname);
+
+ if (kv != NULL)
+ {
+ size_t nlen;
+
+ parsekv = pkgconf_tuple_parse(client, vars, kv, flags);
+ nlen = pkgconf_strlcpy(bptr, parsekv, remain);
+ free(parsekv);
+
+ if (nlen > remain)
+ {
+ pkgconf_warn(client, "warning: truncating very long variable to 64KB\n");
+
+ bptr = buf + (PKGCONF_BUFSIZE - 1);
+ break;
+ }
+
+ bptr += nlen;
+ }
+ }
+ }
+ }
+
+ *bptr = '\0';
+
+ /*
+ * Sigh. Somebody actually attempted to use freedesktop.org pkg-config's broken sysroot support,
+ * which was written by somebody who did not understand how sysroots are supposed to work. This
+ * results in an incorrect path being built as the sysroot will be prepended twice, once explicitly,
+ * and once by variable expansion (the pkgconf approach). We could simply make ${pc_sysrootdir} blank,
+ * but sometimes it is necessary to know the explicit sysroot path for other reasons, so we can't really
+ * do that.
+ *
+ * As a result, we check to see if ${pc_sysrootdir} is prepended as a duplicate, and if so, remove the
+ * prepend. This allows us to handle both our approach and the broken freedesktop.org implementation's
+ * approach. Because a path can be shorter than ${pc_sysrootdir}, we do some checks first to ensure it's
+ * safe to skip ahead in the string to scan for our sysroot dir.
+ *
+ * Finally, we call pkgconf_path_relocate() to clean the path of spurious elements.
+ *
+ * New in 1.9: Only attempt to rewrite the sysroot if we are not processing an uninstalled package.
+ */
+ if (should_rewrite_sysroot(client, vars, buf, flags))
+ {
+ char cleanpath[PKGCONF_ITEM_SIZE];
+ const char *sysroot_dir = find_sysroot(client, vars);
+
+ pkgconf_strlcpy(cleanpath, buf + strlen(sysroot_dir), sizeof cleanpath);
+ pkgconf_path_relocate(cleanpath, sizeof cleanpath);
+
+ return strdup(cleanpath);
+ }
+
+ return strdup(buf);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list)
+ *
+ * Deletes a variable object, removing it from any variable lists and releasing any memory associated
+ * with it.
+ *
+ * :param pkgconf_tuple_t* tuple: The variable object to release.
+ * :param pkgconf_list_t* list: The variable list the variable object is attached to.
+ * :return: nothing
+ */
+void
+pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list)
+{
+ pkgconf_node_delete(&tuple->iter, list);
+
+ free(tuple->key);
+ free(tuple->value);
+ free(tuple);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_tuple_free(pkgconf_list_t *list)
+ *
+ * Deletes a variable list and any variables attached to it.
+ *
+ * :param pkgconf_list_t* list: The variable list to delete.
+ * :return: nothing
+ */
+void
+pkgconf_tuple_free(pkgconf_list_t *list)
+{
+ pkgconf_node_t *node, *next;
+
+ PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
+ pkgconf_tuple_free_entry(node->data, list);
+
+ pkgconf_list_zero(list);
+}
diff --git a/libpkgconf/win-dirent.h b/libpkgconf/win-dirent.h
new file mode 100644
index 000000000000..0734d6dc470f
--- /dev/null
+++ b/libpkgconf/win-dirent.h
@@ -0,0 +1,1028 @@
+/*
+ * Dirent interface for Microsoft Visual Studio
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent. Dirent may be freely distributed
+ * under the MIT license. For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#ifndef DIRENT_H
+#define DIRENT_H
+
+/* Hide warnings about unreferenced local functions */
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wunused-function"
+#elif defined(_MSC_VER)
+# pragma warning(disable:4505)
+#elif defined(__GNUC__)
+# pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+/*
+ * Include windows.h without Windows Sockets 1.1 to prevent conflicts with
+ * Windows Sockets 2.0.
+ */
+#ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <wchar.h>
+#include <string.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
+
+/* Indicates that d_type field is available in dirent structure */
+#define _DIRENT_HAVE_D_TYPE
+
+/* Indicates that d_namlen field is available in dirent structure */
+#define _DIRENT_HAVE_D_NAMLEN
+
+/* Entries missing from MSVC 6.0 */
+#if !defined(FILE_ATTRIBUTE_DEVICE)
+# define FILE_ATTRIBUTE_DEVICE 0x40
+#endif
+
+/* File type and permission flags for stat(), general mask */
+#if !defined(S_IFMT)
+# define S_IFMT _S_IFMT
+#endif
+
+/* Directory bit */
+#if !defined(S_IFDIR)
+# define S_IFDIR _S_IFDIR
+#endif
+
+/* Character device bit */
+#if !defined(S_IFCHR)
+# define S_IFCHR _S_IFCHR
+#endif
+
+/* Pipe bit */
+#if !defined(S_IFFIFO)
+# define S_IFFIFO _S_IFFIFO
+#endif
+
+/* Regular file bit */
+#if !defined(S_IFREG)
+# define S_IFREG _S_IFREG
+#endif
+
+/* Read permission */
+#if !defined(S_IREAD)
+# define S_IREAD _S_IREAD
+#endif
+
+/* Write permission */
+#if !defined(S_IWRITE)
+# define S_IWRITE _S_IWRITE
+#endif
+
+/* Execute permission */
+#if !defined(S_IEXEC)
+# define S_IEXEC _S_IEXEC
+#endif
+
+/* Pipe */
+#if !defined(S_IFIFO)
+# define S_IFIFO _S_IFIFO
+#endif
+
+/* Block device */
+#if !defined(S_IFBLK)
+# define S_IFBLK 0
+#endif
+
+/* Link */
+#if !defined(S_IFLNK)
+# define S_IFLNK 0
+#endif
+
+/* Socket */
+#if !defined(S_IFSOCK)
+# define S_IFSOCK 0
+#endif
+
+/* Read user permission */
+#if !defined(S_IRUSR)
+# define S_IRUSR S_IREAD
+#endif
+
+/* Write user permission */
+#if !defined(S_IWUSR)
+# define S_IWUSR S_IWRITE
+#endif
+
+/* Execute user permission */
+#if !defined(S_IXUSR)
+# define S_IXUSR 0
+#endif
+
+/* Read group permission */
+#if !defined(S_IRGRP)
+# define S_IRGRP 0
+#endif
+
+/* Write group permission */
+#if !defined(S_IWGRP)
+# define S_IWGRP 0
+#endif
+
+/* Execute group permission */
+#if !defined(S_IXGRP)
+# define S_IXGRP 0
+#endif
+
+/* Read others permission */
+#if !defined(S_IROTH)
+# define S_IROTH 0
+#endif
+
+/* Write others permission */
+#if !defined(S_IWOTH)
+# define S_IWOTH 0
+#endif
+
+/* Execute others permission */
+#if !defined(S_IXOTH)
+# define S_IXOTH 0
+#endif
+
+/* Maximum length of file name */
+#if !defined(PATH_MAX)
+# define PATH_MAX MAX_PATH
+#endif
+#if !defined(FILENAME_MAX)
+# define FILENAME_MAX MAX_PATH
+#endif
+#if !defined(NAME_MAX)
+# define NAME_MAX FILENAME_MAX
+#endif
+
+/* File type flags for d_type */
+#define DT_UNKNOWN 0
+#define DT_REG S_IFREG
+#define DT_DIR S_IFDIR
+#define DT_FIFO S_IFIFO
+#define DT_SOCK S_IFSOCK
+#define DT_CHR S_IFCHR
+#define DT_BLK S_IFBLK
+#define DT_LNK S_IFLNK
+
+/* Macros for converting between st_mode and d_type */
+#define IFTODT(mode) ((mode) & S_IFMT)
+#define DTTOIF(type) (type)
+
+/*
+ * File type macros. Note that block devices, sockets and links cannot be
+ * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
+ * only defined for compatibility. These macros should always return false
+ * on Windows.
+ */
+#if !defined(S_ISFIFO)
+# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISDIR)
+# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG)
+# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISLNK)
+# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK)
+# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISCHR)
+# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISBLK)
+# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
+#endif
+
+/* Return the exact length of the file name without zero terminator */
+#define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
+
+/* Return the maximum size of a file name */
+#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1)
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Wide-character version */
+struct _wdirent {
+ /* Always zero */
+ long d_ino;
+
+ /* File position within stream */
+ long d_off;
+
+ /* Structure size */
+ unsigned short d_reclen;
+
+ /* Length of name without \0 */
+ size_t d_namlen;
+
+ /* File type */
+ int d_type;
+
+ /* File name */
+ wchar_t d_name[PATH_MAX+1];
+};
+typedef struct _wdirent _wdirent;
+
+struct _WDIR {
+ /* Current directory entry */
+ struct _wdirent ent;
+
+ /* Private file data */
+ WIN32_FIND_DATAW data;
+
+ /* True if data is valid */
+ int cached;
+
+ /* Win32 search handle */
+ HANDLE handle;
+
+ /* Initial directory name */
+ wchar_t *patt;
+};
+typedef struct _WDIR _WDIR;
+
+/* Multi-byte character version */
+struct dirent {
+ /* Always zero */
+ long d_ino;
+
+ /* File position within stream */
+ long d_off;
+
+ /* Structure size */
+ unsigned short d_reclen;
+
+ /* Length of name without \0 */
+ size_t d_namlen;
+
+ /* File type */
+ int d_type;
+
+ /* File name */
+ char d_name[PATH_MAX+1];
+};
+typedef struct dirent dirent;
+
+struct DIR {
+ struct dirent ent;
+ struct _WDIR *wdirp;
+};
+typedef struct DIR DIR;
+
+
+/* Dirent functions */
+static DIR *opendir(const char *dirname);
+static _WDIR *_wopendir(const wchar_t *dirname);
+
+static struct dirent *readdir(DIR *dirp);
+static struct _wdirent *_wreaddir(_WDIR *dirp);
+
+static int readdir_r(
+ DIR *dirp, struct dirent *entry, struct dirent **result);
+static int _wreaddir_r(
+ _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result);
+
+static int closedir(DIR *dirp);
+static int _wclosedir(_WDIR *dirp);
+
+static void rewinddir(DIR* dirp);
+static void _wrewinddir(_WDIR* dirp);
+
+static int scandir(const char *dirname, struct dirent ***namelist,
+ int (*filter)(const struct dirent*),
+ int (*compare)(const struct dirent**, const struct dirent**));
+
+static int alphasort(const struct dirent **a, const struct dirent **b);
+
+static int versionsort(const struct dirent **a, const struct dirent **b);
+
+static int strverscmp(const char *a, const char *b);
+
+/* For compatibility with Symbian */
+#define wdirent _wdirent
+#define WDIR _WDIR
+#define wopendir _wopendir
+#define wreaddir _wreaddir
+#define wclosedir _wclosedir
+#define wrewinddir _wrewinddir
+
+/* Compatibility with older Microsoft compilers and non-Microsoft compilers */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+# define wcstombs_s dirent_wcstombs_s
+# define mbstowcs_s dirent_mbstowcs_s
+#endif
+
+/* Optimize dirent_set_errno() away on modern Microsoft compilers */
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+# define dirent_set_errno _set_errno
+#endif
+
+
+/* Internal utility functions */
+static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp);
+static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp);
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int dirent_mbstowcs_s(
+ size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords,
+ const char *mbstr, size_t count);
+#endif
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int dirent_wcstombs_s(
+ size_t *pReturnValue, char *mbstr, size_t sizeInBytes,
+ const wchar_t *wcstr, size_t count);
+#endif
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static void dirent_set_errno(int error);
+#endif
+
+
+/*
+ * Open directory stream DIRNAME for read and return a pointer to the
+ * internal working area that is used to retrieve individual directory
+ * entries.
+ */
+static _WDIR *_wopendir(const wchar_t *dirname)
+{
+ wchar_t *p;
+
+ /* Must have directory name */
+ if (dirname == NULL || dirname[0] == '\0') {
+ dirent_set_errno(ENOENT);
+ return NULL;
+ }
+
+ /* Allocate new _WDIR structure */
+ _WDIR *dirp = (_WDIR*) malloc(sizeof(struct _WDIR));
+ if (!dirp)
+ return NULL;
+
+ /* Reset _WDIR structure */
+ dirp->handle = INVALID_HANDLE_VALUE;
+ dirp->patt = NULL;
+ dirp->cached = 0;
+
+ /*
+ * Compute the length of full path plus zero terminator
+ *
+ * Note that on WinRT there's no way to convert relative paths
+ * into absolute paths, so just assume it is an absolute path.
+ */
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+ /* Desktop */
+ DWORD n = GetFullPathNameW(dirname, 0, NULL, NULL);
+#else
+ /* WinRT */
+ size_t n = wcslen(dirname);
+#endif
+
+ /* Allocate room for absolute directory name and search pattern */
+ dirp->patt = (wchar_t*) malloc(sizeof(wchar_t) * n + 16);
+ if (dirp->patt == NULL)
+ goto exit_closedir;
+
+ /*
+ * Convert relative directory name to an absolute one. This
+ * allows rewinddir() to function correctly even when current
+ * working directory is changed between opendir() and rewinddir().
+ *
+ * Note that on WinRT there's no way to convert relative paths
+ * into absolute paths, so just assume it is an absolute path.
+ */
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+ /* Desktop */
+ n = GetFullPathNameW(dirname, n, dirp->patt, NULL);
+ if (n <= 0)
+ goto exit_closedir;
+#else
+ /* WinRT */
+ wcsncpy_s(dirp->patt, n+1, dirname, n);
+#endif
+
+ /* Append search pattern \* to the directory name */
+ p = dirp->patt + n;
+ switch (p[-1]) {
+ case '\\':
+ case '/':
+ case ':':
+ /* Directory ends in path separator, e.g. c:\temp\ */
+ /*NOP*/;
+ break;
+
+ default:
+ /* Directory name doesn't end in path separator */
+ *p++ = '\\';
+ }
+ *p++ = '*';
+ *p = '\0';
+
+ /* Open directory stream and retrieve the first entry */
+ if (!dirent_first(dirp))
+ goto exit_closedir;
+
+ /* Success */
+ return dirp;
+
+ /* Failure */
+exit_closedir:
+ _wclosedir(dirp);
+ return NULL;
+}
+
+/*
+ * Read next directory entry.
+ *
+ * Returns pointer to static directory entry which may be overwritten by
+ * subsequent calls to _wreaddir().
+ */
+static struct _wdirent *_wreaddir(_WDIR *dirp)
+{
+ /*
+ * Read directory entry to buffer. We can safely ignore the return
+ * value as entry will be set to NULL in case of error.
+ */
+ struct _wdirent *entry;
+ (void) _wreaddir_r(dirp, &dirp->ent, &entry);
+
+ /* Return pointer to statically allocated directory entry */
+ return entry;
+}
+
+/*
+ * Read next directory entry.
+ *
+ * Returns zero on success. If end of directory stream is reached, then sets
+ * result to NULL and returns zero.
+ */
+static int _wreaddir_r(
+ _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result)
+{
+ /* Read next directory entry */
+ WIN32_FIND_DATAW *datap = dirent_next(dirp);
+ if (!datap) {
+ /* Return NULL to indicate end of directory */
+ *result = NULL;
+ return /*OK*/0;
+ }
+
+ /*
+ * Copy file name as wide-character string. If the file name is too
+ * long to fit in to the destination buffer, then truncate file name
+ * to PATH_MAX characters and zero-terminate the buffer.
+ */
+ size_t n = 0;
+ while (n < PATH_MAX && datap->cFileName[n] != 0) {
+ entry->d_name[n] = datap->cFileName[n];
+ n++;
+ }
+ entry->d_name[n] = 0;
+
+ /* Length of file name excluding zero terminator */
+ entry->d_namlen = n;
+
+ /* File type */
+ DWORD attr = datap->dwFileAttributes;
+ if ((attr & FILE_ATTRIBUTE_DEVICE) != 0)
+ entry->d_type = DT_CHR;
+ else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ entry->d_type = DT_DIR;
+ else
+ entry->d_type = DT_REG;
+
+ /* Reset dummy fields */
+ entry->d_ino = 0;
+ entry->d_off = 0;
+ entry->d_reclen = sizeof(struct _wdirent);
+
+ /* Set result address */
+ *result = entry;
+ return /*OK*/0;
+}
+
+/*
+ * Close directory stream opened by opendir() function. This invalidates the
+ * DIR structure as well as any directory entry read previously by
+ * _wreaddir().
+ */
+static int _wclosedir(_WDIR *dirp)
+{
+ if (!dirp) {
+ dirent_set_errno(EBADF);
+ return /*failure*/-1;
+ }
+
+ /* Release search handle */
+ if (dirp->handle != INVALID_HANDLE_VALUE)
+ FindClose(dirp->handle);
+
+ /* Release search pattern */
+ free(dirp->patt);
+
+ /* Release directory structure */
+ free(dirp);
+ return /*success*/0;
+}
+
+/*
+ * Rewind directory stream such that _wreaddir() returns the very first
+ * file name again.
+ */
+static void _wrewinddir(_WDIR* dirp)
+{
+ if (!dirp)
+ return;
+
+ /* Release existing search handle */
+ if (dirp->handle != INVALID_HANDLE_VALUE)
+ FindClose(dirp->handle);
+
+ /* Open new search handle */
+ dirent_first(dirp);
+}
+
+/* Get first directory entry */
+static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp)
+{
+ if (!dirp)
+ return NULL;
+
+ /* Open directory and retrieve the first entry */
+ dirp->handle = FindFirstFileExW(
+ dirp->patt, FindExInfoStandard, &dirp->data,
+ FindExSearchNameMatch, NULL, 0);
+ if (dirp->handle == INVALID_HANDLE_VALUE)
+ goto error;
+
+ /* A directory entry is now waiting in memory */
+ dirp->cached = 1;
+ return &dirp->data;
+
+error:
+ /* Failed to open directory: no directory entry in memory */
+ dirp->cached = 0;
+
+ /* Set error code */
+ DWORD errorcode = GetLastError();
+ switch (errorcode) {
+ case ERROR_ACCESS_DENIED:
+ /* No read access to directory */
+ dirent_set_errno(EACCES);
+ break;
+
+ case ERROR_DIRECTORY:
+ /* Directory name is invalid */
+ dirent_set_errno(ENOTDIR);
+ break;
+
+ case ERROR_PATH_NOT_FOUND:
+ default:
+ /* Cannot find the file */
+ dirent_set_errno(ENOENT);
+ }
+ return NULL;
+}
+
+/* Get next directory entry */
+static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp)
+{
+ /* Is the next directory entry already in cache? */
+ if (dirp->cached) {
+ /* Yes, a valid directory entry found in memory */
+ dirp->cached = 0;
+ return &dirp->data;
+ }
+
+ /* No directory entry in cache */
+ if (dirp->handle == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ /* Read the next directory entry from stream */
+ if (FindNextFileW(dirp->handle, &dirp->data) == FALSE)
+ goto exit_close;
+
+ /* Success */
+ return &dirp->data;
+
+ /* Failure */
+exit_close:
+ FindClose(dirp->handle);
+ dirp->handle = INVALID_HANDLE_VALUE;
+ return NULL;
+}
+
+/* Open directory stream using plain old C-string */
+static DIR *opendir(const char *dirname)
+{
+ /* Must have directory name */
+ if (dirname == NULL || dirname[0] == '\0') {
+ dirent_set_errno(ENOENT);
+ return NULL;
+ }
+
+ /* Allocate memory for DIR structure */
+ struct DIR *dirp = (DIR*) malloc(sizeof(struct DIR));
+ if (!dirp)
+ return NULL;
+
+ /* Convert directory name to wide-character string */
+ wchar_t wname[PATH_MAX + 1];
+ size_t n;
+ int error = mbstowcs_s(&n, wname, PATH_MAX + 1, dirname, PATH_MAX+1);
+ if (error)
+ goto exit_failure;
+
+ /* Open directory stream using wide-character name */
+ dirp->wdirp = _wopendir(wname);
+ if (!dirp->wdirp)
+ goto exit_failure;
+
+ /* Success */
+ return dirp;
+
+ /* Failure */
+exit_failure:
+ free(dirp);
+ return NULL;
+}
+
+/* Read next directory entry */
+static struct dirent *readdir(DIR *dirp)
+{
+ /*
+ * Read directory entry to buffer. We can safely ignore the return
+ * value as entry will be set to NULL in case of error.
+ */
+ struct dirent *entry;
+ (void) readdir_r(dirp, &dirp->ent, &entry);
+
+ /* Return pointer to statically allocated directory entry */
+ return entry;
+}
+
+/*
+ * Read next directory entry into called-allocated buffer.
+ *
+ * Returns zero on success. If the end of directory stream is reached, then
+ * sets result to NULL and returns zero.
+ */
+static int readdir_r(
+ DIR *dirp, struct dirent *entry, struct dirent **result)
+{
+ /* Read next directory entry */
+ WIN32_FIND_DATAW *datap = dirent_next(dirp->wdirp);
+ if (!datap) {
+ /* No more directory entries */
+ *result = NULL;
+ return /*OK*/0;
+ }
+
+ /* Attempt to convert file name to multi-byte string */
+ size_t n;
+ int error = wcstombs_s(
+ &n, entry->d_name, PATH_MAX + 1,
+ datap->cFileName, PATH_MAX + 1);
+
+ /*
+ * If the file name cannot be represented by a multi-byte string, then
+ * attempt to use old 8+3 file name. This allows the program to
+ * access files although file names may seem unfamiliar to the user.
+ *
+ * Be ware that the code below cannot come up with a short file name
+ * unless the file system provides one. At least VirtualBox shared
+ * folders fail to do this.
+ */
+ if (error && datap->cAlternateFileName[0] != '\0') {
+ error = wcstombs_s(
+ &n, entry->d_name, PATH_MAX + 1,
+ datap->cAlternateFileName, PATH_MAX + 1);
+ }
+
+ if (!error) {
+ /* Length of file name excluding zero terminator */
+ entry->d_namlen = n - 1;
+
+ /* File attributes */
+ DWORD attr = datap->dwFileAttributes;
+ if ((attr & FILE_ATTRIBUTE_DEVICE) != 0)
+ entry->d_type = DT_CHR;
+ else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ entry->d_type = DT_DIR;
+ else
+ entry->d_type = DT_REG;
+
+ /* Reset dummy fields */
+ entry->d_ino = 0;
+ entry->d_off = 0;
+ entry->d_reclen = sizeof(struct dirent);
+ } else {
+ /*
+ * Cannot convert file name to multi-byte string so construct
+ * an erroneous directory entry and return that. Note that
+ * we cannot return NULL as that would stop the processing
+ * of directory entries completely.
+ */
+ entry->d_name[0] = '?';
+ entry->d_name[1] = '\0';
+ entry->d_namlen = 1;
+ entry->d_type = DT_UNKNOWN;
+ entry->d_ino = 0;
+ entry->d_off = -1;
+ entry->d_reclen = 0;
+ }
+
+ /* Return pointer to directory entry */
+ *result = entry;
+ return /*OK*/0;
+}
+
+/* Close directory stream */
+static int closedir(DIR *dirp)
+{
+ int ok;
+
+ if (!dirp)
+ goto exit_failure;
+
+ /* Close wide-character directory stream */
+ ok = _wclosedir(dirp->wdirp);
+ dirp->wdirp = NULL;
+
+ /* Release multi-byte character version */
+ free(dirp);
+ return ok;
+
+exit_failure:
+ /* Invalid directory stream */
+ dirent_set_errno(EBADF);
+ return /*failure*/-1;
+}
+
+/* Rewind directory stream to beginning */
+static void rewinddir(DIR* dirp)
+{
+ if (!dirp)
+ return;
+
+ /* Rewind wide-character string directory stream */
+ _wrewinddir(dirp->wdirp);
+}
+
+/* Scan directory for entries */
+static int scandir(
+ const char *dirname, struct dirent ***namelist,
+ int (*filter)(const struct dirent*),
+ int (*compare)(const struct dirent**, const struct dirent**))
+{
+ int result;
+
+ /* Open directory stream */
+ DIR *dir = opendir(dirname);
+ if (!dir) {
+ /* Cannot open directory */
+ return /*Error*/ -1;
+ }
+
+ /* Read directory entries to memory */
+ struct dirent *tmp = NULL;
+ struct dirent **files = NULL;
+ size_t size = 0;
+ size_t allocated = 0;
+ while (1) {
+ /* Allocate room for a temporary directory entry */
+ if (!tmp) {
+ tmp = (struct dirent*) malloc(sizeof(struct dirent));
+ if (!tmp)
+ goto exit_failure;
+ }
+
+ /* Read directory entry to temporary area */
+ struct dirent *entry;
+ if (readdir_r(dir, tmp, &entry) != /*OK*/0)
+ goto exit_failure;
+
+ /* Stop if we already read the last directory entry */
+ if (entry == NULL)
+ goto exit_success;
+
+ /* Determine whether to include the entry in results */
+ if (filter && !filter(tmp))
+ continue;
+
+ /* Enlarge pointer table to make room for another pointer */
+ if (size >= allocated) {
+ /* Compute number of entries in the new table */
+ size_t num_entries = size * 2 + 16;
+
+ /* Allocate new pointer table or enlarge existing */
+ void *p = realloc(files, sizeof(void*) * num_entries);
+ if (!p)
+ goto exit_failure;
+
+ /* Got the memory */
+ files = (dirent**) p;
+ allocated = num_entries;
+ }
+
+ /* Store the temporary entry to ptr table */
+ files[size++] = tmp;
+ tmp = NULL;
+ }
+
+exit_failure:
+ /* Release allocated file entries */
+ for (size_t i = 0; i < size; i++) {
+ free(files[i]);
+ }
+
+ /* Release the pointer table */
+ free(files);
+ files = NULL;
+
+ /* Exit with error code */
+ result = /*error*/ -1;
+ goto exit_status;
+
+exit_success:
+ /* Sort directory entries */
+ qsort(files, size, sizeof(void*),
+ (int (*) (const void*, const void*)) compare);
+
+ /* Pass pointer table to caller */
+ if (namelist)
+ *namelist = files;
+
+ /* Return the number of directory entries read */
+ result = (int) size;
+
+exit_status:
+ /* Release temporary directory entry, if we had one */
+ free(tmp);
+
+ /* Close directory stream */
+ closedir(dir);
+ return result;
+}
+
+/* Alphabetical sorting */
+static int alphasort(const struct dirent **a, const struct dirent **b)
+{
+ return strcoll((*a)->d_name, (*b)->d_name);
+}
+
+/* Sort versions */
+static int versionsort(const struct dirent **a, const struct dirent **b)
+{
+ return strverscmp((*a)->d_name, (*b)->d_name);
+}
+
+/* Compare strings */
+static int strverscmp(const char *a, const char *b)
+{
+ size_t i = 0;
+ size_t j;
+
+ /* Find first difference */
+ while (a[i] == b[i]) {
+ if (a[i] == '\0') {
+ /* No difference */
+ return 0;
+ }
+ ++i;
+ }
+
+ /* Count backwards and find the leftmost digit */
+ j = i;
+ while (j > 0 && isdigit((unsigned char)a[j-1])) {
+ --j;
+ }
+
+ /* Determine mode of comparison */
+ if (a[j] == '0' || b[j] == '0') {
+ /* Find the next non-zero digit */
+ while (a[j] == '0' && a[j] == b[j]) {
+ j++;
+ }
+
+ /* String with more digits is smaller, e.g 002 < 01 */
+ if (isdigit((unsigned char)a[j])) {
+ if (!isdigit((unsigned char)b[j])) {
+ return -1;
+ }
+ } else if ((unsigned char)isdigit(b[j])) {
+ return 1;
+ }
+ } else if ((unsigned char)isdigit(a[j]) &&
+ isdigit((unsigned char)b[j])) {
+ /* Numeric comparison */
+ size_t k1 = j;
+ size_t k2 = j;
+
+ /* Compute number of digits in each string */
+ while (isdigit((unsigned char)a[k1])) {
+ k1++;
+ }
+ while (isdigit((unsigned char)b[k2])) {
+ k2++;
+ }
+
+ /* Number with more digits is bigger, e.g 999 < 1000 */
+ if (k1 < k2)
+ return -1;
+ else if (k1 > k2)
+ return 1;
+ }
+
+ /* Alphabetical comparison */
+ return (int) ((unsigned char) a[i]) - ((unsigned char) b[i]);
+}
+
+/* Convert multi-byte string to wide character string */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int dirent_mbstowcs_s(
+ size_t *pReturnValue, wchar_t *wcstr,
+ size_t sizeInWords, const char *mbstr, size_t count)
+{
+ /* Older Visual Studio or non-Microsoft compiler */
+ size_t n = mbstowcs(wcstr, mbstr, sizeInWords);
+ if (wcstr && n >= count)
+ return /*error*/ 1;
+
+ /* Zero-terminate output buffer */
+ if (wcstr && sizeInWords) {
+ if (n >= sizeInWords)
+ n = sizeInWords - 1;
+ wcstr[n] = 0;
+ }
+
+ /* Length of multi-byte string with zero terminator */
+ if (pReturnValue) {
+ *pReturnValue = n + 1;
+ }
+
+ /* Success */
+ return 0;
+}
+#endif
+
+/* Convert wide-character string to multi-byte string */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int dirent_wcstombs_s(
+ size_t *pReturnValue, char *mbstr,
+ size_t sizeInBytes, const wchar_t *wcstr, size_t count)
+{
+ /* Older Visual Studio or non-Microsoft compiler */
+ size_t n = wcstombs(mbstr, wcstr, sizeInBytes);
+ if (mbstr && n >= count)
+ return /*error*/1;
+
+ /* Zero-terminate output buffer */
+ if (mbstr && sizeInBytes) {
+ if (n >= sizeInBytes) {
+ n = sizeInBytes - 1;
+ }
+ mbstr[n] = '\0';
+ }
+
+ /* Length of resulting multi-bytes string WITH zero-terminator */
+ if (pReturnValue) {
+ *pReturnValue = n + 1;
+ }
+
+ /* Success */
+ return 0;
+}
+#endif
+
+/* Set errno variable */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static void dirent_set_errno(int error)
+{
+ /* Non-Microsoft compiler or older Microsoft compiler */
+ errno = error;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*DIRENT_H*/
diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4
new file mode 100644
index 000000000000..bd753b34d7dc
--- /dev/null
+++ b/m4/ax_check_compile_flag.m4
@@ -0,0 +1,53 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+# Check whether the given FLAG works with the current language's compiler
+# or gives an error. (Warnings, however, are ignored)
+#
+# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+# success/failure.
+#
+# If EXTRA-FLAGS is defined, it is added to the current language's default
+# flags (e.g. CFLAGS) when the check is done. The check is thus made with
+# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
+# force the compiler to issue an error when a bad flag is given.
+#
+# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 6
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+ _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+ AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+ [AS_VAR_SET(CACHEVAR,[yes])],
+ [AS_VAR_SET(CACHEVAR,[no])])
+ _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_VAR_IF(CACHEVAR,yes,
+ [m4_default([$2], :)],
+ [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/man/bomtool.1 b/man/bomtool.1
new file mode 100644
index 000000000000..aac6c8014ea7
--- /dev/null
+++ b/man/bomtool.1
@@ -0,0 +1,100 @@
+.\" Copyright (c) 2025 pkgconf authors (see AUTHORS).
+.\"
+.\" Permission to use, copy, modify, and/or distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" This software is provided 'as is' and without any warranty, express or
+.\" implied. In no event shall the authors be liable for any damages arising
+.\" from the use of this software.
+.Dd June 4, 2025
+.Dt BOMTOOL 1
+.Os
+.Sh NAME
+.Nm bomtool
+.Nd a tool for generating SPDX-based software bills of material
+.Sh SYNOPSIS
+.Nm
+.Op Ar options
+.Ar module ...
+.Sh DESCRIPTION
+.Nm
+is a program which generates a textual SPDX 2.0 software bill of
+materials (SBOM) for a given set of pkg-config modules.
+The output of this tool can then be translated into other SBOM
+formats as necessary.
+.Pp
+The
+.Ar options
+are as follows:
+.Bl -tag -width indent
+.It Fl -about
+Print the version number, the Copyright notice, and the license of the
+.Nm
+program to standard output and exit.
+Most other options and all command line arguments are ignored.
+.It Fl -version
+Print the version number of the
+.Nm
+program to standard output and exit.
+Most other options and all command line arguments are ignored.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width indent
+.It Ev PKG_CONFIG_DEBUG_SPEW
+If set, print debugging messages to stderr.
+.It Ev PKG_CONFIG_IGNORE_CONFLICTS
+If set, ignore
+.Ic Conflicts
+rules in modules.
+Has the same effect as the
+.Fl -ignore-conflicts
+option in
+.Xr pkgconf 1
+.
+.It Ev PKG_CONFIG_LIBDIR
+A colon-separated list of low-priority directories where
+.Xr pc 5
+files are looked up.
+The module search path is constructed by appending this list to
+.Ev PKG_CONFIG_PATH ,
+which enjoys higher priority.
+If
+.Ev PKG_CONFIG_LIBDIR
+is not defined, the default list compiled into the
+.Nm
+program from the
+.Dv PKG_DEFAULT_PATH
+preprocessor macro is appended instead.
+If
+.Ev PKG_CONFIG_LIBDIR
+is defined but empty, nothing is appended.
+.It Ev PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH
+Impose a limit on the allowed depth in the dependency graph.
+.It Ev PKG_CONFIG_PATH
+A colon-separated list of high-priority directories where
+.Xr pc 5
+files are looked up.
+.It Ev PKG_CONFIG_PRELOADED_FILES
+Colon-separated list of
+.Xr pc 5
+files which are loaded before any other pkg-config files.
+These packages are given highest priority over any other
+.Xr pc 5
+files that would otherwise provide a given package.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Generating an SBOM for the package named foo:
+.Dl $ bomtool foo
+.Dl SPDXVersion: SPDX-2.2
+.Dl DataLicense: CC0-1.0
+.Dl SPDXID: SPDXRef-DOCUMENT
+.Dl DocumentName: SBOM-SPDX-fooC641.2.3
+.Dl DocumentNamespace: https://spdx.org/spdxdocs/bomtool-2.4.3
+.Dl Creator: Tool: bomtool 2.4.3
+.Dl [...]
+.Sh SEE ALSO
+.Xr pc 5 ,
+.Xr pkgconf 1
diff --git a/man/pc.5 b/man/pc.5
new file mode 100644
index 000000000000..2aee7462b073
--- /dev/null
+++ b/man/pc.5
@@ -0,0 +1,178 @@
+.\" Copyright (c) 2017 pkgconf authors (see AUTHORS).
+.\"
+.\" Permission to use, copy, modify, and/or distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" This software is provided 'as is' and without any warranty, express or
+.\" implied. In no event shall the authors be liable for any damages arising
+.\" from the use of this software.
+.Dd December 15, 2017
+.Dt PC 5
+.Os
+.Sh NAME
+.Nm file.pc
+.Nd pkg-config file format
+.Sh DESCRIPTION
+pkg-config files provide a useful mechanism for storing various information
+about libraries and packages on a given system.
+Information stored by
+.Nm .pc
+files include compiler and linker flags necessary to use a given library, as
+well as any other relevant metadata.
+.Pp
+These
+.Nm .pc
+files are processed by a utility called
+.Nm pkg-config ,
+of which
+.Nm pkgconf
+is an implementation.
+.\"
+.Ss FILE SYNTAX
+The
+.Nm .pc
+file follows a format inspired by RFC822.
+Comments are prefixed by a pound sign, hash sign or octothorpe (#), and variable
+assignment is similar to POSIX shell.
+Properties are defined using RFC822-style stanzas.
+.\"
+.Ss VARIABLES
+.\"
+Variable definitions start with an alphanumeric string, followed by an equal sign,
+and then the value the variable should contain.
+.Pp
+Variable references are always written as "${variable}".
+It is possible to escape literal "${" as "$${".
+.\"
+.Ss PROPERTIES
+.\"
+Properties are set using RFC822-style stanzas which consist of a keyword, followed
+by a colon (:) and then the value the property should be set to.
+Variable substitution is always performed regardless of property type.
+.Pp
+There are three types of property:
+.\"
+.Bl -tag -width indent
+.\"
+.It Literal
+The property will be set to the text of the value.
+.\"
+.It Dependency List
+The property will be set to a list of dependencies parsed from the
+text.
+Dependency lists are defined by this ABNF syntax:
+.Bd -literal
+package-list = *WSP *( package-spec *( package-sep ) )
+package-sep = WSP / ","
+.\"
+package-spec = package-key [ ver-op package-version ]
+ver-op = "<" / "<=" / "=" / "!=" / ">=" / ">"
+.Ed
+.\"
+.It Fragment List
+The property will be set to a list of fragments parsed from the text.
+The input text must be in a format that is suitable for passing to a POSIX
+shell without any shell expansions after variable substitution has been done.
+.\"
+.El
+.Ss PROPERTY KEYWORDS
+.Bl -tag -width indent
+.\"
+.It Name
+The displayed name of the package.
+(mandatory; literal)
+.It Version
+The version of the package.
+(mandatory; literal)
+.It Description
+A description of the package.
+(mandatory; literal)
+.It URL
+A URL to a webpage for the package.
+This is used to recommend where newer versions of the package can be acquired.
+(mandatory; literal)
+.It Cflags
+Required compiler flags.
+These flags are always used, regardless of whether static compilation is requested.
+(optional; fragment list)
+.It Cflags.private
+Required compiler flags for static compilation.
+(optional; fragment list; pkgconf extension)
+.It Copyright
+A copyright attestation statement.
+(optional; literal; pkgconf extension)
+.It Libs
+Required linking flags for this package.
+Libraries this package depends on for linking against it, which are not
+described as dependencies should be specified here.
+(optional; fragment list)
+.It Libs.private
+Required linking flags for this package that are only required when linking
+statically.
+Libraries this package depends on for linking against it statically, which are
+not described as dependencies should be specified here.
+(optional; fragment list)
+.It License
+The asserted SPDX license tag that should be applied to the given package.
+(optional; literal; pkgconf extension)
+.It Maintainer
+The preferred contact for the maintainer. This should be in the format of a
+name followed by an e-mail address or website.
+(optional; literal; pkgconf extension)
+.It Requires
+Required dependencies that must be met for the package to be usable.
+All dependencies must be satisfied or the pkg-config implementation must not use
+the package.
+(optional; dependency list)
+.It Requires.private
+Required dependencies that must be met for the package to be usable for header
+inclusion and static linking.
+All dependencies must be satisfied or the pkg-config implementation must not use
+the package for header inclusion and static linking.
+(optional; dependency list)
+.It Conflicts
+Dependencies that must not be met for the package to be usable.
+If any package in the proposed dependency solution match any dependency in the
+Conflicts list, the package being considered is not usable.
+(optional; dependency list)
+.It Provides
+Dependencies that may be provided by an alternate package.
+If a package cannot be found, the entire package collection is scanned for
+providers which can match the requested dependency.
+(optional; dependency list; pkgconf extension)
+.El
+.Ss EXTENSIONS
+Features that have been marked as a pkgconf extension are only guaranteed to work
+with the pkgconf implementation of pkg-config.
+Other implementations may or may not support the extensions.
+.Pp
+Accordingly, it is suggested that
+.Nm .pc
+files which absolutely depend on these extensions declare a requirement on the
+pkgconf virtual.
+.Sh EXAMPLES
+An example .pc file:
+.Bd -literal
+# This is a comment
+prefix=/home/kaniini/pkg # this defines a variable
+exec_prefix=${prefix} # defining another variable with a substitution
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: libfoo # human-readable name
+Description: an example library called libfoo # human-readable description
+Copyright: Copyright (c) 2022 pkgconf project authors
+License: Apache-2.0
+Maintainer: the pkgconf project <http://www.pkgconf.org>
+Version: 1.0
+URL: http://www.pkgconf.org
+Requires: libbar > 2.0.0
+Conflicts: libbaz <= 3.0.0
+Libs: -L${libdir} -lfoo
+Libs.private: -lm
+Cflags: -I${includedir}/libfoo
+.Ed
+.Sh SEE ALSO
+.Xr pkgconf 1 ,
+.Xr pkg.m4 7
diff --git a/man/pkg.m4.7 b/man/pkg.m4.7
new file mode 100644
index 000000000000..f9210700f8f3
--- /dev/null
+++ b/man/pkg.m4.7
@@ -0,0 +1,143 @@
+.\" Copyright (c) 2017 pkgconf authors (see AUTHORS).
+.\"
+.\" Permission to use, copy, modify, and/or distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" This software is provided 'as is' and without any warranty, express or
+.\" implied. In no event shall the authors be liable for any damages arising
+.\" from the use of this software.
+.Dd December 5, 2017
+.Dt PKG.M4 7
+.Os
+.Sh NAME
+.Nm pkg.m4
+.Nd autoconf macros for using pkgconf
+.Sh SYNOPSIS
+.Nm PKG_PREREQ
+.Nm PKG_PROG_PKG_CONFIG
+.Nm PKG_CHECK_MODULES
+.Nm PKG_CHECK_MODULES_STATIC
+.Nm PKG_INSTALLDIR
+.Nm PKG_NOARCH_INSTALLDIR
+.Nm PKG_CHECK_VAR
+.Nm PKG_WITH_MODULES
+.Nm PKG_HAVE_WITH_MODULES
+.Nm PKG_HAVE_DEFINE_WITH_MODULES
+.Sh DESCRIPTION
+.Nm
+is a collection of autoconf macros which help to configure compiler and linker
+flags for development libraries.
+This allows build systems to detect other dependencies and use them with the
+system toolchain.
+.Sh "AUTOCONF MACROS"
+.Ss "PKG_PREREQ(MIN-VERSION)"
+Checks that the version of the
+.Nm
+autoconf macros in use is at least MIN-VERSION.
+This can be used to ensure a particular
+.Nm
+macro will be available.
+.Ss "PKG_PROG_PKG_CONFIG([MIN-VERSION])"
+Checks for an implementation of
+.Nm pkg-config
+which is at least MIN-VERSION or newer.
+.Ss "PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES [,ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]])"
+.Ss "PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES [,ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]])"
+Checks whether a given module set exists, and if so, defines
+.Nm CFLAGS
+and
+.Nm LIBS
+variables prefixed by
+.Nm VARIABLE-PREFIX
+with the output from
+.Fl -cflags
+and
+.Fl -libs
+respectively.
+.Pp
+The optional
+.Nm ACTION-IF-FOUND
+and
+.Nm ACTION-IF-NOT-FOUND
+arguments are shell fragments that should be executed if the module set is
+found or not found.
+.Pp
+If
+.Nm $PKG_CONFIG
+is not defined, the
+.Nm PKG_PROG_PKG_CONFIG
+macro will be executed to locate a
+.Nm pkg-config
+implementation.
+.Pp
+The
+.Nm PKG_CHECK_MODULES_STATIC
+macro provides the same behaviour as
+.Nm PKG_CHECK_MODULES
+with static linking enabled via the
+.Fl -static
+flag.
+.Ss "PKG_INSTALLDIR(DIRECTORY)"
+Defines the variable $pkgconfigdir as the location where a package
+should install pkg-config .pc files.
+.Pp
+By default the directory is $libdir/pkgconfig, but the default can
+be changed by passing the
+.Nm DIRECTORY
+parameter.
+.Pp
+This value can be overridden with the
+.Fl -with-pkgconfigdir
+configure parameter.
+.Ss "PKG_NOARCH_INSTALLDIR(DIRECTORY)"
+Defines the variable $noarch_pkgconfigdir as the location where a package
+should install pkg-config .pc files.
+.Pp
+By default the directory is $datadir/pkgconfig, but the default can
+be changed by passing the
+.Nm DIRECTORY
+parameter.
+.Pp
+This value can be overridden with the
+.Fl -with-noarch-pkgconfigdir
+configure parameter.
+.Ss "PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])"
+Retrieves the value of the
+.Nm pkg-config
+variable
+.Nm CONFIG-VARIABLE
+from
+.Nm MODULE
+and stores it in the
+.Nm VARIABLE
+variable.
+.Pp
+Note that repeated usage of
+.Nm VARIABLE
+is not recommended as the check will be skipped if the variable is
+already set.
+.Ss "PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], [DESCRIPTION], [DEFAULT])"
+Prepares a "--with-" configure option using the lowercase
+.Nm VARIABLE-PREFIX
+name, merging the behaviour of
+.Nm AC_ARG_WITH
+and
+.Nm PKG_CHECK_MODULES
+in a single macro.
+.Ss "PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, [DESCRIPTION], [DEFAULT])"
+Convenience macro to trigger
+.Nm AM_CONDITIONAL
+after a
+.Nm PKG_WITH_MODULES check.\&
+.Nm VARIABLE-PREFIX
+is exported as a make variable.
+.Ss "PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, [DESCRIPTION], [DEFAULT])"
+Convenience macro to trigger
+.Nm AM_CONDITIONAL
+and
+.Nm AC_DEFINE
+after a
+.Nm PKG_WITH_MODULES check.\&
+.Nm VARIABLE-PREFIX
+is exported as a make variable.
diff --git a/man/pkgconf-personality.5 b/man/pkgconf-personality.5
new file mode 100644
index 000000000000..eee70b41cf0f
--- /dev/null
+++ b/man/pkgconf-personality.5
@@ -0,0 +1,100 @@
+.\" Copyright (c) 2018 pkgconf authors (see AUTHORS).
+.\"
+.\" Permission to use, copy, modify, and/or distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" This software is provided 'as is' and without any warranty, express or
+.\" implied. In no event shall the authors be liable for any damages arising
+.\" from the use of this software.
+.Dd July 19, 2018
+.Dt PKGCONF-PERSONALITY 5
+.Os
+.Sh NAME
+.Nm file.personality
+.Nd pkgconf cross-compile personality file format
+.Sh DESCRIPTION
+pkgconf cross-compile personality files provide a useful mechanism for storing
+various information about system toolchains.
+Information stored by
+.Nm .personality
+files include information about paths used by a cross-compile toolchain, such as
+the sysroot directory and default include and library paths. pkgconf uses this
+information to determine what information is necessary to use libraries.
+.\"
+.Ss FILE SYNTAX
+The
+.Nm .personality
+file follows a format inspired by RFC822.
+Comments are prefixed by a pound sign, hash sign or octothorpe (#), and variable
+assignment is similar to POSIX shell.
+Properties are defined using RFC822-style stanzas.
+.\"
+.Ss PROPERTIES
+.\"
+Properties are set using RFC822-style stanzas which consist of a keyword, followed
+by a colon (:) and then the value the property should be set to.
+Variable substitution is always performed regardless of property type.
+.Pp
+There are three types of property:
+.\"
+.Bl -tag -width indent
+.\"
+.It Literal
+The property will be set to the text of the value.
+.\"
+.It Fragment List
+The property will be set to a list of fragments parsed from the text.
+The input text must be in a format that is suitable for passing to a POSIX
+shell without any shell expansions after variable substitution has been done.
+Elements are delimited with a colon.
+.\"
+.It Boolean
+The property will be set to true if the value is one of: true, yes or 1.
+Otherwise it will be set to false.
+.\"
+.El
+.Ss PROPERTY KEYWORDS
+.Bl -tag -width indent
+.\"
+.It Triplet
+The triplet used by the cross-compile toolchain.
+(mandatory; literal)
+.It SysrootDir
+The directory used by the system root of the cross-compile toolchain.
+(mandatory; literal)
+.It DefaultSearchPaths
+A list of directories to look for
+.Xr pc 5
+files in.
+(mandatory; fragment list)
+.It SystemIncludePaths
+A list of directories that are included by default in the search path for
+include files.
+(mandatory; fragment list)
+.It SystemLibraryPaths
+A list of directories that are included by default in the search path for
+libraries.
+(mandatory; fragment list)
+.It WantDefaultPure
+If true, pkgconf will default to preferring a pure dependency graph.
+(optional; boolean; default is false)
+.It WantDefaultStatic
+If true, pkgconf will default to operating in static linking mode.
+(optional; boolean; default is false)
+.\"
+.El
+.Sh EXAMPLES
+An example .personality file:
+.Bd -literal
+# This is a comment
+Triplet: x86_64-pc-linux-gnu
+SysrootDir: /home/kaniini/sysroot/x86_64-pc-linux-gnu
+DefaultSearchPaths: /home/kaniini/sysroot/x86_64-pc-linux-gnu/lib/pkgconfig:/home/kaniini/sysroot/x86_64-pc-linux-gnu/share/pkgconfig
+SystemIncludePaths: /home/kaniini/sysroot/x86_64-pc-linux-gnu/include
+SystemLibraryPaths: /home/kaniini/sysroot/x86_64-pc-linux-gnu/lib
+.Ed
+.Sh SEE ALSO
+.Xr pkgconf 1 ,
+.Xr pc 5 ,
+.Xr pkg.m4 7
diff --git a/man/pkgconf.1 b/man/pkgconf.1
new file mode 100644
index 000000000000..df286a60593a
--- /dev/null
+++ b/man/pkgconf.1
@@ -0,0 +1,758 @@
+.\" Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 pkgconf authors (see AUTHORS).
+.\"
+.\" Permission to use, copy, modify, and/or distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" This software is provided 'as is' and without any warranty, express or
+.\" implied. In no event shall the authors be liable for any damages arising
+.\" from the use of this software.
+.Dd November 15, 2016
+.Dt PKGCONF 1
+.Os
+.Sh NAME
+.Nm pkgconf
+.Nd a system for configuring build dependency information
+.Sh SYNOPSIS
+.Nm
+.Op Ar options
+.Ar module ...
+.Sh DESCRIPTION
+The
+.Nm
+program retrieves configuration information related to the
+.Ar module
+arguments from
+.Xr pc 5
+files installed on the system and prints parts of the retrieved
+information depending on the specified
+.Ar options .
+The most common use is printing the compiler and linker flags needed
+to build software that uses the libraries given by the
+.Ar module
+arguments.
+.Pp
+The
+.Xr pc 5
+files are searched for along a path constructed from the
+.Fl -with-path
+option, the
+.Ev PKG_CONFIG_PATH
+and
+.Ev PKG_CONFIG_LIBDIR
+environment variables, and some compiled-in default directories.
+The
+.Ar module
+arguments correspond to the file names, but without the
+.Pa .pc
+filename extension.
+.Pp
+Several of the
+.Ar options
+cause immediate exit.
+If multiple of these options are given, only the option with the
+highest priority takes effect and those with lower priority are
+silently ignored.
+These options are, ordered by descending priority:
+.Bl -enum
+.It
+No-module options:
+.Fl -relocate ,
+.Fl -dump-personality ,
+.Fl -about ,
+.Fl -version ,
+.Fl -help ,
+.Fl -atleast-pkgconfig-version ,
+.Fl -list-all ,
+and
+.Fl -list-package-names :
+These options cause all arguments to be ignored.
+.It
+Argument-only options:
+.Fl -atleast-version ,
+.Fl -exact-version ,
+and
+.Fl -max-version :
+These options only inspect modules explicitly specified on the
+command line and do not look at dependencies.
+.It
+Limited-output options:
+.Fl -validate ,
+.Fl -license ,
+.Fl -uninstalled ,
+and
+.Fl -env :
+These options perform dependency resolution, but exit after printing
+the information requested by the highest-priority option,
+ignoring other output options that may have been specified.
+.El
+.Pp
+Several other options require at least one
+.Ar module
+argument, produce output, do not cause early exit, can be combined
+with each other, but override and disable all
+.Fl -cflags
+and
+.Fl -libs
+options:
+.Bl -enum
+.It
+Single-module output options:
+.Fl -path ,
+.Fl -print-variables ,
+.Fl -variable :
+If any of these options is specified, only the first
+.Ar module
+argument is used, all other arguments are silently ignored,
+and no dependency resolution is attempted.
+.It
+Depth-one output options:
+.Fl -print-provides ,
+.Fl -modversion ,
+.Fl -print-requires ,
+and
+.Fl -print-requires-private :
+If any of these options is specified, only modules
+explicitly specified on the command line are inspected
+and no dependency resolution is attempted.
+.It
+General output options:
+.Fl -simulate ,
+.Fl -digraph ,
+.Fl -solution ,
+.Fl -fragment-tree :
+These options do not limit dependency resolution.
+.El
+.Pp
+The most important output options
+.Fl -cflags
+and
+.Fl -libs
+can be combined with each other, but are overridden and ignored if
+any of the options listed above are specified.
+.Pp
+The complete list of
+.Ar options
+is as follows:
+.Bl -tag -width indent
+.It Fl -about
+Print the version number, the Copyright notice, and the license of the
+.Nm
+program to standard output and exit.
+Most other options and all command line arguments are ignored.
+.It Fl -atleast-pkgconfig-version Ns = Ns Ar version
+Exit with error if the requested
+.Ar version
+number is greater than the version number of the
+.Nm
+program, or with success otherwise.
+Most other options and all command line arguments are ignored.
+.It Fl -atleast-version Ns = Ns Ar version
+Check the
+.Ar module
+arguments in the given order.
+Exit with error as soon as a
+.Ar module
+does not exist, and exit with success as soon as the version number of a
+.Ar module
+is greater than or equal to the requested
+.Ar version
+number.
+Exit with error if the version number of each
+.Ar module
+is less than the requested
+.Ar version
+number.
+.It Fl -cflags , Fl -cflags-only-I , Fl -cflags-only-other
+Print all compiler flags required to compile against the
+.Ar module ,
+or only the include path
+.Pq Fl I
+flags, or only the compiler flags that are not include path flags,
+respectively.
+These options imply
+.Fl -print-errors .
+.It Fl -debug
+Print some non-fatal warning messages to standard error output
+that would otherwise silently be ignored.
+This option also implies
+.Fl -print-errors .
+If
+.Nm
+was compiled without defining the preprocessor macro
+.Dv PKGCONF_LITE ,
+this option also prints many debugging messages to standard error output.
+.It Fl -define-prefix
+Attempts to determine the prefix variable to use for CFLAGS and LIBS entry relocations.
+This is mainly useful for platforms where framework SDKs are relocatable, such as Windows.
+.It Fl -define-variable Ns = Ns Ar varname Ns = Ns Ar value
+Define
+.Ar varname
+as
+.Ar value .
+Variables are used in query output, and some modules' results may change based
+on the presence of a variable definition.
+.It Fl -digraph
+Dump the dependency resolver's solution as a graphviz
+.Sq dot
+file.
+This can be used with graphviz to visualize module interdependencies.
+This option is only available if the preprocessor macro
+.Dv PKGCONF_LITE
+was not defined during compilation.
+.It Fl -dont-define-prefix
+Disables the
+.Sq define-prefix
+feature.
+.It Fl -dont-relocate-paths
+Disables the path relocation feature.
+.It Fl -dump-personality
+Print some default settings to standard output, in particular
+the default module search path that is used when
+.Ev PKG_CONFIG_LIBDIR
+is not defined, the default list of include paths that are filtered out when
+.Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH
+is not defined,
+and the default list of library paths that are filtered out when
+.Ev PKG_CONFIG_SYSTEM_LIBRARY_PATH
+is not defined, and exit.
+Most other options and all command line arguments are ignored.
+This option is only available if the preprocessor macro
+.Dv PKGCONF_LITE
+was not defined during compilation.
+.It Fl -env Ns = Ns Ar varname
+Print the requested values as variable declarations in a similar format as the
+.Xr env 1
+command.
+.It Fl -env-only
+Initialize the module search path from
+.Fl -with-path
+and
+.Ev PKG_CONFIG_PATH
+only, ignoring
+.Ev PKG_CONFIG_LIBDIR
+and the compiled-in default directories.
+.It Fl -errors-to-stdout
+Print all error, warning, and debugging messages to standard output
+instead of to standard error output.
+.It Fl -exact-version Ns = Ns Ar version
+Check the
+.Ar module
+arguments in the given order.
+Exit with error as soon as a
+.Ar module
+does not exist, and exit with success as soon as the version number of a
+.Ar module
+is exactly the requested
+.Ar version
+number.
+Exit with error if the version number of each
+.Ar module
+differs from the requested
+.Ar version
+number.
+.It Fl -exists
+Exit with a non-zero exit status
+if the dependency resolver is unable to find all of the requested
+.Ar module Ns s .
+This option is active by default and cannot be disabled.
+However, various other options cause
+.Nm
+to exit and report success or failure before all arguments have been inspected.
+.It Fl -fragment-filter Ns = Ns Ar types
+Filter the fragment lists for the specified
+.Ar types .
+.It Fl -help
+Print a usage summary on standard output and exit.
+Most other options and all command line arguments are ignored.
+.It Fl -ignore-conflicts
+Ignore
+.Sq Conflicts
+rules in modules.
+.It Fl -keep-system-cflags , Fl -keep-system-libs
+Keep CFLAGS or linker flag fragments that would be filtered due to being
+included by default in the compiler.
+.It Fl -libs , Fl -libs-only-L , Fl -libs-only-l , Fl -libs-only-other
+Print all linker flags required to link against the
+.Ar module ,
+or only the library path
+.Pq Fl L
+flags, or only the library
+.Pq Fl l
+flags, or only the linker flags that are neither library path
+nor library flags, respectively.
+These options imply
+.Fl -print-errors .
+.It Fl -list-all
+Walk the module search path in the order of descending priority.
+For each
+.Xr pc 5
+file found, print one line to standard output,
+containing the basename of the file without the extension, the
+.Ic Name
+property, a dash
+.Pq Sq \- ,
+and the
+.Ic Description
+property.
+This option implies
+.Fl -print-errors .
+All command line arguments are ignored.
+.It Fl -list-package-names
+Perform the same search as
+.Fl -list-all ,
+but only print the basename of each
+.Xr pc 5
+file without the extension, not the module name and the description.
+This option implies
+.Fl -print-errors .
+All command line arguments are ignored.
+.It Fl -log-file Ns = Ns Ar file
+Set the name of the output
+.Ar file
+where information about selected modules is logged,
+both about those selected by arguments and as dependencies.
+For each selected module, one line is printed,
+containing the basename of the
+.Xr pc 5
+file without the extension, optionally an operator and version number
+describing the desired range of versions, and either the actual version
+number in square brackets or the string
+.Qq NOT-FOUND .
+If this option is not provided, the name of the output file
+is instead taken from the
+.Ev PKG_CONFIG_LOG
+environment variable, and if that is not provided either,
+this kind of logging is disabled.
+.It Fl -max-version Ns = Ns Ar version
+Check the
+.Ar module
+arguments in the given order.
+Exit with error as soon as a
+.Ar module
+does not exist, and exit with success as soon as the version number of a
+.Ar module
+is less than or equal to the requested
+.Ar version
+number.
+Exit with error if the version number of each
+.Ar module
+is greater than the requested
+.Ar version
+number.
+.It Fl -maximum-traverse-depth Ns = Ns Ar depth
+Impose a limit on the allowed depth in the dependency graph.
+For example, a
+.Ar depth
+of 2 restricts the resolver from acting on child
+dependencies of modules added to the resolver's solution.
+This option is overridden by the
+.Ev PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH
+environment variable and by the options
+.Fl -modversion ,
+.Fl -path ,
+.Fl -print-provides ,
+.Fl -print-requires ,
+.Fl -print-requires-private ,
+.Fl -print-variables ,
+and
+.Fl -variable .
+.It Fl -modversion
+For each specified
+.Ar module ,
+print the version number to standard output.
+If the
+.Fl -verbose
+option is also specified, the name of the respective
+.Ar module
+and a colon is printed before each version number.
+This option implies
+.Fl -print-errors
+and
+.Fl -maximum-traverse-depth Ns =1
+and overrides and disables all
+.Fl -cflags
+and
+.Fl -libs
+flags.
+.It Fl -msvc-syntax
+Use MSVC syntax for
+.Fl -cflags ,
+.Fl -env ,
+and
+.Fl -libs
+output.
+This option is only available if the preprocessor macro
+.Dv PKGCONF_LITE
+was not defined during compilation.
+.It Fl -no-cache
+Skip caching packages when they are loaded into the internal resolver.
+This may result in an alternate dependency graph being computed.
+.It Fl -no-provides
+Ignore
+.Sq Provides
+rules in modules when resolving dependencies.
+.It Fl -no-uninstalled
+Forbids the dependency resolver from considering 'uninstalled' modules as part
+of a solution.
+.It Fl -path
+For the first
+.Ar module
+given on the command line, let the dependency resolver find the
+.Xr pc 5
+file describing that module, print the absolute pathname of that file
+to standard output, and exit immediately,
+ignoring most other options and all other arguments.
+.It Fl -prefix-variable Ns = Ns Ar variable
+Sets the
+.Sq prefix
+variable used by the
+.Sq define-prefix
+feature.
+.It Fl -print-errors
+Print some messages about fatal errors to standard error output
+that would otherwise be omitted.
+This option is implied by many other options, but not by all.
+It can be overridden with
+.Fl -silence-errors .
+.It Fl -print-provides
+For each specified
+.Ar module ,
+print one line to standard output containing the
+.Ic Name
+property, an equal sign
+.Pq Sq = ,
+and the
+.Ic Version
+property.
+If the
+.Ar module
+contains one or more
+.Ic Provides
+properties, print additional lines in dependency list format, one name
+per line, each name optionally followed by an operator and a version.
+This option implies
+.Fl -maximum-traverse-depth Ns =1
+and overrides and disables all
+.Fl -cflags
+and
+.Fl -libs
+flags.
+.It Fl -print-requires , Fl -print-requires-private
+For each specified
+.Ar module ,
+print the
+.Ic Requires
+or
+.Ic Requires.private
+properties, respectively, in dependency list format to standard output.
+Both of these options imply
+.Fl -maximum-traverse-depth Ns =1
+and override and disable all
+.Fl -cflags
+and
+.Fl -libs
+flags.
+.It Fl -print-variables
+For the first
+.Ar module
+given on the command line, print the names of all seen variables
+to standard output, one per line.
+Any subsequent arguments are silently ignored.
+This option implies
+.Fl -print-errors
+and
+.Fl -maximum-traverse-depth Ns =1
+and overrides and disables all
+.Fl -cflags
+and
+.Fl -libs
+flags.
+.It Fl -pure
+Treats the computed dependency graph as if it were pure.
+This is mainly intended for use with the
+.Fl -static
+flag and has no effect if
+.Fl -shared
+is also specified.
+.It Fl -relocate Ns = Ns Ar path
+Relocates a path using the pkgconf_path_relocate API.
+This is mainly used by the testsuite to provide a guaranteed interface
+to the system's path relocation backend.
+.It Fl -shared
+Compute a simple dependency graph that is only suitable for shared linking.
+This option overrides
+.Fl -static .
+.It Fl -short-errors
+When printing error messages about modules that are not found
+or conflict with each other, avoid printing additional, verbose
+instructions explaining potential methods for solving the problem.
+.It Fl -silence-errors
+Do not print any error, warning, or debugging messages at all.
+Overrides all of
+.Fl -debug ,
+.Fl -errors-to-stdout ,
+and
+.Fl -print-errors .
+This option is overridden and disabled if the
+.Ev PKG_CONFIG_DEBUG_SPEW
+environment variable is set.
+.It Fl -simulate
+Simulates resolving a dependency graph based on the requested modules on the
+command line.
+Dumps a series of trees denoting pkgconf's resolver state.
+This option is only available if the preprocessor macro
+.Dv PKGCONF_LITE
+was not defined during compilation.
+.It Fl -solution
+Print the names of the modules requested with
+.Ar module
+arguments and their dependencies to standard output.
+This option is only available if the preprocessor macro
+.Dv PKGCONF_LITE
+was not defined during compilation.
+.It Fl -static
+Compute a deeper dependency graph and use compiler/linker flags intended for
+static linking.
+This option is overridden by
+.Fl -shared .
+.It Fl -uninstalled
+Exit with a non-zero result if the dependency resolver uses an
+.Sq uninstalled
+module as part of its solution.
+.It Fl -validate Ar package ...
+Validate specific
+.Sq .pc
+files for correctness.
+This option implies
+.Fl -print-errors
+and
+.Fl -errors-to-stdout .
+.It Fl -variable Ns = Ns Ar varname
+For the first
+.Ar module
+given on the command line, print the value of the variable with the name
+.Ar varname
+to standard output.
+Any subsequent arguments are silently ignored.
+This option implies
+.Fl -maximum-traverse-depth Ns =1
+and overrides and disables all
+.Fl -cflags
+and
+.Fl -libs
+flags.
+.It Fl -verbose
+This option only has an effect if
+.Fl -modversion
+is also specified.
+It prints the name of the respective
+.Ar module
+and a colon before each version number.
+.It Fl -version
+Print the version number of the
+.Nm
+program to standard output and exit.
+Most other options and all command line arguments are ignored.
+.It Fl -with-path Ns = Ns Ar path
+Prepend the directory
+.Ar path
+to the module search path,
+giving it priority over all other directories including those from
+.Ev PKG_CONFIG_PATH
+and
+.Ev PKG_CONFIG_LIBDIR .
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width indent
+.It Ev CPATH
+First supplementary colon-separated list of include paths filtered out
+in the same way as
+.Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH .
+.It Ev CPLUS_INCLUDE_PATH
+Third supplementary colon-separated list of include paths filtered out
+in the same way as
+.Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH .
+.It Ev C_INCLUDE_PATH
+Second supplementary colon-separated list of include paths filtered out
+in the same way as
+.Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH .
+.It Ev DESTDIR
+If set to the same value as
+.Ev PKG_CONFIG_SYSROOT_DIR ,
+behave in the same way as if
+.Ev PKG_CONFIG_FDO_SYSROOT_RULES
+is set.
+If
+.Ev PKG_CONFIG_SYSROOT_DIR
+is not set or set to a different value,
+.Ev DESTDIR
+is ignored.
+.It Ev LIBRARY_PATH
+Supplementary colon-separated list of library paths filtered out
+in the same way as
+.Ev PKG_CONFIG_SYSTEM_LIBRARY_PATH .
+.It Ev OBJC_INCLUDE_PATH
+Fourth supplementary colon-separated list of include paths filtered out
+in the same way as
+.Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH .
+.It Ev PKG_CONFIG_ALLOW_SYSTEM_CFLAGS
+If set, this variable has the same effect as the
+.Fl -keep-system-cflags
+option.
+.It Ev PKG_CONFIG_ALLOW_SYSTEM_LIBS
+If set, this variable has the same effect as the
+.Fl -keep-system-libs
+option.
+.It Ev PKG_CONFIG_DEBUG_SPEW
+If set, override and disable the
+.Fl -silence-errors
+option.
+.It Ev PKG_CONFIG_DISABLE_UNINSTALLED
+If set, enables the same behaviour as the
+.Fl -no-uninstalled
+flag.
+.It Ev PKG_CONFIG_DONT_DEFINE_PREFIX
+If set, this variable has the same effect as the
+.Fl -dont-define-prefix
+option.
+.It Ev PKG_CONFIG_DONT_RELOCATE_PATHS
+If set, disables the path relocation feature.
+.It Ev PKG_CONFIG_FDO_SYSROOT_RULES
+If set, follow the sysroot prefixing rules that freedesktop.org pkg-config uses.
+.It Ev PKG_CONFIG_IGNORE_CONFLICTS
+If set, ignore
+.Ic Conflicts
+rules in modules.
+Has the same effect as the
+.Fl -ignore-conflicts
+option.
+.It Ev PKG_CONFIG_LIBDIR
+A colon-separated list of low-priority directories where
+.Xr pc 5
+files are looked up.
+The module search path is constructed by appending this list to
+.Ev PKG_CONFIG_PATH ,
+which enjoys higher priority.
+If
+.Ev PKG_CONFIG_LIBDIR
+is not defined, the default list compiled into the
+.Nm
+program from the
+.Dv PKG_DEFAULT_PATH
+preprocessor macro is appended instead.
+If
+.Ev PKG_CONFIG_LIBDIR
+is defined but empty, nothing is appended.
+.It Ev PKG_CONFIG_LOG
+If set, log information about selected modules
+to the file with the name stored in this variable.
+For more details, see the
+.Fl -log-file
+command line option, which overrides this variable.
+.It Ev PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH
+Impose a limit on the allowed depth in the dependency graph.
+This variable overrides the
+.Fl -maximum-traverse-depth
+option, but is overridden by the other options mentioned there.
+.It Ev PKG_CONFIG_MSVC_SYNTAX
+If set, use MSVC syntax for
+.Fl -cflags ,
+.Fl -env ,
+and
+.Fl -libs
+output.
+This variable has the same effect as the
+.Fl -msvc-syntax
+option.
+If the preprocessor macro
+.Dv PKGCONF_LITE
+was defined during compilation, this variable is ignored.
+.It Ev PKG_CONFIG_PATH
+A colon-separated list of high-priority directories where
+.Xr pc 5
+files are looked up.
+The module search path is constructed
+by prepending the directory specified with
+.Fl -with-path ,
+if any, and unless
+.Fl -env-only
+is specified, by appending either
+.Ev PKG_CONFIG_LIBDIR
+or the compiled-in default directories with lower priority.
+.It Ev PKG_CONFIG_PRELOADED_FILES
+Colon-separated list of
+.Xr pc 5
+files which are loaded before any other pkg-config files.
+These packages are given highest priority over any other
+.Xr pc 5
+files that would otherwise provide a given package.
+.It Ev PKG_CONFIG_PURE_DEPGRAPH
+If set, enables the same behaviour as the
+.Fl -pure
+flag.
+.It Ev PKG_CONFIG_RELOCATE_PATHS
+If set, this variable has the same effect as the
+.Fl -define-prefix
+option.
+.It Ev PKG_CONFIG_SYSROOT_DIR
+If set, this variable defines a
+.Sq sysroot
+directory, which will be prepended to every path variable
+beginning with the prefix variable in a given
+.Xr pc 5
+file.
+Useful for cross compilation.
+The value of this environment variable is also copied into the global variable
+.Va pc_sysrootdir .
+.It Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH
+Colon-separated list of include paths that are filtered out
+and not printed by the
+.Fl -cflags
+and
+.Fl -cflags-only-I
+options because they are considered system include paths.
+If not defined, the default list compiled into the
+.Nm
+program from the
+.Dv SYSTEM_INCLUDEDIR
+preprocessor macro is used instead.
+This variable is a pkgconf-specific extension.
+Any directories listed in the environment variables
+.Ev CPATH ,
+.Ev C_INCLUDE_PATH ,
+.Ev CPLUS_INCLUDE_PATH ,
+and
+.Ev OBJC_INCLUDE_PATH
+are also filtered out.
+.It Ev PKG_CONFIG_SYSTEM_LIBRARY_PATH
+Colon-separated list of library paths that are filtered out
+and not printed by the
+.Fl -libs
+and
+.Fl -libs-only-L
+options because they are considered system library paths.
+If not defined, the default list compiled into the
+.Nm
+program from the
+.Dv SYSTEM_LIBDIR
+preprocessor macro is used instead.
+This variable is a pkgconf-specific extension.
+.It Ev PKG_CONFIG_TOP_BUILD_DIR
+The value of the
+.Va pc_top_builddir
+global variable.
+If this environment variable is not defined, the string
+.Qq $(top_builddir)
+is used as the value of
+.Va pc_top_builddir .
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Displaying the CFLAGS of a package:
+.Dl $ pkgconf --cflags foo
+.Dl -fPIC -I/usr/include/foo
+.Sh SEE ALSO
+.Xr pc 5 ,
+.Xr pkg.m4 7
diff --git a/meson.build b/meson.build
new file mode 100644
index 000000000000..30928a04c510
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,199 @@
+project('pkgconf', 'c',
+ version : '2.5.1',
+ license : 'ISC',
+ default_options : ['c_std=c99'],
+ meson_version: '>=0.52',
+)
+
+cc = meson.get_compiler('c')
+
+add_project_arguments(
+ '-D_BSD_SOURCE',
+ '-D_DEFAULT_SOURCE',
+ '-D_POSIX_C_SOURCE=200809L',
+ cc.get_supported_arguments(
+ '-Wimplicit-function-declaration',
+ '-Wmisleading-indentation',
+ ),
+ language : 'c',
+)
+
+cdata = configuration_data()
+
+check_functions = [
+ ['strlcat', 'string.h'],
+ ['strlcpy', 'string.h'],
+ ['strndup', 'string.h'],
+ ['strdup', 'string.h'],
+ ['strncasecmp', 'strings.h'],
+ ['strcasecmp', 'strings.h'],
+ ['reallocarray', 'stdlib.h'],
+ ['pledge', 'unistd.h'],
+ ['unveil', 'unistd.h'],
+]
+
+foreach f : check_functions
+ name = f[0].to_upper().underscorify()
+ if cc.has_function(f[0], prefix : '#define _BSD_SOURCE\n#define _DEFAULT_SOURCE\n#define _POSIX_C_SOURCE 200809L\n#include <@0@>'.format(f[1])) and cc.has_header_symbol(f[1], f[0], prefix : '#define _BSD_SOURCE\n#define _DEFAULT_SOURCE\n#define _POSIX_C_SOURCE 200809L')
+ cdata.set('HAVE_@0@'.format(name), 1)
+ cdata.set('HAVE_DECL_@0@'.format(name), 1)
+ else
+ cdata.set('HAVE_DECL_@0@'.format(name), 0)
+ endif
+endforeach
+
+default_path = []
+foreach f : ['libdir', 'datadir']
+ default_path += [join_paths(get_option('prefix'), get_option(f), 'pkgconfig')]
+endforeach
+
+personality_path = []
+foreach f : ['libdir', 'datadir']
+ personality_path += [join_paths(get_option('prefix'), get_option(f), 'pkgconfig', 'personality.d')]
+endforeach
+
+SYSTEM_LIBDIR = get_option('with-system-libdir')
+if SYSTEM_LIBDIR != ''
+ cdata.set_quoted('SYSTEM_LIBDIR', SYSTEM_LIBDIR)
+else
+ cdata.set_quoted('SYSTEM_LIBDIR', join_paths(get_option('prefix'), get_option('libdir')))
+endif
+SYSTEM_INCLUDEDIR = get_option('with-system-includedir')
+if SYSTEM_INCLUDEDIR != ''
+ cdata.set_quoted('SYSTEM_INCLUDEDIR', SYSTEM_INCLUDEDIR)
+else
+ cdata.set_quoted('SYSTEM_INCLUDEDIR', join_paths(get_option('prefix'), get_option('includedir')))
+endif
+cdata.set_quoted('PKG_DEFAULT_PATH', ':'.join(default_path))
+cdata.set_quoted('PERSONALITY_PATH', ':'.join(personality_path))
+cdata.set_quoted('PACKAGE_NAME', meson.project_name())
+cdata.set_quoted('PACKAGE_VERSION', meson.project_version())
+cdata.set_quoted('PACKAGE_BUGREPORT', 'https://todo.sr.ht/~kaniini/pkgconf')
+cdata.set('abs_top_srcdir', meson.current_source_dir())
+cdata.set('abs_top_builddir', meson.current_build_dir())
+
+
+subdir('libpkgconf')
+
+libtype = get_option('default_library')
+if libtype == 'static'
+ build_static = '-DPKGCONFIG_IS_STATIC'
+else
+ build_static = '-DPKGCONFIG_IS_NOT_STATIC'
+endif
+
+libpkgconf = library('pkgconf',
+ 'libpkgconf/argvsplit.c',
+ 'libpkgconf/audit.c',
+ 'libpkgconf/buffer.c',
+ 'libpkgconf/bsdstubs.c',
+ 'libpkgconf/cache.c',
+ 'libpkgconf/client.c',
+ 'libpkgconf/dependency.c',
+ 'libpkgconf/fileio.c',
+ 'libpkgconf/fragment.c',
+ 'libpkgconf/parser.c',
+ 'libpkgconf/path.c',
+ 'libpkgconf/personality.c',
+ 'libpkgconf/pkg.c',
+ 'libpkgconf/queue.c',
+ 'libpkgconf/tuple.c',
+ c_args: ['-DLIBPKGCONF_EXPORT', build_static],
+ install : true,
+ version : '7.0.0',
+ soversion : '7',
+)
+
+# For other projects using libpkgconfig as a subproject
+dep_libpkgconf = declare_dependency(
+ link_with : libpkgconf,
+ include_directories : include_directories('.'),
+)
+
+# If we have a new enough meson override the dependency so that only
+# `dependency('libpkgconf')` is required from the consumer
+if meson.version().version_compare('>= 0.54.0')
+ meson.override_dependency('libpkgconf', dep_libpkgconf)
+endif
+
+pkg = import('pkgconfig')
+pkg.generate(libpkgconf,
+ name : 'libpkgconf',
+ description : 'a library for accessing and manipulating development framework configuration',
+ url: 'http://github.com/pkgconf/pkgconf',
+ filebase : 'libpkgconf',
+ subdirs: ['pkgconf'],
+ extra_cflags : build_static
+)
+
+cli_include = include_directories('cli')
+
+pkgconf_exe = executable('pkgconf',
+ 'cli/main.c',
+ 'cli/getopt_long.c',
+ 'cli/renderer-msvc.c',
+ link_with : libpkgconf,
+ c_args : build_static,
+ include_directories : cli_include,
+ install : true)
+
+bomtool_exe = executable('bomtool',
+ 'cli/bomtool/main.c',
+ 'cli/getopt_long.c',
+ link_with : libpkgconf,
+ c_args : build_static,
+ include_directories : cli_include,
+ install : true)
+
+with_tests = get_option('tests')
+kyua_exe = find_program('kyua', required : with_tests, disabler : true, native : true)
+atf_sh_exe = find_program('atf-sh', required : with_tests, disabler : true, native : true)
+kyuafile = configure_file(input : 'Kyuafile.in', output : 'Kyuafile', configuration : cdata)
+test('kyua', kyua_exe, args : ['--config=none', 'test', '--kyuafile', kyuafile, '--build-root', meson.current_build_dir()])
+subdir('tests')
+
+install_man('man/bomtool.1')
+install_man('man/pkgconf.1')
+install_man('man/pkg.m4.7')
+install_man('man/pc.5')
+install_man('man/pkgconf-personality.5')
+install_data('pkg.m4', install_dir: 'share/aclocal')
+install_data('AUTHORS', install_dir: 'share/doc/pkgconf')
+install_data('README.md', install_dir: 'share/doc/pkgconf')
+
+if host_machine.system() == 'windows'
+ conf_data = configuration_data()
+ conf_data.set('VERSION', meson.project_version())
+ conf_data.set('EXE', pkgconf_exe.full_path())
+ conf_data.set('DLL', libpkgconf.full_path())
+ if host_machine.cpu() != 'x86_64'
+ wixl_arch = 'x86'
+ else
+ wixl_arch = 'x64'
+ endif
+ conf_data.set('WIXL_ARCH', wixl_arch)
+
+ python = find_program('python3')
+ wixl = find_program('wixl', required: false, version: '>= 0.105')
+ msi_filename = 'pkgconf-@0@-@1@.msi'.format(wixl_arch, meson.project_version())
+
+ wxsfile = configure_file(input: 'pkgconf.wxs.in', output: 'pkgconf.wxs', configuration: conf_data)
+
+ if wixl.found()
+ licensefile = custom_target(
+ 'License.rtf',
+ input: 'COPYING',
+ output: 'License.rtf',
+ command: [python, files('txt2rtf.py'), '@INPUT@', '@OUTPUT@'],
+ )
+
+ msi = custom_target(
+ msi_filename,
+ input: [wxsfile, licensefile, pkgconf_exe],
+ output: msi_filename,
+ command: [wixl, '--arch', wixl_arch, '--ext', 'ui', '-o', msi_filename, wxsfile],
+ )
+
+ alias_target('msi', msi)
+ endif
+endif
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 000000000000..e1360520bee6
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,19 @@
+option(
+ 'tests',
+ type: 'feature',
+ description: 'Build tests which depends upon the kyua framework',
+)
+
+option(
+ 'with-system-libdir',
+ type: 'string',
+ value: '',
+ description: 'Specify the system library directory (default {prefix}/{libdir})'
+)
+
+option(
+ 'with-system-includedir',
+ type: 'string',
+ value: '',
+ description: 'Specify the system include directory (default {prefix}/{includedir})'
+)
diff --git a/pkg.m4 b/pkg.m4
new file mode 100644
index 000000000000..ec5a70da38f5
--- /dev/null
+++ b/pkg.m4
@@ -0,0 +1,350 @@
+# pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*-
+# serial 13 (pkgconf)
+
+dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, see <https://www.gnu.org/licenses/>.
+dnl
+dnl As a special exception to the GNU General Public License, if you
+dnl distribute this file as part of a program that contains a
+dnl configuration script generated by Autoconf, you may include it under
+dnl the same distribution terms that you use for the rest of that
+dnl program.
+
+dnl PKG_PREREQ(MIN-VERSION)
+dnl -----------------------
+dnl Since: 0.29
+dnl
+dnl Verify that the version of the pkg-config macros are at least
+dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
+dnl installed version of pkg-config, this checks the developer's version
+dnl of pkg.m4 when generating configure.
+dnl
+dnl To ensure that this macro is defined, also add:
+dnl m4_ifndef([PKG_PREREQ],
+dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
+dnl
+dnl See the "Since" comment for each macro you use to see what version
+dnl of the macros you require.
+m4_defun([PKG_PREREQ],
+[m4_define([PKG_MACROS_VERSION], [0.29.2])
+m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
+ [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
+])dnl PKG_PREREQ
+
+dnl PKG_PROG_PKG_CONFIG([MIN-VERSION], [ACTION-IF-NOT-FOUND])
+dnl ---------------------------------------------------------
+dnl Since: 0.16
+dnl
+dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
+dnl first found in the path. Checks that the version of pkg-config found
+dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
+dnl used since that's the first version where most current features of
+dnl pkg-config existed.
+dnl
+dnl If pkg-config is not found or older than specified, it will result
+dnl in an empty PKG_CONFIG variable. To avoid widespread issues with
+dnl scripts not checking it, ACTION-IF-NOT-FOUND defaults to aborting.
+dnl You can specify [PKG_CONFIG=false] as an action instead, which would
+dnl result in pkg-config tests failing, but no bogus error messages.
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
+m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=m4_default([$1], [0.9.0])
+ AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ PKG_CONFIG=""
+ fi
+fi
+if test -z "$PKG_CONFIG"; then
+ m4_default([$2], [AC_MSG_ERROR([pkg-config not found])])
+fi[]dnl
+])dnl PKG_PROG_PKG_CONFIG
+
+dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------------------------------
+dnl Since: 0.18
+dnl
+dnl Check to see whether a particular set of modules exists. Similar to
+dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
+dnl
+dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+dnl only at the first occurrence in configure.ac, so if the first place
+dnl it's called might be skipped (such as if it is within an "if", you
+dnl have to call PKG_CHECK_EXISTS manually
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+ m4_default([$2], [:])
+m4_ifvaln([$3], [else
+ $3])dnl
+fi])
+
+dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+dnl ---------------------------------------------
+dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
+dnl pkg_failed based on the result.
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+ pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+ PKG_CHECK_EXISTS([$3],
+ [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes ],
+ [pkg_failed=yes])
+ else
+ pkg_failed=untried
+fi[]dnl
+])dnl _PKG_CONFIG
+
+dnl _PKG_SHORT_ERRORS_SUPPORTED
+dnl ---------------------------
+dnl Internal check to see if pkg-config supports short errors.
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi[]dnl
+])dnl _PKG_SHORT_ERRORS_SUPPORTED
+
+
+dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl [ACTION-IF-NOT-FOUND])
+dnl --------------------------------------------------------------
+dnl Since: 0.4.0
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
+dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $2])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+ AC_MSG_RESULT([no])
+ _PKG_SHORT_ERRORS_SUPPORTED
+ if test $_pkg_short_errors_supported = yes; then
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
+ else
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+ m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])[]dnl
+ ])
+elif test $pkg_failed = untried; then
+ AC_MSG_RESULT([no])
+ m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
+ ])
+else
+ $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+ $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+ AC_MSG_RESULT([yes])
+ $3
+fi[]dnl
+])dnl PKG_CHECK_MODULES
+
+
+dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl [ACTION-IF-NOT-FOUND])
+dnl ---------------------------------------------------------------------
+dnl Since: 0.29
+dnl
+dnl Checks for existence of MODULES and gathers its build flags with
+dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
+dnl and VARIABLE-PREFIX_LIBS from --libs.
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
+dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
+dnl configure.ac.
+AC_DEFUN([PKG_CHECK_MODULES_STATIC],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+_save_PKG_CONFIG=$PKG_CONFIG
+PKG_CONFIG="$PKG_CONFIG --static"
+PKG_CHECK_MODULES($@)
+PKG_CONFIG=$_save_PKG_CONFIG[]dnl
+])dnl PKG_CHECK_MODULES_STATIC
+
+
+dnl PKG_INSTALLDIR([DIRECTORY])
+dnl -------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable pkgconfigdir as the location where a module
+dnl should install pkg-config .pc files. By default the directory is
+dnl $libdir/pkgconfig, but the default can be changed by passing
+dnl DIRECTORY. The user can override through the --with-pkgconfigdir
+dnl parameter.
+AC_DEFUN([PKG_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+ [pkg-config installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([pkgconfigdir],
+ [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
+ [with_pkgconfigdir=]pkg_default)
+AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_INSTALLDIR
+
+
+dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
+dnl --------------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable noarch_pkgconfigdir as the location where a
+dnl module should install arch-independent pkg-config .pc files. By
+dnl default the directory is $datadir/pkgconfig, but the default can be
+dnl changed by passing DIRECTORY. The user can override through the
+dnl --with-noarch-pkgconfigdir parameter.
+AC_DEFUN([PKG_NOARCH_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+ [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([noarch-pkgconfigdir],
+ [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
+ [with_noarch_pkgconfigdir=]pkg_default)
+AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_NOARCH_INSTALLDIR
+
+
+dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
+dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------
+dnl Since: 0.28
+dnl
+dnl Retrieves the value of the pkg-config variable for the given module.
+AC_DEFUN([PKG_CHECK_VAR],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
+
+_PKG_CONFIG([$1], [variable="][$3]["], [$2])
+AS_VAR_COPY([$1], [pkg_cv_][$1])
+
+AS_VAR_IF([$1], [""], [$5], [$4])dnl
+])dnl PKG_CHECK_VAR
+
+dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES,
+dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND],
+dnl [DESCRIPTION], [DEFAULT])
+dnl ------------------------------------------
+dnl
+dnl Prepare a "--with-" configure option using the lowercase
+dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and
+dnl PKG_CHECK_MODULES in a single macro.
+AC_DEFUN([PKG_WITH_MODULES],
+[
+m4_pushdef([with_arg], m4_tolower([$1]))
+
+m4_pushdef([description],
+ [m4_default([$5], [build with ]with_arg[ support])])
+
+m4_pushdef([def_arg], [m4_default([$6], [auto])])
+m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes])
+m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no])
+
+m4_case(def_arg,
+ [yes],[m4_pushdef([with_without], [--without-]with_arg)],
+ [m4_pushdef([with_without],[--with-]with_arg)])
+
+AC_ARG_WITH(with_arg,
+ AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),,
+ [AS_TR_SH([with_]with_arg)=def_arg])
+
+AS_CASE([$AS_TR_SH([with_]with_arg)],
+ [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)],
+ [auto],[PKG_CHECK_MODULES([$1],[$2],
+ [m4_n([def_action_if_found]) $3],
+ [m4_n([def_action_if_not_found]) $4])])
+
+m4_popdef([with_arg])
+m4_popdef([description])
+m4_popdef([def_arg])
+
+])dnl PKG_WITH_MODULES
+
+dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
+dnl [DESCRIPTION], [DEFAULT])
+dnl -----------------------------------------------
+dnl
+dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES
+dnl check._[VARIABLE-PREFIX] is exported as make variable.
+AC_DEFUN([PKG_HAVE_WITH_MODULES],
+[
+PKG_WITH_MODULES([$1],[$2],,,[$3],[$4])
+
+AM_CONDITIONAL([HAVE_][$1],
+ [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"])
+])dnl PKG_HAVE_WITH_MODULES
+
+dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
+dnl [DESCRIPTION], [DEFAULT])
+dnl ------------------------------------------------------
+dnl
+dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after
+dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make
+dnl and preprocessor variable.
+AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES],
+[
+PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4])
+
+AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"],
+ [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])])
+])dnl PKG_HAVE_DEFINE_WITH_MODULES
diff --git a/pkgconf.wxs.in b/pkgconf.wxs.in
new file mode 100644
index 000000000000..13ebb069a50b
--- /dev/null
+++ b/pkgconf.wxs.in
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+
+ <?define Arch = "@WIXL_ARCH@"?>
+ <?if $(var.Arch) = "x64"?>
+ <?define GLIB_ARCH = "win64"?>
+ <?define ArchString = "64-bit"?>
+ <?define ArchProgramFilesFolder = "ProgramFiles64Folder"?>
+ <?define Win64 = "yes"?>
+ <?else?>
+ <?define GLIB_ARCH = "win32"?>
+ <?define ArchString = "32-bit"?>
+ <?define ArchProgramFilesFolder = "ProgramFilesFolder"?>
+ <?define Win64 = "no"?>
+ <?endif?>
+
+
+ <Product Id="*"
+ Name="pkgconf @VERSION@ ($(var.ArchString))"
+ Language="1033"
+ Version="@VERSION@"
+ Manufacturer="pkgconf"
+ UpgradeCode="4faedad2-3f9d-45cc-89a7-3732ad2db0f7">
+
+ <Package InstallerVersion="200"
+ Compressed="yes"
+ InstallScope="perMachine" />
+
+ <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
+ <MediaTemplate EmbedCab="yes" />
+
+ <Feature Id="ProductFeature" Title="pkgconf" Level="1">
+ <ComponentGroupRef Id="ProductComponents" />
+ </Feature>
+
+ <Directory Id="TARGETDIR" Name="SourceDir">
+ <Directory Id="$(var.ArchProgramFilesFolder)">
+ <Directory Id="INSTALLFOLDER" Name="pkgconf @VERSION@" />
+ </Directory>
+ </Directory>
+
+ <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
+ <Component Id="PkgconfExe" Guid="*" Win64="$(var.Win64)">
+ <File Id="PkgconfExeFile"
+ Source="@EXE@"
+ KeyPath="yes" />
+ <File Id="PkgconfigExeFile"
+ Name="pkg-config.exe"
+ Source="@EXE@"/>
+ <File Id="PkgconfDllFile"
+ Source="@DLL@"/>
+ <Environment Id="PATH"
+ Name="PATH"
+ Value="[INSTALLFOLDER]"
+ Permanent="no"
+ Part="last"
+ Action="set"
+ System="yes" />
+ </Component>
+ </ComponentGroup>
+
+ <UIRef Id="WixUI_Minimal" />
+ </Product>
+</Wix>
diff --git a/tests/Kyuafile.in b/tests/Kyuafile.in
new file mode 100644
index 000000000000..f50b749fd717
--- /dev/null
+++ b/tests/Kyuafile.in
@@ -0,0 +1,14 @@
+syntax(2)
+
+test_suite('pkgconf')
+
+atf_test_program{name='basic'}
+atf_test_program{name='requires'}
+atf_test_program{name='regress'}
+atf_test_program{name='parser'}
+atf_test_program{name='sysroot'}
+atf_test_program{name='conflicts'}
+atf_test_program{name='version'}
+atf_test_program{name='framework'}
+atf_test_program{name='provides'}
+atf_test_program{name='symlink'}
diff --git a/tests/basic.sh b/tests/basic.sh
new file mode 100755
index 000000000000..8debb32f2b38
--- /dev/null
+++ b/tests/basic.sh
@@ -0,0 +1,391 @@
+#!/usr/bin/env atf-sh
+
+. $(atf_get_srcdir)/test_env.sh
+
+tests_init \
+ noargs \
+ libs \
+ libs_cflags \
+ libs_cflags_version \
+ libs_cflags_version_multiple \
+ libs_cflags_version_alt \
+ libs_cflags_version_different \
+ libs_cflags_version_different_bad \
+ libs_env \
+ exists_nonexitent \
+ nonexitent \
+ exists_version \
+ exists_version_bad \
+ exists_version_bad2 \
+ exists_version_bad3 \
+ exists \
+ exists2 \
+ exists3 \
+ exists_version_alt \
+ exists_cflags \
+ exists_cflags_env \
+ uninstalled_bad \
+ uninstalled \
+ libs_intermediary \
+ libs_circular1 \
+ libs_circular2 \
+ libs_circular_directpc \
+ libs_static \
+ libs_static_ordering \
+ libs_metapackage \
+ license_isc \
+ license_noassertion \
+ modversion_noflatten \
+ pkg_config_path \
+ nolibs \
+ nocflags \
+ arbitary_path \
+ with_path \
+ relocatable \
+ single_depth_selectors \
+ print_variables_env \
+ variable_env \
+ variable_no_recurse
+
+noargs_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check -s exit:1 -e ignore pkgconf
+}
+
+libs_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -lfoo\n" \
+ pkgconf --libs foo
+}
+
+libs_cflags_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -L/test/lib -lfoo\n" \
+ pkgconf --cflags --libs foo
+}
+
+atf_test_case basic_libs_cflags_version
+libs_cflags_version_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -L/test/lib -lfoo\n" \
+ pkgconf --cflags --libs 'foo > 1.2'
+}
+
+libs_cflags_version_multiple_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -L/test/lib -lbar -lfoo\n" \
+ pkgconf --cflags --libs 'foo > 1.2 bar >= 1.3'
+}
+
+libs_cflags_version_multiple_coma_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -L/test/lib -lbar -lfoo\n" \
+ pkgconf --cflags --libs 'foo > 1.2,bar >= 1.3'
+}
+
+libs_cflags_version_alt_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -L/test/lib -lfoo\n" \
+ pkgconf --cflags --libs 'foo' '>' '1.2'
+}
+
+libs_cflags_version_different_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -L/test/lib -lfoo\n" \
+ pkgconf --cflags --libs 'foo' '!=' '1.3.0'
+}
+
+atf_test_case basic_libs_cflags_version_different_bad
+libs_cflags_version_different_bad_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -s exit:1 \
+ -e inline:"Package dependency requirement 'foo != 1.2.3' could not be satisfied.\nPackage 'foo' has version '1.2.3', required version is '!= 1.2.3'\n" \
+ pkgconf --cflags --libs 'foo' '!=' '1.2.3'
+}
+
+exists_nonexitent_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -s exit:1 \
+ pkgconf --exists nonexistant
+}
+
+nonexitent_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -s exit:1 \
+ pkgconf nonexistant
+}
+
+exists_version_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ pkgconf --exists 'foo > 1.2'
+}
+
+exists_version_bad_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -s exit:1 \
+ pkgconf --exists 'foo > 1.2.3'
+}
+
+exists_version_alt_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ pkgconf --exists 'foo' '>' '1.2'
+}
+
+uninstalled_bad_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -s exit:1 \
+ pkgconf --uninstalled 'foo'
+}
+
+uninstalled_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ pkgconf --uninstalled 'omg'
+}
+
+exists_version_bad2_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -s exit:1 \
+ pkgconf --exists 'foo >= '
+}
+
+exists_version_bad3_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -s exit:1 \
+ pkgconf --exists 'tilde >= 1.0.0'
+}
+
+exists_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ pkgconf --exists 'tilde = 1.0.0~rc1'
+}
+
+exists2_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ pkgconf --exists 'tilde <= 1.0.0'
+}
+
+exists3_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ pkgconf --exists '' 'foo'
+}
+
+libs_intermediary_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-lintermediary-1 -lintermediary-2 -lfoo -lbar -lbaz\n" \
+ pkgconf --libs intermediary-1 intermediary-2
+}
+
+libs_circular2_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"circular-1: breaking circular reference (circular-1 -> circular-2 -> circular-1)\n" \
+ pkgconf circular-2 --validate
+}
+
+libs_circular1_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"circular-3: breaking circular reference (circular-3 -> circular-1 -> circular-3)\n" \
+ pkgconf circular-1 --validate
+}
+
+libs_circular_directpc_body()
+{
+ atf_check \
+ -o inline:"-lcircular-3 -lcircular-1 -lcircular-2\n" \
+ pkgconf --libs ${selfdir}/lib1/circular-3.pc
+}
+
+libs_static_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"/libfoo.a -pthread\n" \
+ pkgconf --libs static-archive-libs
+}
+
+libs_static_ordering_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -lbar -lfoo\n" \
+ pkgconf --libs foo bar
+}
+
+libs_metapackage_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -lbar -lfoo\n" \
+ pkgconf --static --libs metapackage-3
+}
+
+pkg_config_path_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1${PATH_SEP}${selfdir}/lib2"
+ atf_check \
+ -o inline:"-L/test/lib -lfoo\n" \
+ pkgconf --libs foo
+ atf_check \
+ -o inline:"-L/test/lib -lbar -lfoo\n" \
+ pkgconf --libs bar
+}
+
+with_path_body()
+{
+ atf_check \
+ -o inline:"-L/test/lib -lfoo\n" \
+ pkgconf --with-path=${selfdir}/lib1 --with-path=${selfdir}/lib2 --libs foo
+ atf_check \
+ -o inline:"-L/test/lib -lbar -lfoo\n" \
+ pkgconf --with-path=${selfdir}/lib1 --with-path=${selfdir}/lib2 --libs bar
+}
+
+nolibs_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"\n" \
+ pkgconf --libs nolib
+}
+
+nocflags_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"\n" \
+ pkgconf --cflags nocflag
+}
+
+arbitary_path_body()
+{
+ cp ${selfdir}/lib1/foo.pc .
+ atf_check \
+ -o inline:"-L/test/lib -lfoo\n" \
+ pkgconf --libs foo.pc
+}
+
+relocatable_body()
+{
+ basedir=$(pkgconf --relocate ${selfdir})
+ atf_check \
+ -o inline:"${basedir}/lib-relocatable\n" \
+ pkgconf --define-prefix --variable=prefix ${basedir}/lib-relocatable/lib/pkgconfig/foo.pc
+}
+
+single_depth_selectors_body()
+{
+ export PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH=1
+ atf_check \
+ -o inline:"foo\n" \
+ pkgconf --with-path=${selfdir}/lib3 --print-requires bar
+}
+
+license_isc_body()
+{
+ atf_check \
+ -o inline:"foo: ISC\n" \
+ pkgconf --with-path=${selfdir}/lib1 --license foo
+}
+
+license_noassertion_body()
+{
+ atf_check \
+ -o inline:"bar: NOASSERTION\nfoo: ISC\n" \
+ pkgconf --with-path=${selfdir}/lib1 --license bar
+}
+
+modversion_noflatten_body()
+{
+ atf_check \
+ -o inline:"1.3\n" \
+ pkgconf --with-path=${selfdir}/lib1 --modversion bar
+}
+
+exists_cflags_body()
+{
+ atf_check \
+ -o inline:"-DHAVE_FOO\n" \
+ pkgconf --with-path=${selfdir}/lib1 --cflags --exists-cflags --fragment-filter=D foo
+}
+
+exists_cflags_env_body()
+{
+ atf_check \
+ -o inline:"FOO_CFLAGS='-DHAVE_FOO'\n" \
+ pkgconf --with-path=${selfdir}/lib1 --cflags --exists-cflags --fragment-filter=D --env=FOO foo
+}
+
+libs_env_body()
+{
+ atf_check \
+ -o inline:"FOO_LIBS='-L/test/lib -lfoo'\n" \
+ pkgconf --with-path=${selfdir}/lib1 --libs --env=FOO foo
+}
+
+print_variables_env_body()
+{
+ atf_check \
+ -o inline:"FOO_CFLAGS='-fPIC -I/test/include/foo'\nFOO_LIBS='-L/test/lib -lfoo'\nFOO_INCLUDEDIR='/test/include'\nFOO_LIBDIR='/test/lib'\nFOO_EXEC_PREFIX='/test'\nFOO_PREFIX='/test'\nFOO_PCFILEDIR='${selfdir}/lib1'\n" \
+ pkgconf --with-path=${selfdir}/lib1 --env=FOO --print-variables --cflags --libs foo
+
+}
+
+variable_env_body()
+{
+ atf_check \
+ -o inline:"FOO_INCLUDEDIR='/test/include'\n" \
+ pkgconf --with-path=${selfdir}/lib1 --env=FOO --variable=includedir foo
+}
+
+variable_no_recurse_body()
+{
+ atf_check \
+ -o inline:"/test/include\n" \
+ pkgconf --with-path=${selfdir}/lib1 --variable=includedir bar
+}
diff --git a/tests/builtins.sh b/tests/builtins.sh
new file mode 100755
index 000000000000..cbdea37e0c5f
--- /dev/null
+++ b/tests/builtins.sh
@@ -0,0 +1,68 @@
+#!/usr/bin/env atf-sh
+
+. $(atf_get_srcdir)/test_env.sh
+
+tests_init \
+ modversion \
+ variable \
+ define_variable \
+ global_variable
+
+modversion_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"1.0.1 \n" \
+ pkgconf --modversion pkg-config
+}
+
+variable_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"/test \n" \
+ pkgconf --variable=prefix foo
+}
+
+define_variable_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"/test2 \n" \
+ pkgconf --define-variable=prefix=/test2 --variable=prefix foo
+}
+
+global_variable_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"${selfdir}/lib1 \n"
+ pkgconf --exists -foo
+}
+
+argv_parse_3_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-llib-1 -pthread /test/lib/lib2.so \n" \
+ pkgconf --libs argv-parse-3
+}
+
+tilde_quoting_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L~ -ltilde \n" \
+ pkgconf --libs tilde-quoting
+ atf_check \
+ -o inline:"-I~ \n" \
+ pkgconf --cflags tilde-quoting
+}
+
+paren_quoting_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L\$(libdir) -ltilde \n" \
+ pkgconf --libs paren-quoting
+}
diff --git a/tests/conflicts.sh b/tests/conflicts.sh
new file mode 100755
index 000000000000..da6396f6b42c
--- /dev/null
+++ b/tests/conflicts.sh
@@ -0,0 +1,23 @@
+#!/usr/bin/env atf-sh
+
+. $(atf_get_srcdir)/test_env.sh
+
+tests_init \
+ libs \
+ ignore
+
+libs_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -lconflicts\n" \
+ pkgconf --libs conflicts
+}
+
+ignore_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -lconflicts\n" \
+ pkgconf --ignore-conflicts --libs conflicts
+}
diff --git a/tests/framework.sh b/tests/framework.sh
new file mode 100755
index 000000000000..367692335746
--- /dev/null
+++ b/tests/framework.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env atf-sh
+
+. $(atf_get_srcdir)/test_env.sh
+
+tests_init \
+ libs
+
+libs_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-F/test/lib -framework framework-1\n" \
+ pkgconf --libs framework-1
+ atf_check \
+ -o inline:"-F/test/lib -framework framework-2 -framework framework-1\n" \
+ pkgconf --libs framework-2
+ atf_check \
+ -o inline:"-F/test/lib -framework framework-2 -framework framework-1\n" \
+ pkgconf --libs framework-1 framework-2
+}
diff --git a/tests/lib-relocatable/lib/pkgconfig/foo.pc b/tests/lib-relocatable/lib/pkgconfig/foo.pc
new file mode 100644
index 000000000000..f154eb76ccf1
--- /dev/null
+++ b/tests/lib-relocatable/lib/pkgconfig/foo.pc
@@ -0,0 +1,11 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: foo
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lfoo
+Cflags: -fPIC -I${includedir}/foo
+Cflags.private: -DFOO_STATIC
diff --git a/tests/lib1/argv-parse-2.pc b/tests/lib1/argv-parse-2.pc
new file mode 100644
index 000000000000..cfc0fcf904b2
--- /dev/null
+++ b/tests/lib1/argv-parse-2.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: argv-parse-2
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -llib-1 -pthread ${libdir}/lib2.so
+Cflags:
diff --git a/tests/lib1/argv-parse-3.pc b/tests/lib1/argv-parse-3.pc
new file mode 100644
index 000000000000..b115b3001205
--- /dev/null
+++ b/tests/lib1/argv-parse-3.pc
@@ -0,0 +1,11 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: argv-parse-3
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -llib-1 \
+ -pthread ${libdir}/lib2.so
+Cflags:
diff --git a/tests/lib1/argv-parse.pc b/tests/lib1/argv-parse.pc
new file mode 100644
index 000000000000..d445ae78e7f8
--- /dev/null
+++ b/tests/lib1/argv-parse.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: argv-parse
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -llib-3 -llib-1 -llib-2 -lpthread
+Cflags:
diff --git a/tests/lib1/bar.pc b/tests/lib1/bar.pc
new file mode 100644
index 000000000000..9157d06b5fd6
--- /dev/null
+++ b/tests/lib1/bar.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: bar
+Description: Another pkg-config test
+Version: 1.3
+Libs: -L${libdir} -lbar
+Requires: foo
diff --git a/tests/lib1/baz.pc b/tests/lib1/baz.pc
new file mode 100644
index 000000000000..d9cb258020bc
--- /dev/null
+++ b/tests/lib1/baz.pc
@@ -0,0 +1,11 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: bar
+Description: Another pkg-config test (with private Requires, ha!)
+Version: 1.3
+Libs: -L${libdir} -lbaz
+Libs.private: -L${libdir} -lzee
+Requires.private: foo
diff --git a/tests/lib1/billion-laughs.pc b/tests/lib1/billion-laughs.pc
new file mode 100644
index 000000000000..ad1ee7948b47
--- /dev/null
+++ b/tests/lib1/billion-laughs.pc
@@ -0,0 +1,13 @@
+v9=lol
+v8=${v9}${v9}${v9}${v9}${v9}${v9}${v9}${v9}${v9}${v9}
+v7=${v8}${v8}${v8}${v8}${v8}${v8}${v8}${v8}${v8}${v8}
+v6=${v7}${v7}${v7}${v7}${v7}${v7}${v7}${v7}${v7}${v7}
+v5=${v6}${v6}${v6}${v6}${v6}${v6}${v6}${v6}${v6}${v6}
+v4=${v5}${v5}${v5}${v5}${v5}${v5}${v5}${v5}${v5}${v5}
+v3=${v4}${v4}${v4}${v4}${v4}${v4}${v4}${v4}${v4}${v4}
+v2=${v3}${v3}${v3}${v3}${v3}${v3}${v3}${v3}${v3}${v3}
+v1=${v2}${v2}${v2}${v2}${v2}${v2}${v2}${v2}${v2}${v2}
+v0=${v1}${v1}${v1}${v1}${v1}${v1}${v1}${v1}${v1}${v1}
+Name: One Billion Laughs
+Version: ${v0}
+Description: Don't install this!
diff --git a/tests/lib1/c-comment.pc b/tests/lib1/c-comment.pc
new file mode 100644
index 000000000000..ed776a1f33e4
--- /dev/null
+++ b/tests/lib1/c-comment.pc
@@ -0,0 +1,17 @@
+/*
+ This is a C-style comment, which technically isn't allowed, but
+ some pkg-config files use them.
+ */
+
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: c-comment
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lfoo
+Cflags: -fPIC -I${includedir}/foo
+Cflags.private: -DFOO_STATIC
+License: ISC
diff --git a/tests/lib1/case-sensitivity.pc b/tests/lib1/case-sensitivity.pc
new file mode 100644
index 000000000000..9618061c485c
--- /dev/null
+++ b/tests/lib1/case-sensitivity.pc
@@ -0,0 +1,7 @@
+foo=3
+Foo=4
+
+Name: case-sensitivity
+Description: Package for testing case-sensitivity
+Version: 1
+Requires: foo < ${foo}
diff --git a/tests/lib1/cflags-internal.pc b/tests/lib1/cflags-internal.pc
new file mode 100644
index 000000000000..500e566df716
--- /dev/null
+++ b/tests/lib1/cflags-internal.pc
@@ -0,0 +1,9 @@
+prefix=/test/local
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: cflags-internal
+Description: A testing pkg-config file
+Version: 1.2.3
+Cflags: -I${includedir}/foo
diff --git a/tests/lib1/cflags-libs-only.pc b/tests/lib1/cflags-libs-only.pc
new file mode 100644
index 000000000000..718e7cd95010
--- /dev/null
+++ b/tests/lib1/cflags-libs-only.pc
@@ -0,0 +1,10 @@
+prefix=/test/local
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: cflags-libs-only
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lfoo
+Cflags: -I${includedir}/foo
diff --git a/tests/lib1/cflags-libs-private-a.pc b/tests/lib1/cflags-libs-private-a.pc
new file mode 100644
index 000000000000..fbb7742ba2e8
--- /dev/null
+++ b/tests/lib1/cflags-libs-private-a.pc
@@ -0,0 +1,7 @@
+Name: cflags-libs-private-a
+Version: 1
+Description: test case for issue #370
+Cflags:
+Libs:
+Requires.private: cflags-libs-private-b
+
diff --git a/tests/lib1/cflags-libs-private-b.pc b/tests/lib1/cflags-libs-private-b.pc
new file mode 100644
index 000000000000..0d5e5a45bfaa
--- /dev/null
+++ b/tests/lib1/cflags-libs-private-b.pc
@@ -0,0 +1,7 @@
+Name: cflags-libs-private-b
+Version: 1
+Description: test case for issue #370
+Cflags:
+Libs:
+Requires: cflags-libs-private-c
+
diff --git a/tests/lib1/cflags-libs-private-c.pc b/tests/lib1/cflags-libs-private-c.pc
new file mode 100644
index 000000000000..7efc38e4bb95
--- /dev/null
+++ b/tests/lib1/cflags-libs-private-c.pc
@@ -0,0 +1,6 @@
+Name: cflags-libs-private-c
+Version: 1
+Description: test case for issue #370
+Cflags:
+Libs: -lc
+
diff --git a/tests/lib1/cflags-whitespace-trailing.pc b/tests/lib1/cflags-whitespace-trailing.pc
new file mode 100644
index 000000000000..2e660353b00d
--- /dev/null
+++ b/tests/lib1/cflags-whitespace-trailing.pc
@@ -0,0 +1,4 @@
+Name: CFlags Trailing Whitespace Bug
+Description: Demonstrates problems with -I with spaces
+Version: 1
+Cflags: -I/usr/include -I/usr/include/foo
diff --git a/tests/lib1/cflags-whitespace.pc b/tests/lib1/cflags-whitespace.pc
new file mode 100644
index 000000000000..4cd1cd164aa6
--- /dev/null
+++ b/tests/lib1/cflags-whitespace.pc
@@ -0,0 +1,4 @@
+Name: CFlags Whitespace Bug
+Description: Demonstrates problems with -I with spaces and sysroot munging
+Version: 1
+Cflags: -I /opt/bad/include
diff --git a/tests/lib1/child-prefix/pkgconfig/child-prefix-1.pc b/tests/lib1/child-prefix/pkgconfig/child-prefix-1.pc
new file mode 100644
index 000000000000..0d32d89d9259
--- /dev/null
+++ b/tests/lib1/child-prefix/pkgconfig/child-prefix-1.pc
@@ -0,0 +1,11 @@
+prefix=/usr
+exec_prefix=/usr
+libdir=${prefix}/lib64
+includedir=${prefix}/include
+
+Name: child-prefix-1
+Description: child prefix 1 test data
+Requires:
+Version: 1.0
+Libs: -L${libdir} -lchild-prefix-1
+Cflags: -I${includedir}/child-prefix-1
diff --git a/tests/lib1/circular-1.pc b/tests/lib1/circular-1.pc
new file mode 100644
index 000000000000..bbdb9f79cc86
--- /dev/null
+++ b/tests/lib1/circular-1.pc
@@ -0,0 +1,11 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: circular-1
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -lcircular-1
+Requires: circular-2
+Cflags:
diff --git a/tests/lib1/circular-2.pc b/tests/lib1/circular-2.pc
new file mode 100644
index 000000000000..2086834a6b85
--- /dev/null
+++ b/tests/lib1/circular-2.pc
@@ -0,0 +1,11 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: circular-2
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -lcircular-2
+Requires: circular-3
+Cflags:
diff --git a/tests/lib1/circular-3.pc b/tests/lib1/circular-3.pc
new file mode 100644
index 000000000000..e8b8e77a6c54
--- /dev/null
+++ b/tests/lib1/circular-3.pc
@@ -0,0 +1,11 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: circular-3
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -lcircular-3
+Requires: circular-1
+Cflags:
diff --git a/tests/lib1/comments-in-fields.pc b/tests/lib1/comments-in-fields.pc
new file mode 100644
index 000000000000..8e65a6557f4f
--- /dev/null
+++ b/tests/lib1/comments-in-fields.pc
@@ -0,0 +1,9 @@
+Name: comments-in-fields
+Description: Comments in fields test case
+URL: http://pkgconf.org/
+Version: 0
+Requires: # foo
+Requires.private: foo
+Libs: -lfoo # -lbar
+Libs.private: -lbar
+Cflags: -I${includedir}
diff --git a/tests/lib1/comments.pc b/tests/lib1/comments.pc
new file mode 100644
index 000000000000..be83b3e63b40
--- /dev/null
+++ b/tests/lib1/comments.pc
@@ -0,0 +1,6 @@
+Name: comments
+Description: Portable CD-ROM I/O library
+Version: 0
+#Requires: bar-2.0
+Libs: -lfoo
+Cflags: -I${includedir}
diff --git a/tests/lib1/conflicts.pc b/tests/lib1/conflicts.pc
new file mode 100644
index 000000000000..bec486982d69
--- /dev/null
+++ b/tests/lib1/conflicts.pc
@@ -0,0 +1,11 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: conflicts
+Description: A testing pkg-config file
+Conflicts: foo <= 1.3
+Version: 1.2.3
+Libs: -L${libdir} -lconflicts
+Cflags: -fPIC -I${includedir}/conflicts
diff --git a/tests/lib1/depgraph-break.pc b/tests/lib1/depgraph-break.pc
new file mode 100644
index 000000000000..61e00cc62bc1
--- /dev/null
+++ b/tests/lib1/depgraph-break.pc
@@ -0,0 +1,12 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: depgraph-break
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lfoo
+Cflags: -fPIC -I${includedir}/foo
+Cflags.private: -DFOO_STATIC
+Requires: nonexistant
diff --git a/tests/lib1/dos-lineendings.pc b/tests/lib1/dos-lineendings.pc
new file mode 100644
index 000000000000..cf169f159c7d
--- /dev/null
+++ b/tests/lib1/dos-lineendings.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: dos-lineendings
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir}/dos-lineendings -ldos-lineendings
+Cflags: -fPIC -I${includedir}/dos-lineendings
diff --git a/tests/lib1/empty-tuple.pc b/tests/lib1/empty-tuple.pc
new file mode 100644
index 000000000000..a1794954a4f1
--- /dev/null
+++ b/tests/lib1/empty-tuple.pc
@@ -0,0 +1,6 @@
+xcflags=
+
+Name: empty-tuple
+Description: testing file
+Version: 1
+CFlags: ${xcflags}
diff --git a/tests/lib1/escaped-backslash.pc b/tests/lib1/escaped-backslash.pc
new file mode 100644
index 000000000000..6abcbb064597
--- /dev/null
+++ b/tests/lib1/escaped-backslash.pc
@@ -0,0 +1,4 @@
+Name: escaped-backslash
+Version: 1
+Description: test package for backslash escape scenario
+Cflags: -IC:\\A
diff --git a/tests/lib1/explicit-sysroot.pc b/tests/lib1/explicit-sysroot.pc
new file mode 100644
index 000000000000..63c7ca042bd8
--- /dev/null
+++ b/tests/lib1/explicit-sysroot.pc
@@ -0,0 +1,7 @@
+prefix=/usr
+datarootdir=${prefix}/share
+pkgdatadir=${pc_sysrootdir}/${datarootdir}/test
+
+Name: Test
+Description: Testing pc_sysrootdir auto-expansion
+Version: 1.0
diff --git a/tests/lib1/flag-order-1.pc b/tests/lib1/flag-order-1.pc
new file mode 100644
index 000000000000..d84175197b19
--- /dev/null
+++ b/tests/lib1/flag-order-1.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+cflags=-I${includedir}
+
+Name: flag order 1
+Description: test for flag ordering
+Version: 1
+Libs: -L${libdir} -Bdynamic -lfoo -Bstatic -lbar
diff --git a/tests/lib1/flag-order-3.pc b/tests/lib1/flag-order-3.pc
new file mode 100644
index 000000000000..d813f50dbf4f
--- /dev/null
+++ b/tests/lib1/flag-order-3.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+cflags=-I${includedir}
+
+Name: flag order 3
+Description: test for flag ordering
+Version: 1
+Libs: -L${libdir} -Wl,--start-group -lfoo -lbar -Wl,--end-group
diff --git a/tests/lib1/foo.pc b/tests/lib1/foo.pc
new file mode 100644
index 000000000000..daef9f9fa956
--- /dev/null
+++ b/tests/lib1/foo.pc
@@ -0,0 +1,12 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: foo
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lfoo
+Cflags: -fPIC -I${includedir}/foo
+Cflags.private: -DFOO_STATIC
+License: ISC
diff --git a/tests/lib1/foobar.pc b/tests/lib1/foobar.pc
new file mode 100644
index 000000000000..e9aa84337600
--- /dev/null
+++ b/tests/lib1/foobar.pc
@@ -0,0 +1,12 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: foobar
+Description: A testing pkg-config file
+Version: 3.2.1
+Libs: -L${libdir} -lfoobar
+Cflags: -fPIC -I${includedir}/foobar
+Cflags.private: -DFOOBAR_STATIC
+License: ISC
diff --git a/tests/lib1/fragment-collision-1.pc b/tests/lib1/fragment-collision-1.pc
new file mode 100644
index 000000000000..201bcb0a5f97
--- /dev/null
+++ b/tests/lib1/fragment-collision-1.pc
@@ -0,0 +1,4 @@
+Name: fragment-collision-1
+Version: 0
+Description: fragment collision test package
+Cflags: -D_THREAD_SAFE -pthread
diff --git a/tests/lib1/fragment-collision-2.pc b/tests/lib1/fragment-collision-2.pc
new file mode 100644
index 000000000000..2e6feb4a61c2
--- /dev/null
+++ b/tests/lib1/fragment-collision-2.pc
@@ -0,0 +1,4 @@
+Name: fragment-collision-2
+Version: 0
+Description: fragment collision test package
+CFlags: -D_FOO
diff --git a/tests/lib1/fragment-collision-intermediary.pc b/tests/lib1/fragment-collision-intermediary.pc
new file mode 100644
index 000000000000..1ddb6f502cf7
--- /dev/null
+++ b/tests/lib1/fragment-collision-intermediary.pc
@@ -0,0 +1,5 @@
+Name: fragment-collision-intermediary
+Version: 0
+Description: fragment collision test package
+Requires.private: fragment-collision-1, fragment-collision-2
+CFlags: -D_BAR
diff --git a/tests/lib1/fragment-collision.pc b/tests/lib1/fragment-collision.pc
new file mode 100644
index 000000000000..c58267513da2
--- /dev/null
+++ b/tests/lib1/fragment-collision.pc
@@ -0,0 +1,6 @@
+Name: fragment-collision
+Version: 0
+Description: fragment collision test package
+Requires: fragment-collision-2, fragment-collision-intermediary
+Requires.private: fragment-collision-1
+Cflags: -D_BAZ
diff --git a/tests/lib1/fragment-comment.pc b/tests/lib1/fragment-comment.pc
new file mode 100644
index 000000000000..dfdd4f3b1dc8
--- /dev/null
+++ b/tests/lib1/fragment-comment.pc
@@ -0,0 +1,4 @@
+Name: fragment-comment
+Description: Test case for issue #215
+Version: 1.0
+Cflags: kuku=\#ttt \ No newline at end of file
diff --git a/tests/lib1/fragment-escaping-1.pc b/tests/lib1/fragment-escaping-1.pc
new file mode 100644
index 000000000000..b147951e5024
--- /dev/null
+++ b/tests/lib1/fragment-escaping-1.pc
@@ -0,0 +1,5 @@
+Name: fragment-escaping-1
+Version: 0
+Description: fragment escaping test
+Cflags: "-IC:\\D E"
+Libs: "-LC:\\D E" -lE
diff --git a/tests/lib1/fragment-escaping-2.pc b/tests/lib1/fragment-escaping-2.pc
new file mode 100644
index 000000000000..12e03b3482f5
--- /dev/null
+++ b/tests/lib1/fragment-escaping-2.pc
@@ -0,0 +1,4 @@
+Name: fragment-escaping-2
+Version: 0
+Description: fragment escaping test
+Cflags: '-IC:\D E'
diff --git a/tests/lib1/fragment-escaping-3.pc b/tests/lib1/fragment-escaping-3.pc
new file mode 100644
index 000000000000..0f002ddb084c
--- /dev/null
+++ b/tests/lib1/fragment-escaping-3.pc
@@ -0,0 +1,4 @@
+Name: fragment-escaping-3
+Version: 0
+Description: fragment escaping test
+Cflags: -IC:\\D\ E
diff --git a/tests/lib1/fragment-group-a.pc b/tests/lib1/fragment-group-a.pc
new file mode 100644
index 000000000000..0e3966eab765
--- /dev/null
+++ b/tests/lib1/fragment-group-a.pc
@@ -0,0 +1,4 @@
+Name: fragment-group-a
+Version: 1.0
+Description: Test fixture for fragment groups
+Libs: -Wl,--start-group -la -lb -Wl,--end-group
diff --git a/tests/lib1/fragment-group-b.pc b/tests/lib1/fragment-group-b.pc
new file mode 100644
index 000000000000..ac7dda7223ac
--- /dev/null
+++ b/tests/lib1/fragment-group-b.pc
@@ -0,0 +1,4 @@
+Name: fragment-group-b
+Version: 1.0
+Description: Test fixture for fragment groups
+Libs: -nodefaultlibs
diff --git a/tests/lib1/fragment-group-c.pc b/tests/lib1/fragment-group-c.pc
new file mode 100644
index 000000000000..f79965be0f1d
--- /dev/null
+++ b/tests/lib1/fragment-group-c.pc
@@ -0,0 +1,4 @@
+Name: fragment-group-c
+Version: 1.0
+Description: Test fixture for fragment groups
+Libs: -Wl,--start-group -la -lgcc -Wl,--end-group -Wl,--gc-sections
diff --git a/tests/lib1/fragment-groups-2.pc b/tests/lib1/fragment-groups-2.pc
new file mode 100644
index 000000000000..f73f70698f56
--- /dev/null
+++ b/tests/lib1/fragment-groups-2.pc
@@ -0,0 +1,4 @@
+Name: fragment-groups-2
+Version: 1.0
+Description: Test fixture for fragment groups
+Requires: fragment-group-a, fragment-group-b, fragment-group-c
diff --git a/tests/lib1/fragment-groups.pc b/tests/lib1/fragment-groups.pc
new file mode 100644
index 000000000000..9645f9e9719a
--- /dev/null
+++ b/tests/lib1/fragment-groups.pc
@@ -0,0 +1,4 @@
+Name: fragment-groups
+Version: 1.0
+Description: Test fixture for fragment groups
+Libs: -Wl,--start-group -la -lb -Wl,--end-group -nodefaultlibs -Wl,--start-group -la -lgcc -Wl,--end-group -Wl,--gc-sections
diff --git a/tests/lib1/fragment-quoting-2.pc b/tests/lib1/fragment-quoting-2.pc
new file mode 100644
index 000000000000..49c6ac79ce76
--- /dev/null
+++ b/tests/lib1/fragment-quoting-2.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: fragment-quoting
+Description: A testing pkg-config file
+Version: 1.2.3
+Cflags: -fPIC -I${includedir}/foo -DQUOTED="${prefix}/share/doc"
+Cflags.private: -DFOO_STATIC
diff --git a/tests/lib1/fragment-quoting-3.pc b/tests/lib1/fragment-quoting-3.pc
new file mode 100644
index 000000000000..4ff59fc37e0c
--- /dev/null
+++ b/tests/lib1/fragment-quoting-3.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: fragment-quoting
+Description: A testing pkg-config file
+Version: 1.2.3
+Cflags: -fPIC -I${includedir}/foo -DQUOTED=\"${prefix}/share/doc\"
+Cflags.private: -DFOO_STATIC
diff --git a/tests/lib1/fragment-quoting-5.pc b/tests/lib1/fragment-quoting-5.pc
new file mode 100644
index 000000000000..22df0b3afbaa
--- /dev/null
+++ b/tests/lib1/fragment-quoting-5.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: fragment-quoting-5
+Description: A testing pkg-config file
+Version: 1.2.3
+Cflags: -fPIC -I${includedir}/foo -DQUOTED='${prefix}/share/doc'
+Cflags.private: -DFOO_STATIC
diff --git a/tests/lib1/fragment-quoting-7.pc b/tests/lib1/fragment-quoting-7.pc
new file mode 100644
index 000000000000..902e2f634e72
--- /dev/null
+++ b/tests/lib1/fragment-quoting-7.pc
@@ -0,0 +1,4 @@
+Name: fragment-quoting-7
+Description: A testing pkg-config file, this time from Go
+Version: 1.2.3
+Cflags: -Dhello=10 -Dworld=+32 -DDEFINED_FROM_PKG_CONFIG=hello\ world
diff --git a/tests/lib1/fragment-quoting.pc b/tests/lib1/fragment-quoting.pc
new file mode 100644
index 000000000000..5d8e70b57f82
--- /dev/null
+++ b/tests/lib1/fragment-quoting.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: fragment-quoting
+Description: A testing pkg-config file
+Version: 1.2.3
+Cflags: -fPIC -I${includedir}/foo -DQUOTED='"${prefix}/share/doc"'
+Cflags.private: -DFOO_STATIC
diff --git a/tests/lib1/framework-1.pc b/tests/lib1/framework-1.pc
new file mode 100644
index 000000000000..fcaa00814837
--- /dev/null
+++ b/tests/lib1/framework-1.pc
@@ -0,0 +1,9 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: framework-1
+Description: Another pkg-config test
+Version: 1.3
+Libs: -F${libdir} -framework framework-1
diff --git a/tests/lib1/framework-2.pc b/tests/lib1/framework-2.pc
new file mode 100644
index 000000000000..793655a6cc36
--- /dev/null
+++ b/tests/lib1/framework-2.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: framework-2
+Description: Another pkg-config test
+Version: 1.3
+Libs: -F${libdir} -framework framework-2
+Requires: framework-1
diff --git a/tests/lib1/idirafter-ordering.pc b/tests/lib1/idirafter-ordering.pc
new file mode 100644
index 000000000000..c42f7022736e
--- /dev/null
+++ b/tests/lib1/idirafter-ordering.pc
@@ -0,0 +1,4 @@
+Name: Bad
+Description: Demonstrates problems with -idirafter in old pkg-config; see also https://bugs.freedesktop.org/show_bug.cgi?id=23480
+Version: 1
+Cflags: -I/opt/bad/include1 -idirafter -I/opt/bad/include2 -I/opt/bad/include3
diff --git a/tests/lib1/idirafter.pc b/tests/lib1/idirafter.pc
new file mode 100644
index 000000000000..6c37c8bffedb
--- /dev/null
+++ b/tests/lib1/idirafter.pc
@@ -0,0 +1,4 @@
+Name: Bad
+Description: Demonstrates problems with -idirafter in both old pkg-config and current pkgconf; see also https://bugs.freedesktop.org/show_bug.cgi?id=97337
+Version: 1
+Cflags: -idirafter /opt/bad/include -idirafter /opt/bad2/include
diff --git a/tests/lib1/incomplete.pc b/tests/lib1/incomplete.pc
new file mode 100644
index 000000000000..40f0d91b9fc9
--- /dev/null
+++ b/tests/lib1/incomplete.pc
@@ -0,0 +1,4 @@
+Name: incomplete
+Description: incomplete package without CFLAGS/LIBS entries (freedesktop #54271)
+Version: 1.2.3
+
diff --git a/tests/lib1/intermediary-1.pc b/tests/lib1/intermediary-1.pc
new file mode 100644
index 000000000000..38ee4830ca38
--- /dev/null
+++ b/tests/lib1/intermediary-1.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: intermediary-1
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -lintermediary-1 -lfoo -lbar -lbaz
+Cflags:
diff --git a/tests/lib1/intermediary-2.pc b/tests/lib1/intermediary-2.pc
new file mode 100644
index 000000000000..188ce6b0955f
--- /dev/null
+++ b/tests/lib1/intermediary-2.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: intermediary-2
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -lintermediary-2 -lfoo -lbar -lbaz
+Cflags:
diff --git a/tests/lib1/isystem.pc b/tests/lib1/isystem.pc
new file mode 100644
index 000000000000..98416b54ff34
--- /dev/null
+++ b/tests/lib1/isystem.pc
@@ -0,0 +1,4 @@
+Name: Bad
+Description: Demonstrates problems with -isystem in both old pkg-config and current pkgconf; see also https://bugs.freedesktop.org/show_bug.cgi?id=72584
+Version: 1
+Cflags: -isystem /opt/bad/include -isystem /opt/bad2/include
diff --git a/tests/lib1/malformed-1.pc b/tests/lib1/malformed-1.pc
new file mode 100644
index 000000000000..4f4a700f017d
--- /dev/null
+++ b/tests/lib1/malformed-1.pc
@@ -0,0 +1,2 @@
+prefix=/usr\nName: GKrellM\nDescription: Extensible GTK system monitoring application\nVersion: 2.3.7\nRequires: gtk+-2.0 >= 2.4.0\nCflags: -I/usr/include\n
+
diff --git a/tests/lib1/malformed-quoting.pc b/tests/lib1/malformed-quoting.pc
new file mode 100644
index 000000000000..b8a324edadda
--- /dev/null
+++ b/tests/lib1/malformed-quoting.pc
@@ -0,0 +1,4 @@
+Name: malformed-quoting
+Version: 1
+Description: None.
+Cflags: '-I/ABC
diff --git a/tests/lib1/malformed-version.pc b/tests/lib1/malformed-version.pc
new file mode 100644
index 000000000000..41222cc358b3
--- /dev/null
+++ b/tests/lib1/malformed-version.pc
@@ -0,0 +1,5 @@
+Name: malformed-version
+Version: 3.922 2018-03-17
+Description: None.
+Cflags: -Ifoo
+Libs: -lbar
diff --git a/tests/lib1/metapackage-1.pc b/tests/lib1/metapackage-1.pc
new file mode 100644
index 000000000000..bb59db1a7406
--- /dev/null
+++ b/tests/lib1/metapackage-1.pc
@@ -0,0 +1,6 @@
+Name: metapackage-1
+Version: 0.1
+Description: metapackage for testing purposes
+Requires.private: metapackage-2
+Libs: -lmetapackage-1
+Cflags: -I/metapackage-1
diff --git a/tests/lib1/metapackage-2.pc b/tests/lib1/metapackage-2.pc
new file mode 100644
index 000000000000..d8bf7d6b37a1
--- /dev/null
+++ b/tests/lib1/metapackage-2.pc
@@ -0,0 +1,5 @@
+Name: metapackage-2
+Version: 0.1
+Description: metapackage for testing purposes
+Libs: -lmetapackage-2
+Cflags: -I/metapackage-2
diff --git a/tests/lib1/metapackage-3.pc b/tests/lib1/metapackage-3.pc
new file mode 100644
index 000000000000..111954a04fce
--- /dev/null
+++ b/tests/lib1/metapackage-3.pc
@@ -0,0 +1,4 @@
+Name: metapackage-3
+Version: 0.1
+Description: metapackage for testing purposes
+Requires.private: bar
diff --git a/tests/lib1/metapackage.pc b/tests/lib1/metapackage.pc
new file mode 100644
index 000000000000..3ce0db354a35
--- /dev/null
+++ b/tests/lib1/metapackage.pc
@@ -0,0 +1,4 @@
+Name: metapackage
+Version: 0.1
+Description: metapackage for testing purposes
+Requires: metapackage-1, metapackage-2
diff --git a/tests/lib1/missing-require.pc b/tests/lib1/missing-require.pc
new file mode 100644
index 000000000000..ede148d85e8c
--- /dev/null
+++ b/tests/lib1/missing-require.pc
@@ -0,0 +1,11 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: missing-require
+Description: A testing pkg-config file
+Version: 1.2.3
+Requires.private: missing
+Libs: -L${libdir} -lfoo
+Cflags: -fPIC -I${includedir}/foo
diff --git a/tests/lib1/multiline-bogus.pc b/tests/lib1/multiline-bogus.pc
new file mode 100644
index 000000000000..c7f00653c7ca
--- /dev/null
+++ b/tests/lib1/multiline-bogus.pc
@@ -0,0 +1,9 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: multiline
+Description: A multiline
+description blah blah blah
+Version: 1.2.3
diff --git a/tests/lib1/multiline.pc b/tests/lib1/multiline.pc
new file mode 100644
index 000000000000..75b5902f894c
--- /dev/null
+++ b/tests/lib1/multiline.pc
@@ -0,0 +1,9 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: multiline
+Description: A multiline \
+ description
+Version: 1.2.3
diff --git a/tests/lib1/no-trailing-newline.pc b/tests/lib1/no-trailing-newline.pc
new file mode 100644
index 000000000000..eaf2f8f21910
--- /dev/null
+++ b/tests/lib1/no-trailing-newline.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: no-trailing-newline
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir}/no-trailing-newline -lno-trailing-newline
+Cflags: -I${includedir}/no-trailing-newline
diff --git a/tests/lib1/nocflag.pc b/tests/lib1/nocflag.pc
new file mode 100644
index 000000000000..0a3d7524cf4e
--- /dev/null
+++ b/tests/lib1/nocflag.pc
@@ -0,0 +1,9 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: foo
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lfoo
diff --git a/tests/lib1/nolib.pc b/tests/lib1/nolib.pc
new file mode 100644
index 000000000000..b47e30c27a8e
--- /dev/null
+++ b/tests/lib1/nolib.pc
@@ -0,0 +1,9 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: foo
+Description: A testing pkg-config file
+Version: 1.2.3
+Cflags: -fPIC -I${includedir}/foo
diff --git a/tests/lib1/omg-sysroot-uninstalled.pc b/tests/lib1/omg-sysroot-uninstalled.pc
new file mode 100644
index 000000000000..7e391b951e18
--- /dev/null
+++ b/tests/lib1/omg-sysroot-uninstalled.pc
@@ -0,0 +1,10 @@
+prefix=${pc_sysrootdir}/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: omg-sysroot
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lomg
+Cflags: -fPIC -I${includedir}/omg
diff --git a/tests/lib1/omg-uninstalled.pc b/tests/lib1/omg-uninstalled.pc
new file mode 100644
index 000000000000..17d8078aa68a
--- /dev/null
+++ b/tests/lib1/omg-uninstalled.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: omg
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lomg
+Cflags: -fPIC -I${includedir}/omg
diff --git a/tests/lib1/orphaned-requires-private.pc b/tests/lib1/orphaned-requires-private.pc
new file mode 100644
index 000000000000..c62bab4459eb
--- /dev/null
+++ b/tests/lib1/orphaned-requires-private.pc
@@ -0,0 +1,8 @@
+Name: orphaned-requires-private
+Version: 1
+Description: A testing module for pkgconf
+URL: http://www.pkgconf.org/
+Requires: foo
+Requires.private: orphaned-foo
+Cflags: -DUNREACHABLE
+Libs:
diff --git a/tests/lib1/paren-quoting.pc b/tests/lib1/paren-quoting.pc
new file mode 100644
index 000000000000..08ed49d0258e
--- /dev/null
+++ b/tests/lib1/paren-quoting.pc
@@ -0,0 +1,5 @@
+Name: paren-quoting
+Description: Another pkg-config test
+Version: 1.3
+Libs: -L$(libdir) -ltilde
+Cflags: -I$(includedir)
diff --git a/tests/lib1/pcfiledir.pc b/tests/lib1/pcfiledir.pc
new file mode 100644
index 000000000000..fa6ada923c42
--- /dev/null
+++ b/tests/lib1/pcfiledir.pc
@@ -0,0 +1,8 @@
+prefix=${pcfiledir}
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: foo
+Description: A testing pkg-config file with a ${pcfiledir}
+Version: 1.2.3
diff --git a/tests/lib1/prefix-foo1.pc b/tests/lib1/prefix-foo1.pc
new file mode 100644
index 000000000000..7a6e90089646
--- /dev/null
+++ b/tests/lib1/prefix-foo1.pc
@@ -0,0 +1,12 @@
+prefix=/test/bar
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: prefix-foo1
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lfoo1
+Cflags: -fPIC -I${includedir}/foo -DBAR
+Cflags.private: -DFOO_STATIC
+
diff --git a/tests/lib1/prefix-foo2.pc b/tests/lib1/prefix-foo2.pc
new file mode 100644
index 000000000000..0546975e2307
--- /dev/null
+++ b/tests/lib1/prefix-foo2.pc
@@ -0,0 +1,12 @@
+prefix=/test/bar
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: prefix-foo2
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lfoo2
+Cflags: -fPIC -I${includedir}/foo -DFOO
+Cflags.private: -DFOO_STATIC
+
diff --git a/tests/lib1/private-libs-duplication.pc b/tests/lib1/private-libs-duplication.pc
new file mode 100644
index 000000000000..7f0f57343609
--- /dev/null
+++ b/tests/lib1/private-libs-duplication.pc
@@ -0,0 +1,7 @@
+Name: private-libs-duplication
+Description: test
+Version: 1.0
+Requires: baz bar
+Libs: -lprivate
+Libs.private: -lfoo
+
diff --git a/tests/lib1/provides-request-simple.pc b/tests/lib1/provides-request-simple.pc
new file mode 100644
index 000000000000..7e12c2d40dd8
--- /dev/null
+++ b/tests/lib1/provides-request-simple.pc
@@ -0,0 +1,6 @@
+Name: provides-request-simple
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -lfoo
+Cflags: -I/usr/include/foo
+Requires: provides-test-foo
diff --git a/tests/lib1/provides.pc b/tests/lib1/provides.pc
new file mode 100644
index 000000000000..0480c7375c88
--- /dev/null
+++ b/tests/lib1/provides.pc
@@ -0,0 +1,6 @@
+Name: provides
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -lfoo
+Cflags: -I/usr/include/foo
+Provides: provides-test-foo = 1.0.0, provides-test-bar > 1.1.0, provides-test-baz >= 1.1.0, provides-test-quux < 1.2.0, provides-test-moo <= 1.2.0, provides-test-meow != 1.3.0
diff --git a/tests/lib1/quotes.pc b/tests/lib1/quotes.pc
new file mode 100644
index 000000000000..3213fbe5a1ea
--- /dev/null
+++ b/tests/lib1/quotes.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: quotes
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lfoo
+Cflags: -DQUOTED=\"bla\" -DA=\"escaped\ string\'\ \literal\" -DB="\1\$" -DC='bla'
diff --git a/tests/lib1/requires-internal-2.pc b/tests/lib1/requires-internal-2.pc
new file mode 100644
index 000000000000..84677205cf31
--- /dev/null
+++ b/tests/lib1/requires-internal-2.pc
@@ -0,0 +1,9 @@
+prefix=/test/local
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: requires-internal
+Description: A testing pkg-config file
+Version: 1.2.3
+Requires.internal: cflags-internal
diff --git a/tests/lib1/requires-internal-collision.pc b/tests/lib1/requires-internal-collision.pc
new file mode 100644
index 000000000000..2b257cf4f9ec
--- /dev/null
+++ b/tests/lib1/requires-internal-collision.pc
@@ -0,0 +1,10 @@
+prefix=/test/local
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: requires-internal
+Description: A testing pkg-config file
+Version: 1.2.3
+Requires.internal: cflags-internal
+Requires.private: cflags-internal
diff --git a/tests/lib1/requires-internal-missing.pc b/tests/lib1/requires-internal-missing.pc
new file mode 100644
index 000000000000..8c55a1ddfa63
--- /dev/null
+++ b/tests/lib1/requires-internal-missing.pc
@@ -0,0 +1,10 @@
+prefix=/test/local
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: requires-internal
+Description: A testing pkg-config file
+Version: 1.2.3
+Requires.internal: static-libs, missing
+Requires.private: foo
diff --git a/tests/lib1/requires-internal.pc b/tests/lib1/requires-internal.pc
new file mode 100644
index 000000000000..b7d5a05d93bd
--- /dev/null
+++ b/tests/lib1/requires-internal.pc
@@ -0,0 +1,10 @@
+prefix=/test/local
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: requires-internal
+Description: A testing pkg-config file
+Version: 1.2.3
+Requires.internal: static-libs
+Requires.private: foo
diff --git a/tests/lib1/spaces-in-paths.pc b/tests/lib1/spaces-in-paths.pc
new file mode 100644
index 000000000000..490624e84c63
--- /dev/null
+++ b/tests/lib1/spaces-in-paths.pc
@@ -0,0 +1,7 @@
+prefix=/test\ with\ spaces
+includedir=${prefix}/include
+
+Name: spaces-in-paths
+Version: 1
+Description: test package for properly expanding spaces in variables
+Cflags: -I${includedir} -I${includedir}/subdir
diff --git a/tests/lib1/static-archive-libs.pc b/tests/lib1/static-archive-libs.pc
new file mode 100644
index 000000000000..95f7bfba92ad
--- /dev/null
+++ b/tests/lib1/static-archive-libs.pc
@@ -0,0 +1,10 @@
+prefix=/test/local
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: static-archive-libs
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: /libfoo.a -pthread
+Cflags: -I${includedir}/foo
diff --git a/tests/lib1/static-libs.pc b/tests/lib1/static-libs.pc
new file mode 100644
index 000000000000..075b331ff193
--- /dev/null
+++ b/tests/lib1/static-libs.pc
@@ -0,0 +1,12 @@
+prefix=/test/local
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: static-libs
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -lbar
+Libs.private: -lbar-private
+Cflags: -I${includedir}/foo
+Requires.private: foo
diff --git a/tests/lib1/sysroot-dir-2.pc b/tests/lib1/sysroot-dir-2.pc
new file mode 100644
index 000000000000..31f3ffdb0673
--- /dev/null
+++ b/tests/lib1/sysroot-dir-2.pc
@@ -0,0 +1,7 @@
+prefix=${pc_sysrootdir}/usr
+
+Name: sysroot-dir
+Description: Package for testing whether sysroot dirs are repeated or not
+Version: 1
+CFlags: -I${prefix}/include
+Libs: -L${prefix}/lib -lfoo
diff --git a/tests/lib1/sysroot-dir-3.pc b/tests/lib1/sysroot-dir-3.pc
new file mode 100644
index 000000000000..8c53a453a442
--- /dev/null
+++ b/tests/lib1/sysroot-dir-3.pc
@@ -0,0 +1,9 @@
+prefix=/sysroot/usr
+includedir=${prefix}/include
+libdir=${prefix}/lib
+
+Name: sysroot-dir
+Description: Package for testing whether sysroot dirs are repeated or not
+Version: 1
+CFlags: -I${includedir}
+Libs: -L${libdir} -lfoo
diff --git a/tests/lib1/sysroot-dir-4.pc b/tests/lib1/sysroot-dir-4.pc
new file mode 100644
index 000000000000..524dc7c17cf3
--- /dev/null
+++ b/tests/lib1/sysroot-dir-4.pc
@@ -0,0 +1,9 @@
+prefix=${pc_sysrootdir}/usr
+includedir=${prefix}/include
+libdir=${prefix}/lib
+
+Name: sysroot-dir
+Description: Package for testing whether sysroot dirs are repeated or not
+Version: 1
+CFlags: -I${includedir}
+Libs: -L${libdir} -lfoo
diff --git a/tests/lib1/sysroot-dir-5.pc b/tests/lib1/sysroot-dir-5.pc
new file mode 100644
index 000000000000..8c53a453a442
--- /dev/null
+++ b/tests/lib1/sysroot-dir-5.pc
@@ -0,0 +1,9 @@
+prefix=/sysroot/usr
+includedir=${prefix}/include
+libdir=${prefix}/lib
+
+Name: sysroot-dir
+Description: Package for testing whether sysroot dirs are repeated or not
+Version: 1
+CFlags: -I${includedir}
+Libs: -L${libdir} -lfoo
diff --git a/tests/lib1/sysroot-dir.pc b/tests/lib1/sysroot-dir.pc
new file mode 100644
index 000000000000..8334c5019728
--- /dev/null
+++ b/tests/lib1/sysroot-dir.pc
@@ -0,0 +1,5 @@
+Name: sysroot-dir
+Description: Package for testing whether sysroot dirs are repeated or not
+Version: 1
+CFlags: -I/sysroot/include
+Libs: -L/sysroot/lib -lfoo
diff --git a/tests/lib1/tilde-quoting.pc b/tests/lib1/tilde-quoting.pc
new file mode 100644
index 000000000000..4d37978c7487
--- /dev/null
+++ b/tests/lib1/tilde-quoting.pc
@@ -0,0 +1,5 @@
+Name: tilde
+Description: Another pkg-config test
+Version: 1.3
+Libs: -L~ -ltilde
+Cflags: -I~
diff --git a/tests/lib1/tilde.pc b/tests/lib1/tilde.pc
new file mode 100644
index 000000000000..a4002ecd29ec
--- /dev/null
+++ b/tests/lib1/tilde.pc
@@ -0,0 +1,11 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: Tilde version test
+Description: Test package for checking rpmvercmp ~ handling
+Version: 1.0.0~rc1
+Requires:
+Libs: -L${libdir} -ltilde
+Cflags: -I${includedir}
diff --git a/tests/lib1/truncated.pc b/tests/lib1/truncated.pc
new file mode 100644
index 000000000000..4977bc62c0e5
--- /dev/null
+++ b/tests/lib1/truncated.pc
@@ -0,0 +1 @@
+~ \ No newline at end of file
diff --git a/tests/lib1/tuple-quoting.pc b/tests/lib1/tuple-quoting.pc
new file mode 100644
index 000000000000..666ba4a7a46a
--- /dev/null
+++ b/tests/lib1/tuple-quoting.pc
@@ -0,0 +1,7 @@
+prefix="/test"
+libdir=${prefix}/lib
+
+Name: tuple-quoting
+Description: Another pkg-config test
+Version: 1.3
+Libs: -L${libdir} -lfoo
diff --git a/tests/lib1/typelibdir.pc b/tests/lib1/typelibdir.pc
new file mode 100644
index 000000000000..16e46c4729f0
--- /dev/null
+++ b/tests/lib1/typelibdir.pc
@@ -0,0 +1,11 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+typelibdir=${libdir}/typelibdir
+
+Name: test
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -llib-1
+Cflags:
diff --git a/tests/lib1/unavailable-provider.pc b/tests/lib1/unavailable-provider.pc
new file mode 100644
index 000000000000..a8eed6648b59
--- /dev/null
+++ b/tests/lib1/unavailable-provider.pc
@@ -0,0 +1,11 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: unavailable_provider
+Description: Provides an otherwise unavailable package
+Version: 1.2.3
+Provides: unavailable = 1.2.3
+Libs: -lunavailable
+Cflags:
diff --git a/tests/lib1/utf8.pc b/tests/lib1/utf8.pc
new file mode 100644
index 000000000000..6507ec0f25b9
--- /dev/null
+++ b/tests/lib1/utf8.pc
@@ -0,0 +1,10 @@
+prefix=/tëst
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: utf8
+Description: Library installed in a prefix with UTF-8
+Version: 0
+Libs: -L${libdir} -lutf8
+Cflags: -I${includedir}
diff --git a/tests/lib1/variable-whitespace.pc b/tests/lib1/variable-whitespace.pc
new file mode 100644
index 000000000000..8d8eab22cde2
--- /dev/null
+++ b/tests/lib1/variable-whitespace.pc
@@ -0,0 +1,11 @@
+
+prefix=/test
+exec_prefix= ${prefix}
+libdir= ${prefix}/lib64
+includedir=${prefix}/include
+
+Name: variable-whitespace
+Description: A test for variable whitespace
+Version: 2.12.3
+Libs: -lvariable-whitespace -L${libdir}
+Cflags: -I${includedir}
diff --git a/tests/lib2/foo.pc b/tests/lib2/foo.pc
new file mode 100644
index 000000000000..44588f243528
--- /dev/null
+++ b/tests/lib2/foo.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: foo
+Description: A testing pkg-config file
+Version: 1.2.3
+Libs: -L${libdir} -lfoo
+Cflags: -fPIC -I${includedir}/foo
diff --git a/tests/lib3/bar.pc b/tests/lib3/bar.pc
new file mode 100644
index 000000000000..9157d06b5fd6
--- /dev/null
+++ b/tests/lib3/bar.pc
@@ -0,0 +1,10 @@
+prefix=/test
+exec_prefix=${prefix}
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: bar
+Description: Another pkg-config test
+Version: 1.3
+Libs: -L${libdir} -lbar
+Requires: foo
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644
index 000000000000..56406c3bb0c8
--- /dev/null
+++ b/tests/meson.build
@@ -0,0 +1,23 @@
+configure_file(input: 'Kyuafile.in', output: 'Kyuafile', configuration: cdata)
+configure_file(input: 'test_env.sh.in', output: 'test_env.sh', configuration: cdata)
+
+
+tests = [
+ 'basic',
+ 'builtins',
+ 'conflicts',
+ 'framework',
+ 'parser',
+ 'provides',
+ 'regress',
+ 'requires',
+ 'symlink',
+ 'sysroot',
+ 'version',
+]
+
+
+# yuck
+foreach test : tests
+ test_file = configure_file(input: test + '.sh', output: test, copy: true)
+endforeach
diff --git a/tests/parser.sh b/tests/parser.sh
new file mode 100755
index 000000000000..9d9f9c6ff4ba
--- /dev/null
+++ b/tests/parser.sh
@@ -0,0 +1,372 @@
+#!/usr/bin/env atf-sh
+
+. $(atf_get_srcdir)/test_env.sh
+
+tests_init \
+ comments \
+ comments_in_fields \
+ dos \
+ no_trailing_newline \
+ argv_parse \
+ bad_option \
+ argv_parse_3 \
+ tilde_quoting \
+ paren_quoting \
+ multiline_field \
+ multiline_bogus_header \
+ escaped_backslash \
+ flag_order_1 \
+ flag_order_2 \
+ flag_order_3 \
+ flag_order_4 \
+ quoted \
+ variable_whitespace \
+ fragment_escaping_1 \
+ fragment_escaping_2 \
+ fragment_escaping_3 \
+ fragment_quoting \
+ fragment_quoting_2 \
+ fragment_quoting_3 \
+ fragment_quoting_5 \
+ fragment_quoting_7 \
+ fragment_comment \
+ msvc_fragment_quoting \
+ msvc_fragment_render_cflags \
+ tuple_dequote \
+ version_with_whitespace \
+ version_with_whitespace_2 \
+ version_with_whitespace_diagnostic \
+ fragment_groups \
+ fragment_groups_composite \
+ fragment_tree \
+ truncated \
+ c_comment
+
+comments_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-lfoo\n" \
+ pkgconf --libs comments
+}
+
+comments_in_fields_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-lfoo\n" \
+ pkgconf --libs comments-in-fields
+}
+
+dos_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib/dos-lineendings -ldos-lineendings\n" \
+ pkgconf --libs dos-lineendings
+}
+
+no_trailing_newline_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-I/test/include/no-trailing-newline\n" \
+ pkgconf --cflags no-trailing-newline
+}
+
+argv_parse_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-llib-3 -llib-1 -llib-2 -lpthread\n" \
+ pkgconf --libs argv-parse
+}
+
+bad_option_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -e ignore \
+ -s eq:1 \
+ pkgconf --exists -foo
+}
+
+argv_parse_3_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-llib-1 -pthread /test/lib/lib2.so\n" \
+ pkgconf --libs argv-parse-3
+}
+
+tilde_quoting_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L~ -ltilde\n" \
+ pkgconf --libs tilde-quoting
+ atf_check \
+ -o inline:"-I~\n" \
+ pkgconf --cflags tilde-quoting
+}
+
+paren_quoting_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L\$(libdir) -ltilde\n" \
+ pkgconf --libs paren-quoting
+}
+
+multiline_field_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -e ignore \
+ -o match:"multiline description" \
+ pkgconf --list-all
+}
+
+multiline_bogus_header_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -s eq:0 \
+ pkgconf --exists multiline-bogus
+}
+
+escaped_backslash_body()
+{
+ atf_check \
+ -e ignore \
+ -o inline:"-IC:\\\\\\\\A\n" \
+ pkgconf --with-path=${selfdir}/lib1 --cflags escaped-backslash
+}
+
+quoted_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-DQUOTED=\\\"bla\\\" -DA=\\\"escaped\\ string\\\'\\ literal\\\" -DB=\\\\\\1\$ -DC=bla\n" \
+ pkgconf --cflags quotes
+}
+
+flag_order_1_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -Bdynamic -lfoo -Bstatic -lbar\n" \
+ pkgconf --libs flag-order-1
+}
+
+flag_order_2_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -Bdynamic -lfoo -Bstatic -lbar -lfoo\n" \
+ pkgconf --libs flag-order-1 foo
+}
+
+flag_order_3_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -Wl,--start-group -lfoo -lbar -Wl,--end-group\n" \
+ pkgconf --libs flag-order-3
+}
+
+flag_order_4_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -Wl,--start-group -lfoo -lbar -Wl,--end-group -lfoo\n" \
+ pkgconf --libs flag-order-3 foo
+}
+
+variable_whitespace_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-I/test/include\n" \
+ pkgconf --cflags variable-whitespace
+}
+
+fragment_quoting_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -DQUOTED=\\\"/test/share/doc\\\"\n" \
+ pkgconf --cflags fragment-quoting
+}
+
+fragment_quoting_2_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -DQUOTED=/test/share/doc\n" \
+ pkgconf --cflags fragment-quoting-2
+}
+
+fragment_quoting_3_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -DQUOTED=\\\"/test/share/doc\\\"\n" \
+ pkgconf --cflags fragment-quoting-3
+}
+
+fragment_quoting_5_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -DQUOTED=/test/share/doc\n" \
+ pkgconf --cflags fragment-quoting-5
+}
+
+fragment_quoting_7_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-Dhello=10 -Dworld=+32 -DDEFINED_FROM_PKG_CONFIG=hello\\ world\n" \
+ pkgconf --cflags fragment-quoting-7
+}
+
+fragment_escaping_1_body()
+{
+ atf_check \
+ -o inline:"-IC:\\\\\\\\D\\ E\n" \
+ pkgconf --with-path="${selfdir}/lib1" --cflags fragment-escaping-1
+}
+
+fragment_escaping_2_body()
+{
+ atf_check \
+ -o inline:"-IC:\\\\\\\\D\\ E\n" \
+ pkgconf --with-path="${selfdir}/lib1" --cflags fragment-escaping-2
+}
+
+fragment_escaping_3_body()
+{
+ atf_check \
+ -o inline:"-IC:\\\\\\\\D\\ E\n" \
+ pkgconf --with-path="${selfdir}/lib1" --cflags fragment-escaping-3
+}
+
+fragment_quoting_7a_body()
+{
+ set -x
+
+ test_cflags=$(pkgconf --with-path=${selfdir}/lib1 --cflags fragment-quoting-7)
+ echo $test_cflags
+# test_cflags='-Dhello=10 -Dworld=+32 -DDEFINED_FROM_PKG_CONFIG=hello\\ world'
+
+ cat > test.c <<- __TESTCASE_END__
+ int main(int argc, char *argv[]) { return DEFINED_FROM_PKG_CONFIG; }
+ __TESTCASE_END__
+ cc -o test-fragment-quoting-7 ${test_cflags} ./test.c
+ atf_check -e 42 ./test-fragment-quoting-7
+ rm -f test.c test-fragment-quoting-7
+
+ set +x
+}
+
+
+fragment_comment_body()
+{
+ atf_check \
+ -o inline:'kuku=\#ttt\n' \
+ pkgconf --with-path="${selfdir}/lib1" --cflags fragment-comment
+}
+
+msvc_fragment_quoting_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:'/libpath:"C:\D E" E.lib \n' \
+ pkgconf --libs --msvc-syntax fragment-escaping-1
+}
+
+msvc_fragment_render_cflags_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:'/I/test/include/foo /DFOO_STATIC \n' \
+ pkgconf --cflags --static --msvc-syntax foo
+}
+
+tuple_dequote_body()
+{
+ atf_check \
+ -o inline:'-L/test/lib -lfoo\n' \
+ pkgconf --with-path="${selfdir}/lib1" --libs tuple-quoting
+}
+
+version_with_whitespace_body()
+{
+ atf_check \
+ -o inline:'3.922\n' \
+ pkgconf --with-path="${selfdir}/lib1" --modversion malformed-version
+}
+
+version_with_whitespace_2_body()
+{
+ atf_check \
+ -o inline:'malformed-version = 3.922\n' \
+ pkgconf --with-path="${selfdir}/lib1" --print-provides malformed-version
+}
+
+version_with_whitespace_diagnostic_body()
+{
+ atf_check \
+ -o match:warning \
+ pkgconf --with-path="${selfdir}/lib1" --validate malformed-version
+}
+
+fragment_groups_body()
+{
+ atf_check \
+ -o inline:'-Wl,--start-group -la -lb -Wl,--end-group -nodefaultlibs -Wl,--start-group -la -lgcc -Wl,--end-group -Wl,--gc-sections\n' \
+ pkgconf --with-path="${selfdir}/lib1" --libs fragment-groups
+}
+
+fragment_groups_composite_body()
+{
+ atf_check \
+ -o inline:'-Wl,--start-group -la -lb -Wl,--end-group -nodefaultlibs -Wl,--start-group -la -lgcc -Wl,--end-group -Wl,--gc-sections\n' \
+ pkgconf --with-path="${selfdir}/lib1" --libs fragment-groups-2
+}
+
+truncated_body()
+{
+ atf_check \
+ -o match:warning -s exit:1 \
+ pkgconf --with-path="${selfdir}/lib1" --validate truncated
+}
+
+c_comment_body()
+{
+ atf_check \
+ -o match:warning \
+ pkgconf --with-path="${selfdir}/lib1" --validate c-comment
+}
+
+fragment_tree_body()
+{
+ atf_check \
+ -o inline:"'-Wl,--start-group' [untyped]
+ '-la' [type l]
+ '-lb' [type l]
+ '-Wl,--end-group' [untyped]
+
+'-nodefaultlibs' [untyped]
+'-Wl,--start-group' [untyped]
+ '-la' [type l]
+ '-lgcc' [type l]
+ '-Wl,--end-group' [untyped]
+
+'-Wl,--gc-sections' [untyped]
+
+" \
+ pkgconf --with-path="${selfdir}/lib1" --fragment-tree fragment-groups-2
+}
+
diff --git a/tests/provides.sh b/tests/provides.sh
new file mode 100755
index 000000000000..b699dbf563f1
--- /dev/null
+++ b/tests/provides.sh
@@ -0,0 +1,310 @@
+#!/usr/bin/env atf-sh
+
+. $(atf_get_srcdir)/test_env.sh
+
+tests_init \
+ simple \
+ foo \
+ bar \
+ baz \
+ quux \
+ moo \
+ meow \
+ indirect_dependency_node
+
+simple_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+OUTPUT="provides-test-foo = 1.0.0
+provides-test-bar > 1.1.0
+provides-test-baz >= 1.1.0
+provides-test-quux < 1.2.0
+provides-test-moo <= 1.2.0
+provides-test-meow != 1.3.0
+provides = 1.2.3
+"
+ atf_check \
+ -o inline:"${OUTPUT}" \
+ pkgconf --print-provides provides
+ atf_check \
+ -o inline:"-lfoo\n" \
+ pkgconf --libs provides-request-simple
+ atf_check \
+ -e ignore \
+ -s exit:1 \
+ pkgconf --no-provides --libs provides-request-simple
+}
+
+foo_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o ignore \
+ pkgconf --libs provides-test-foo
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-foo = 1.0.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-foo >= 1.0.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-foo <= 1.0.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-foo != 1.0.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-foo > 1.0.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-foo < 1.0.0'
+}
+
+bar_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o ignore \
+ pkgconf --libs provides-test-bar
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-bar = 1.1.1'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-bar >= 1.1.1'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-bar <= 1.1.1'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-bar != 1.1.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-bar != 1.1.1'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-bar > 1.1.1'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-bar <= 1.1.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-bar <= 1.2.0'
+}
+
+baz_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o ignore \
+ pkgconf --libs provides-test-baz
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-baz = 1.1.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-baz >= 1.1.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-baz <= 1.1.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-baz != 1.1.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-baz != 1.0.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-baz > 1.1.1'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-baz > 1.1.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-baz < 1.1.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-baz < 1.2.0'
+}
+
+quux_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o ignore \
+ pkgconf --libs provides-test-quux
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux = 1.1.9'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux >= 1.1.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux >= 1.1.9'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux >= 1.2.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux <= 1.2.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux <= 1.1.9'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux != 1.2.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux != 1.1.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux != 1.0.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux > 1.1.9'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux > 1.2.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux < 1.1.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-quux > 1.2.0'
+}
+
+moo_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o ignore \
+ pkgconf --libs provides-test-moo
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-moo = 1.2.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-moo >= 1.1.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-moo >= 1.2.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-moo >= 1.2.1'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-moo <= 1.2.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-moo != 1.1.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-moo != 1.0.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-moo > 1.1.9'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-moo > 1.2.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-moo < 1.1.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-moo < 1.2.0'
+}
+
+meow_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o ignore \
+ pkgconf --libs provides-test-meow
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-meow = 1.3.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-meow != 1.3.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-meow > 1.2.9'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-meow < 1.3.1'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-meow < 1.3.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-meow > 1.3.0'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-meow >= 1.3.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-meow >= 1.3.1'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --libs 'provides-test-meow <= 1.3.0'
+ atf_check \
+ -o ignore \
+ pkgconf --libs 'provides-test-meow < 1.2.9'
+}
+
+indirect_dependency_node_body()
+{
+ atf_check \
+ -o inline:'1.2.3\n' \
+ pkgconf --with-path="${selfdir}/lib1" --modversion 'provides-test-meow'
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ pkgconf --with-path="${selfdir}/lib1" --modversion 'provides-test-meow = 1.3.0'
+}
diff --git a/tests/regress.sh b/tests/regress.sh
new file mode 100755
index 000000000000..b264f3593f90
--- /dev/null
+++ b/tests/regress.sh
@@ -0,0 +1,354 @@
+#!/usr/bin/env atf-sh
+
+. $(atf_get_srcdir)/test_env.sh
+
+tests_init \
+ case_sensitivity \
+ depgraph_break_1 \
+ depgraph_break_2 \
+ depgraph_break_3 \
+ define_variable \
+ define_variable_override \
+ variable \
+ keep_system_libs \
+ libs \
+ libs_only \
+ libs_never_mergeback \
+ cflags_only \
+ cflags_never_mergeback \
+ incomplete_libs \
+ incomplete_cflags \
+ isystem_munge_order \
+ isystem_munge_sysroot \
+ idirafter_munge_order \
+ idirafter_munge_sysroot \
+ idirafter_ordering \
+ modversion_common_prefix \
+ modversion_fullpath \
+ modversion_provides \
+ modversion_uninstalled \
+ modversion_one_word_expression \
+ modversion_two_word_expression \
+ modversion_three_word_expression \
+ modversion_one_word_expression_no_space \
+ modversion_one_word_expression_no_space_zero \
+ pcpath \
+ virtual_variable \
+ fragment_collision \
+ malformed_1 \
+ malformed_quoting \
+ explicit_sysroot \
+ empty_tuple \
+ solver_requires_private_debounce \
+ billion_laughs \
+ define_prefix_child_prefix_1 \
+ define_prefix_child_prefix_1_env
+
+# sysroot_munge \
+
+case_sensitivity_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"3\n" \
+ pkgconf --variable=foo case-sensitivity
+ atf_check \
+ -o inline:"4\n" \
+ pkgconf --variable=Foo case-sensitivity
+}
+
+depgraph_break_1_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check -s exit:1 -e ignore \
+ pkgconf --exists --print-errors 'foo > 0.6.0 foo < 0.8.0'
+}
+
+depgraph_break_2_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check -s exit:1 -e ignore \
+ pkgconf --exists --print-errors 'nonexisting foo <= 3'
+}
+
+depgraph_break_3_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check -s exit:1 -e ignore \
+ pkgconf --exists --print-errors 'depgraph-break'
+}
+
+define_variable_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check -o inline:"\\\${libdir}/typelibdir\n" \
+ pkgconf --variable=typelibdir --define-variable='libdir=\${libdir}' typelibdir
+}
+
+define_variable_override_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check -o inline:"/test\n" \
+ pkgconf --variable=prefix --define-variable='prefix=/test' typelibdir
+}
+
+variable_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"/test/include\n" \
+ pkgconf --variable=includedir foo
+}
+
+keep_system_libs_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ eval export "$LIBRARY_PATH_ENV"="/test/local/lib"
+ atf_check \
+ -o inline:"\n" \
+ pkgconf --libs-only-L cflags-libs-only
+
+ atf_check \
+ -o inline:"-L/test/local/lib\n" \
+ pkgconf --libs-only-L --keep-system-libs cflags-libs-only
+}
+
+libs_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/local/lib -lfoo\n" \
+ pkgconf --libs cflags-libs-only
+}
+
+libs_only_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/local/lib -lfoo\n" \
+ pkgconf --libs-only-L --libs-only-l cflags-libs-only
+}
+
+libs_never_mergeback_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/bar/lib -lfoo1\n" \
+ pkgconf --libs prefix-foo1
+ atf_check \
+ -o inline:"-L/test/bar/lib -lfoo1 -lfoo2\n" \
+ pkgconf --libs prefix-foo1 prefix-foo2
+}
+
+cflags_only_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-I/test/local/include/foo\n" \
+ pkgconf --cflags-only-I --cflags-only-other cflags-libs-only
+}
+
+cflags_never_mergeback_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-I/test/bar/include/foo -DBAR -fPIC -DFOO\n" \
+ pkgconf --cflags prefix-foo1 prefix-foo2
+}
+
+incomplete_libs_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"\n" \
+ pkgconf --libs incomplete
+}
+
+incomplete_cflags_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"\n" \
+ pkgconf --cflags incomplete
+}
+
+isystem_munge_order_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-isystem /opt/bad/include -isystem /opt/bad2/include\n" \
+ pkgconf --cflags isystem
+}
+
+isystem_munge_sysroot_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1" PKG_CONFIG_SYSROOT_DIR="${selfdir}"
+ atf_check \
+ -o match:"-isystem ${selfdir}/opt/bad/include" \
+ pkgconf --cflags isystem
+}
+
+idirafter_munge_order_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-idirafter /opt/bad/include -idirafter /opt/bad2/include\n" \
+ pkgconf --cflags idirafter
+}
+
+idirafter_munge_sysroot_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1" PKG_CONFIG_SYSROOT_DIR="${selfdir}"
+ atf_check \
+ -o match:"-idirafter ${selfdir}/opt/bad/include" \
+ pkgconf --cflags idirafter
+}
+
+idirafter_ordering_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-I/opt/bad/include1 -idirafter -I/opt/bad/include2 -I/opt/bad/include3\n" \
+ pkgconf --cflags idirafter-ordering
+}
+
+pcpath_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib2"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo\n" \
+ pkgconf --cflags ${selfdir}/lib3/bar.pc
+}
+
+sysroot_munge_body()
+{
+ sed "s|/sysroot/|${selfdir}/|g" ${selfdir}/lib1/sysroot-dir.pc > ${selfdir}/lib1/sysroot-dir-selfdir.pc
+ export PKG_CONFIG_PATH="${selfdir}/lib1" PKG_CONFIG_SYSROOT_DIR="${selfdir}"
+ atf_check \
+ -o inline:"-L${selfdir}/lib -lfoo\n" \
+ pkgconf --libs sysroot-dir-selfdir
+}
+
+virtual_variable_body()
+{
+ atf_check -s exit:0 \
+ pkgconf --exists pkg-config
+ atf_check -s exit:0 \
+ pkgconf --exists pkgconf
+
+ atf_check -o inline:"${pcpath}\n" \
+ pkgconf --variable=pc_path pkg-config
+ atf_check -o inline:"${pcpath}\n" \
+ pkgconf --variable=pc_path pkgconf
+}
+
+fragment_collision_body()
+{
+ atf_check -o inline:"-D_BAZ -D_BAR -D_FOO -D_THREAD_SAFE -pthread\n" \
+ pkgconf --with-path="${selfdir}/lib1" --cflags fragment-collision
+}
+
+malformed_1_body()
+{
+ atf_check -s exit:1 -o ignore \
+ pkgconf --validate --with-path="${selfdir}/lib1" malformed-1
+}
+
+malformed_quoting_body()
+{
+ atf_check -s exit:0 -o ignore \
+ pkgconf --validate --with-path="${selfdir}/lib1" malformed-quoting
+}
+
+explicit_sysroot_body()
+{
+ export PKG_CONFIG_SYSROOT_DIR=${selfdir}
+ atf_check -o inline:"${selfdir}/usr/share/test\n" \
+ pkgconf --with-path="${selfdir}/lib1" --variable=pkgdatadir explicit-sysroot
+}
+
+empty_tuple_body()
+{
+ atf_check -o inline:"\n" \
+ pkgconf --with-path="${selfdir}/lib1" --cflags empty-tuple
+}
+
+solver_requires_private_debounce_body()
+{
+ atf_check -o inline:"-I/metapackage-1 -I/metapackage-2 -lmetapackage-1 -lmetapackage-2\n" \
+ pkgconf --with-path="${selfdir}/lib1" --cflags --libs metapackage
+}
+
+billion_laughs_body()
+{
+ atf_check -o inline:"warning: truncating very long variable to 64KB\nwarning: truncating very long variable to 64KB\nwarning: truncating very long variable to 64KB\nwarning: truncating very long variable to 64KB\nwarning: truncating very long variable to 64KB\n" \
+ pkgconf --with-path="${selfdir}/lib1" --validate billion-laughs
+}
+
+modversion_common_prefix_body()
+{
+ atf_check -o inline:"foo: 1.2.3\nfoobar: 3.2.1\n" \
+ pkgconf --with-path="${selfdir}/lib1" --modversion --verbose foo foobar
+}
+
+modversion_fullpath_body()
+{
+ atf_check -o inline:"1.2.3\n" \
+ pkgconf --modversion "${selfdir}/lib1/foo.pc"
+}
+
+modversion_provides_body()
+{
+ atf_check -o inline:"1.2.3\n" \
+ pkgconf --with-path="${selfdir}/lib1" --modversion unavailable
+}
+
+modversion_uninstalled_body()
+{
+ atf_check -o inline:"1.2.3\n" \
+ pkgconf --with-path="${selfdir}/lib1" --modversion omg
+}
+
+modversion_one_word_expression_body()
+{
+ atf_check -o inline:"1.2.3\n" \
+ pkgconf --with-path="${selfdir}/lib1" --modversion "foo > 1.0"
+}
+
+modversion_two_word_expression_body()
+{
+ atf_check -o inline:"1.2.3\n" \
+ pkgconf --with-path="${selfdir}/lib1" --modversion foo "> 1.0"
+}
+
+modversion_three_word_expression_body()
+{
+ atf_check -o inline:"1.2.3\n" \
+ pkgconf --with-path="${selfdir}/lib1" --modversion foo ">" 1.0
+}
+
+modversion_one_word_expression_no_space_body()
+{
+ atf_check -o inline:"1.2.3\n" \
+ pkgconf --with-path="${selfdir}/lib1" --modversion "foo >1.0"
+}
+
+modversion_one_word_expression_no_space_zero_body()
+{
+ atf_check -o inline:"1.2.3\n" \
+ pkgconf --with-path="${selfdir}/lib1" --modversion "foo >0.5"
+}
+
+define_prefix_child_prefix_1_body()
+{
+ atf_check -o inline:"-I${selfdir}/lib1/include/child-prefix-1 -L${selfdir}/lib1/lib64 -lchild-prefix-1\n" \
+ pkgconf --with-path="${selfdir}/lib1/child-prefix/pkgconfig" --define-prefix --cflags --libs child-prefix-1
+}
+
+define_prefix_child_prefix_1_env_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1/child-prefix/pkgconfig"
+ export PKG_CONFIG_RELOCATE_PATHS=1
+ atf_check -o inline:"-I${selfdir}/lib1/include/child-prefix-1 -L${selfdir}/lib1/lib64 -lchild-prefix-1\n" \
+ pkgconf --cflags --libs child-prefix-1
+}
diff --git a/tests/requires.sh b/tests/requires.sh
new file mode 100755
index 000000000000..7a95a66cd232
--- /dev/null
+++ b/tests/requires.sh
@@ -0,0 +1,176 @@
+#!/usr/bin/env atf-sh
+
+. $(atf_get_srcdir)/test_env.sh
+
+tests_init \
+ libs \
+ libs_cflags \
+ libs_static \
+ libs_static_pure \
+ cflags_libs_private \
+ argv_parse2 \
+ static_cflags \
+ private_duplication \
+ private_duplication_digraph \
+ foo_bar \
+ bar_foo \
+ foo_metapackage_3 \
+ libs_static2 \
+ missing \
+ requires_internal \
+ requires_internal_missing \
+ requires_internal_collision \
+ orphaned_requires_private
+
+libs_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -lbar -lfoo\n" \
+ pkgconf --libs bar
+}
+
+libs_cflags_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -L/test/lib -lbaz\n" \
+ pkgconf --libs --cflags baz
+}
+
+libs_static_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -lbaz -L/test/lib -lzee -lfoo\n" \
+ pkgconf --static --libs baz
+}
+
+libs_static_pure_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-L/test/lib -lbaz -lfoo\n" \
+ pkgconf --static --pure --libs baz
+}
+
+argv_parse2_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-llib-1 -pthread /test/lib/lib2.so\n" \
+ pkgconf --static --libs argv-parse-2
+}
+
+static_cflags_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo -DFOO_STATIC\n" \
+ pkgconf --static --cflags baz
+}
+
+private_duplication_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-lprivate -lbaz -lzee -lbar -lfoo\n" \
+ pkgconf --static --libs-only-l private-libs-duplication
+}
+
+private_duplication_digraph_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o 'match:"user:request" -> "private-libs-duplication"' \
+ -o 'match:"private-libs-duplication" -> "bar"' \
+ -o 'match:"private-libs-duplication" -> "baz"' \
+ -o 'match:"bar" -> "foo"' \
+ -o 'match:"baz" -> "foo"' \
+ pkgconf --static --libs-only-l private-libs-duplication --digraph
+}
+
+bar_foo_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-lbar -lfoo\n" \
+ pkgconf --static --libs-only-l bar foo
+}
+
+foo_bar_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-lbar -lfoo\n" \
+ pkgconf --static --libs-only-l foo bar
+}
+
+foo_metapackage_3_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-lbar -lfoo\n" \
+ pkgconf --static --libs-only-l foo metapackage-3
+}
+
+libs_static2_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -o inline:"-lbar -lbar-private -L/test/lib -lfoo\n" \
+ pkgconf --static --libs static-libs
+}
+
+missing_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --cflags missing-require
+}
+
+requires_internal_body()
+{
+ atf_check \
+ -o inline:"-lbar -lbar-private -L/test/lib -lfoo\n" \
+ pkgconf --with-path="${selfdir}/lib1" --static --libs requires-internal
+}
+
+requires_internal_missing_body()
+{
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --with-path="${selfdir}/lib1" --static --libs requires-internal-missing
+}
+
+requires_internal_collision_body()
+{
+ atf_check \
+ -o inline:"-I/test/local/include/foo\n" \
+ pkgconf --with-path="${selfdir}/lib1" --cflags requires-internal-collision
+}
+
+orphaned_requires_private_body()
+{
+ atf_check \
+ -s exit:1 \
+ -e ignore \
+ -o ignore \
+ pkgconf --with-path="${selfdir}/lib1" --cflags --libs orphaned-requires-private
+}
+
+cflags_libs_private_body()
+{
+ atf_check \
+ -o inline:"\n" \
+ pkgconf --with-path="${selfdir}/lib1" --libs cflags-libs-private-a
+
+ atf_check \
+ -o inline:"-lc\n" \
+ pkgconf --with-path="${selfdir}/lib1" --static --libs cflags-libs-private-a
+}
diff --git a/tests/symlink.sh b/tests/symlink.sh
new file mode 100755
index 000000000000..7f49655755eb
--- /dev/null
+++ b/tests/symlink.sh
@@ -0,0 +1,122 @@
+#!/usr/bin/env atf-sh
+
+. $(atf_get_srcdir)/test_env.sh
+
+tests_init \
+ pcfiledir_symlink_absolute \
+ pcfiledir_symlink_relative
+
+# - We need to create a temporary subtree, since symlinks are not preserved
+# in "make dist".
+# - ${srcdir} is relative and since we need to compare paths, we would have
+# to portably canonicalize it again, which is hard. Instead, just keep
+# the whole thing nested.
+pcfiledir_symlink_absolute_body()
+{
+ mkdir -p tmp/child
+ cp -f "${selfdir}/lib1/pcfiledir.pc" tmp/child/
+ ln -f -s "${PWD}/tmp/child/pcfiledir.pc" tmp/pcfiledir.pc # absolute
+ ln -f -s tmp/pcfiledir.pc pcfiledir.pc
+
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir.pc
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix tmp/pcfiledir.pc
+ atf_check \
+ -o inline:"tmp/child\n" \
+ pkgconf --variable=prefix tmp/child/pcfiledir.pc
+
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix "${PWD}/pcfiledir.pc"
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix "${PWD}/tmp/pcfiledir.pc"
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix "${PWD}/tmp/child/pcfiledir.pc"
+
+ export PKG_CONFIG_PATH="."
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+ export PKG_CONFIG_PATH="${PWD}"
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+
+ export PKG_CONFIG_PATH="tmp"
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+ export PKG_CONFIG_PATH="${PWD}/tmp"
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+
+ export PKG_CONFIG_PATH="tmp/child"
+ atf_check \
+ -o inline:"tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+ export PKG_CONFIG_PATH="${PWD}/tmp/child"
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+}
+
+pcfiledir_symlink_relative_body()
+{
+ mkdir -p tmp/child
+ cp -f "${selfdir}/lib1/pcfiledir.pc" tmp/child/
+ ln -f -s child/pcfiledir.pc tmp/pcfiledir.pc # relative
+ ln -f -s tmp/pcfiledir.pc pcfiledir.pc
+
+ atf_check \
+ -o inline:"tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir.pc
+ atf_check \
+ -o inline:"tmp/child\n" \
+ pkgconf --variable=prefix tmp/pcfiledir.pc
+ atf_check \
+ -o inline:"tmp/child\n" \
+ pkgconf --variable=prefix tmp/child/pcfiledir.pc
+
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix "${PWD}/pcfiledir.pc"
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix "${PWD}/tmp/pcfiledir.pc"
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix "${PWD}/tmp/child/pcfiledir.pc"
+
+ export PKG_CONFIG_PATH="."
+ atf_check \
+ -o inline:"tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+ export PKG_CONFIG_PATH="${PWD}"
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+
+ export PKG_CONFIG_PATH="tmp"
+ atf_check \
+ -o inline:"tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+ export PKG_CONFIG_PATH="${PWD}/tmp"
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+
+ export PKG_CONFIG_PATH="tmp/child"
+ atf_check \
+ -o inline:"tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+ export PKG_CONFIG_PATH="${PWD}/tmp/child"
+ atf_check \
+ -o inline:"${PWD}/tmp/child\n" \
+ pkgconf --variable=prefix pcfiledir
+}
diff --git a/tests/sysroot.sh b/tests/sysroot.sh
new file mode 100755
index 000000000000..9d61eaea311c
--- /dev/null
+++ b/tests/sysroot.sh
@@ -0,0 +1,110 @@
+#!/usr/bin/env atf-sh
+
+. $(atf_get_srcdir)/test_env.sh
+
+tests_init \
+ cflags \
+ variable \
+ do_not_eat_slash \
+ do_not_duplicate_sysroot_dir \
+ uninstalled \
+ uninstalled_pkgconf1 \
+ uninstalled_fdo \
+ uninstalled_fdo_pc_sysrootdir
+
+do_not_eat_slash_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ export PKG_CONFIG_SYSROOT_DIR="/"
+ atf_check \
+ -o inline:"-fPIC -I/test/include/foo\n" \
+ pkgconf --cflags baz
+}
+
+cflags_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ export PKG_CONFIG_SYSROOT_DIR="${SYSROOT_DIR}"
+ atf_check \
+ -o inline:"-fPIC -I${SYSROOT_DIR}/test/include/foo\n" \
+ pkgconf --cflags baz
+}
+
+variable_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ export PKG_CONFIG_SYSROOT_DIR="${SYSROOT_DIR}"
+ atf_check \
+ -o inline:"${SYSROOT_DIR}/test\n" \
+ pkgconf --variable=prefix foo
+ atf_check \
+ -o inline:"${SYSROOT_DIR}/test/include\n" \
+ pkgconf --variable=includedir foo
+}
+
+do_not_duplicate_sysroot_dir_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ export PKG_CONFIG_SYSROOT_DIR="/sysroot"
+
+ atf_check \
+ -o inline:"-I/sysroot/usr/include\n" \
+ pkgconf --cflags sysroot-dir-2
+
+ atf_check \
+ -o inline:"-I/sysroot/usr/include\n" \
+ pkgconf --cflags sysroot-dir-3
+
+ atf_check \
+ -o inline:"-I/sysroot/usr/include\n" \
+ pkgconf --cflags sysroot-dir-5
+
+ export PKG_CONFIG_SYSROOT_DIR="${SYSROOT_DIR}"
+
+ atf_check \
+ -o inline:"-I${SYSROOT_DIR}/usr/include\n" \
+ pkgconf --cflags sysroot-dir-4
+}
+
+uninstalled_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ export PKG_CONFIG_SYSROOT_DIR="/sysroot"
+
+ atf_check \
+ -o inline:"-L/test/lib -lomg\n" \
+ pkgconf --libs omg
+}
+
+uninstalled_pkgconf1_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ export PKG_CONFIG_SYSROOT_DIR="/sysroot"
+ export PKG_CONFIG_PKGCONF1_SYSROOT_RULES="1"
+
+ atf_check \
+ -o inline:"-L/sysroot/test/lib -lomg\n" \
+ pkgconf --libs omg
+}
+
+uninstalled_fdo_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ export PKG_CONFIG_SYSROOT_DIR="/sysroot"
+ export PKG_CONFIG_FDO_SYSROOT_RULES="1"
+
+ atf_check \
+ -o inline:"-L/test/lib -lomg\n" \
+ pkgconf --libs omg
+}
+
+uninstalled_fdo_pc_sysrootdir_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ export PKG_CONFIG_SYSROOT_DIR="/sysroot"
+ export PKG_CONFIG_FDO_SYSROOT_RULES="1"
+
+ atf_check \
+ -o inline:"-L/sysroot/test/lib -lomg\n" \
+ pkgconf --libs omg-sysroot
+}
diff --git a/tests/test_env.sh.in b/tests/test_env.sh.in
new file mode 100644
index 000000000000..d2b6136f18c9
--- /dev/null
+++ b/tests/test_env.sh.in
@@ -0,0 +1,50 @@
+srcdir="$(atf_get_srcdir)"
+export PATH="$srcdir/..:${PATH}"
+
+#--- begin windows kludge ---
+# When building with Visual Studio, binaries are in a subdirectory named after the configration...
+# and the configuration is not known unless you're in the IDE, or something.
+# So just guess. This won't work well if you build more than one configuration.
+the_configuration=""
+for configuration in Debug Release RelWithDebInfo
+do
+ if test -d "$srcdir/../$configuration"
+ then
+ if test "$the_configuration" != ""
+ then
+ echo "test_env.sh: FAIL: more than one configuration found"
+ exit 1
+ fi
+ the_configuration=$configuration
+ export PATH="$srcdir/../${configuration}:${PATH}"
+ fi
+done
+#--- end kludge ---
+
+selfdir="@abs_top_srcdir@/tests"
+LIBRARY_PATH_ENV="LIBRARY_PATH"
+PATH_SEP=":"
+SYSROOT_DIR="${selfdir}"
+case "$(uname -s)" in
+Haiku) LIBRARY_PATH_ENV="BELIBRARIES";;
+esac
+
+prefix="@prefix@"
+exec_prefix="@exec_prefix@"
+datarootdir="@datarootdir@"
+pcpath="@PKG_DEFAULT_PATH@"
+
+tests_init()
+{
+ TESTS="$@"
+ export TESTS
+ for t ; do
+ atf_test_case $t
+ done
+}
+
+atf_init_test_cases() {
+ for t in ${TESTS}; do
+ atf_add_test_case $t
+ done
+}
diff --git a/tests/version.sh b/tests/version.sh
new file mode 100755
index 000000000000..0172c0437da6
--- /dev/null
+++ b/tests/version.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env atf-sh
+
+. $(atf_get_srcdir)/test_env.sh
+
+tests_init \
+ atleast \
+ exact \
+ max
+
+atleast_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ pkgconf --atleast-version 1.0 foo
+ atf_check \
+ -s exit:1 \
+ pkgconf --atleast-version 2.0 foo
+}
+
+exact_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -s exit:1 \
+ pkgconf --exact-version 1.0 foo
+ atf_check \
+ pkgconf --exact-version 1.2.3 foo
+}
+
+max_body()
+{
+ export PKG_CONFIG_PATH="${selfdir}/lib1"
+ atf_check \
+ -s exit:1 \
+ pkgconf --max-version 1.0 foo
+ atf_check \
+ pkgconf --max-version 2.0 foo
+}
diff --git a/txt2rtf.py b/txt2rtf.py
new file mode 100644
index 000000000000..111ba3742142
--- /dev/null
+++ b/txt2rtf.py
@@ -0,0 +1,35 @@
+def text_to_rtf(input_file: str, output_file: str) -> None:
+ with open(input_file, "r", encoding="utf-8") as file:
+ text_content = file.read()
+
+ text_content = text_content.replace("\\", "\\\\")
+ text_content = text_content.replace("{", "\\{")
+ text_content = text_content.replace("}", "\\}")
+
+ text_content = text_content.replace("\n", "\\par\n")
+
+ rtf_content = "{\\rtf1\\ansi\\ansicpg1252\\cocoartf2580\\cocoasubrtf220\n"
+ rtf_content += "{\\fonttbl\\f0\\fswiss\\fcharset0 Helvetica;}\n"
+ rtf_content += "\\vieww12000\\viewh15840\\viewkind0\n"
+ rtf_content += "\\pard\\tx720\\tx1440\\tx2160\\tx2880\\tx3600\\tx4320\\pardirnatural\\partightenfactor0\n"
+ rtf_content += "\\f0\\fs24 "
+ rtf_content += text_content
+ rtf_content += "\n}"
+
+ with open(output_file, "w", encoding="utf-8") as file:
+ file.write(rtf_content)
+
+ print(f"Conversion complete! RTF file saved as: {output_file}")
+
+
+if __name__ == "__main__":
+ import sys
+
+ if len(sys.argv) != 3:
+ print(f"Usage: python {sys.argv[0]} input.txt output.rtf")
+ sys.exit(1)
+
+ input_file = sys.argv[1]
+ output_file = sys.argv[2]
+
+ text_to_rtf(input_file, output_file)