1 /*
2 * main.c
3 * main() routine
4 *
5 * SPDX-License-Identifier: pkgconf
6 *
7 * Copyright (c) 2011-2025 pkgconf authors (see AUTHORS).
8 *
9 * Permission to use, copy, modify, and/or distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * This software is provided 'as is' and without any warranty, express or
14 * implied. In no event shall the authors be liable for any damages arising
15 * from the use of this software.
16 */
17
18 #include "libpkgconf/config.h"
19 #include <libpkgconf/stdinc.h>
20 #include <libpkgconf/libpkgconf.h>
21 #include "getopt_long.h"
22 #include "core.h"
23
24 static const char *
environ_lookup_handler(const pkgconf_client_t * client,const char * key)25 environ_lookup_handler(const pkgconf_client_t *client, const char *key)
26 {
27 (void) client;
28
29 return getenv(key);
30 }
31
32 static bool
error_handler(const char * msg,const pkgconf_client_t * client,void * data)33 error_handler(const char *msg, const pkgconf_client_t *client, void *data)
34 {
35 (void) data;
36 pkgconf_cli_state_t *state = client->client_data;
37
38 if (state->error_msgout == NULL)
39 return true;
40
41 pkgconf_output_file_fmt(state->error_msgout, "%s", msg);
42 return true;
43 }
44
45 static void
unveil_handler(const pkgconf_client_t * client,const char * path,const char * permissions)46 unveil_handler(const pkgconf_client_t *client, const char *path, const char *permissions)
47 {
48 (void) client;
49
50 if (pkgconf_unveil(path, permissions) == -1 && errno != ENOENT)
51 {
52 pkgconf_output_file_fmt(stderr, "pkgconf: unveil failed: %s\n", strerror(errno));
53 exit(EXIT_FAILURE);
54 }
55 }
56
57 #ifndef PKGCONF_LITE
58 static pkgconf_cross_personality_t *
deduce_personality(char * argv[])59 deduce_personality(char *argv[])
60 {
61 const char *argv0 = argv[0];
62 char *i, *prefix;
63 pkgconf_cross_personality_t *out;
64
65 i = strrchr(argv0, '/');
66 if (i != NULL)
67 argv0 = i + 1;
68
69 #if defined(_WIN32) || defined(_WIN64)
70 i = strrchr(argv0, '\\');
71 if (i != NULL)
72 argv0 = i + 1;
73 #endif
74
75 i = strstr(argv0, "-pkg");
76 if (i == NULL)
77 return pkgconf_cross_personality_default();
78
79 prefix = pkgconf_strndup(argv0, i - argv0);
80 out = pkgconf_cross_personality_find(prefix);
81 free(prefix);
82 if (out == NULL)
83 return pkgconf_cross_personality_default();
84
85 return out;
86 }
87 #endif
88
89 #ifdef _WIN32
90 static UINT original_console_cp;
91 static UINT original_console_out_cp;
92
93 static void
restore_console_code_page(void)94 restore_console_code_page(void)
95 {
96 SetConsoleCP(original_console_cp);
97 SetConsoleOutputCP(original_console_out_cp);
98 }
99 #endif
100
101 static void
version(void)102 version(void)
103 {
104 printf("%s\n", PACKAGE_VERSION);
105 }
106
107 static void
about(void)108 about(void)
109 {
110 printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
111 printf("Copyright (c) 2011-2026 pkgconf authors (see AUTHORS in documentation directory)\n\n");
112 printf("Permission to use, copy, modify, and/or distribute this software for any\n");
113 printf("purpose with or without fee is hereby granted, provided that the above\n");
114 printf("copyright notice and this permission notice appear in all copies.\n\n");
115 printf("This software is provided 'as is' and without any warranty, express or\n");
116 printf("implied. In no event shall the authors be liable for any damages arising\n");
117 printf("from the use of this software.\n\n");
118 printf("Report bugs at <%s>.\n", PACKAGE_BUGREPORT);
119 }
120
121 static void
usage(void)122 usage(void)
123 {
124 printf("usage: %s [OPTIONS] [LIBRARIES]\n", PACKAGE_NAME);
125
126 printf("\nbasic options:\n\n");
127
128 printf(" --help this message\n");
129 printf(" --about print pkgconf version and license to stdout\n");
130 printf(" --version print supported pkg-config version to stdout\n");
131 printf(" --verbose print additional information\n");
132 printf(" --atleast-pkgconfig-version check whether or not pkgconf is compatible\n");
133 printf(" with a specified pkg-config version\n");
134 printf(" --errors-to-stdout print all errors on stdout instead of stderr\n");
135 printf(" --print-errors ensure all errors are printed\n");
136 printf(" --short-errors be less verbose about some errors\n");
137 printf(" --silence-errors explicitly be silent about errors\n");
138 printf(" --list-all list all known packages\n");
139 printf(" --list-package-names list all known package names\n");
140 #ifndef PKGCONF_LITE
141 printf(" --simulate simulate walking the calculated dependency graph\n");
142 #endif
143 printf(" --no-cache do not cache already seen packages when\n");
144 printf(" walking the dependency graph\n");
145 printf(" --log-file=filename write an audit log to a specified file\n");
146 printf(" --with-path=path adds a directory to the search path\n");
147 printf(" --define-prefix override the prefix variable with one that is guessed based on\n");
148 printf(" the location of the .pc file\n");
149 printf(" --dont-define-prefix do not override the prefix variable under any circumstances\n");
150 printf(" --prefix-variable=varname sets the name of the variable that pkgconf considers\n");
151 printf(" to be the package prefix\n");
152 printf(" --dont-relocate-paths disables path relocation support\n");
153
154 #ifndef PKGCONF_LITE
155 printf("\ncross-compilation personality support:\n\n");
156 printf(" --personality=triplet|filename sets the personality to 'triplet' or a file named 'filename'\n");
157 printf(" --dump-personality dumps details concerning selected personality\n");
158 #endif
159
160 printf("\nchecking specific pkg-config database entries:\n\n");
161
162 printf(" --atleast-version require a specific version of a module\n");
163 printf(" --exact-version require an exact version of a module\n");
164 printf(" --max-version require a maximum version of a module\n");
165 printf(" --exists check whether or not a module exists\n");
166 printf(" --uninstalled check whether or not an uninstalled module will be used\n");
167 printf(" --no-uninstalled never use uninstalled modules when satisfying dependencies\n");
168 printf(" --no-provides do not use 'provides' rules to resolve dependencies\n");
169 printf(" --maximum-traverse-depth maximum allowed depth for dependency graph\n");
170 printf(" --static be more aggressive when computing dependency graph\n");
171 printf(" (for static linking)\n");
172 printf(" --shared use a simplified dependency graph (usually default)\n");
173 printf(" --pure optimize a static dependency graph as if it were a normal\n");
174 printf(" dependency graph\n");
175 printf(" --env-only look only for package entries in PKG_CONFIG_PATH\n");
176 printf(" --ignore-conflicts ignore 'conflicts' rules in modules\n");
177 printf(" --validate validate specific .pc files for correctness\n");
178
179 printf("\nquerying specific pkg-config database fields:\n\n");
180
181 printf(" --define-variable=varname=value define variable 'varname' as 'value'\n");
182 printf(" --variable=varname print specified variable entry to stdout\n");
183 printf(" --cflags print required CFLAGS to stdout\n");
184 printf(" --cflags-only-I print required include-dir CFLAGS to stdout\n");
185 printf(" --cflags-only-other print required non-include-dir CFLAGS to stdout\n");
186 printf(" --libs print required linker flags to stdout\n");
187 printf(" --libs-only-L print required LDPATH linker flags to stdout\n");
188 printf(" --libs-only-l print required LIBNAME linker flags to stdout\n");
189 printf(" --libs-only-other print required other linker flags to stdout\n");
190 printf(" --print-requires print required dependency frameworks to stdout\n");
191 printf(" --print-requires-private print required dependency frameworks for static\n");
192 printf(" linking to stdout\n");
193 printf(" --print-provides print provided dependencies to stdout\n");
194 printf(" --print-variables print all known variables in module to stdout\n");
195 #ifndef PKGCONF_LITE
196 printf(" --digraph print entire dependency graph in graphviz 'dot' format\n");
197 printf(" --print-digraph-query-nodes also print query nodes in 'dot' format\n");
198 printf(" --solution print dependency graph solution in a simple format\n");
199 #endif
200 printf(" --keep-system-cflags keep -I%s entries in cflags output\n", SYSTEM_INCLUDEDIR);
201 printf(" --keep-system-libs keep -L%s entries in libs output\n", SYSTEM_LIBDIR);
202 printf(" --path show the exact filenames for any matching .pc files\n");
203 printf(" --modversion print the specified module's version to stdout\n");
204 printf(" --internal-cflags do not filter 'internal' cflags from output\n");
205 printf(" --license print the specified module's license to stdout if known\n");
206 printf(" --link-abi print the link ABIs (e.g. c, c++) the module must be linked against\n");
207 printf(" --source print the specified module's source code location to stdout if known\n");
208 printf(" --exists-cflags add -DHAVE_FOO fragments to cflags for each found module\n");
209
210 printf("\nfiltering output:\n\n");
211 #ifndef PKGCONF_LITE
212 printf(" --msvc-syntax print translatable fragments in MSVC syntax\n");
213 #endif
214 printf(" --fragment-filter=types filter output fragments to the specified types\n");
215 printf(" --env=prefix print output as shell-compatible environmental variables\n");
216 printf(" --fragment-tree visualize printed CFLAGS/LIBS fragments as a tree\n");
217 printf(" --newlines use newlines for whitespace between fragments\n");
218
219 printf("\nreport bugs to <%s>.\n", PACKAGE_BUGREPORT);
220 }
221
222 int
main(int argc,char * argv[])223 main(int argc, char *argv[])
224 {
225 #ifdef _WIN32
226 // When activeCodePage is set to UTF-8 in the application manifest (requires Windows 1903+),
227 // GetACP() returns CP_UTF8 but the console code pages are not automatically updated to match.
228 // Detect this condition via GetACP() == CP_UTF8 and align the console code pages accordingly.
229 // Restoring on exit is safe: if ACP is already CP_UTF8, resetting to the saved values is a no-op.
230 if (GetACP() == CP_UTF8)
231 {
232 original_console_cp = GetConsoleCP();
233 original_console_out_cp = GetConsoleOutputCP();
234 SetConsoleCP(CP_UTF8);
235 SetConsoleOutputCP(CP_UTF8);
236 atexit(restore_console_code_page);
237 }
238 #endif
239
240 int ret;
241 pkgconf_cli_state_t state = {
242 .want_flags = 0,
243 };
244 pkgconf_list_t dir_list = PKGCONF_LIST_INITIALIZER;
245 char *env_traverse_depth;
246 char *logfile_arg = NULL;
247 pkgconf_cross_personality_t *personality = NULL;
248
249 if (pkgconf_pledge("stdio rpath wpath cpath unveil", NULL) == -1)
250 {
251 pkgconf_output_file_fmt(stderr, "pkgconf: pledge failed: %s\n", strerror(errno));
252 return EXIT_FAILURE;
253 }
254
255 #ifdef _WIN32
256 /* When running regression tests in cygwin, and building native
257 * executable, tests fail unless native executable outputs unix
258 * line endings. Come to think of it, this will probably help
259 * real people who use cygwin build environments but native pkgconf, too.
260 */
261 _setmode(fileno(stdout), O_BINARY);
262 _setmode(fileno(stderr), O_BINARY);
263 #endif
264
265 struct pkg_option options[] = {
266 { "version", no_argument, &state.want_flags, PKG_VERSION|PKG_PRINT_ERRORS, },
267 { "about", no_argument, &state.want_flags, PKG_ABOUT|PKG_PRINT_ERRORS, },
268 { "atleast-version", required_argument, NULL, 2, },
269 { "atleast-pkgconfig-version", required_argument, NULL, 3, },
270 { "libs", no_argument, &state.want_flags, PKG_LIBS|PKG_PRINT_ERRORS, },
271 { "cflags", no_argument, &state.want_flags, PKG_CFLAGS|PKG_PRINT_ERRORS, },
272 { "modversion", no_argument, &state.want_flags, PKG_MODVERSION|PKG_PRINT_ERRORS, },
273 { "variable", required_argument, NULL, 7, },
274 { "exists", no_argument, &state.want_flags, PKG_EXISTS, },
275 { "print-errors", no_argument, &state.want_flags, PKG_PRINT_ERRORS, },
276 { "short-errors", no_argument, &state.want_flags, PKG_SHORT_ERRORS, },
277 { "maximum-traverse-depth", required_argument, NULL, 11, },
278 { "static", no_argument, &state.want_flags, PKG_STATIC, },
279 { "shared", no_argument, &state.want_flags, PKG_SHARED, },
280 { "pure", no_argument, &state.want_flags, PKG_PURE, },
281 { "print-requires", no_argument, &state.want_flags, PKG_REQUIRES, },
282 { "print-variables", no_argument, &state.want_flags, PKG_VARIABLES|PKG_PRINT_ERRORS, },
283 #ifndef PKGCONF_LITE
284 { "digraph", no_argument, &state.want_flags, PKG_DIGRAPH, },
285 { "solution", no_argument, &state.want_flags, PKG_SOLUTION, },
286 #endif
287 { "help", no_argument, &state.want_flags, PKG_HELP, },
288 { "env-only", no_argument, &state.want_flags, PKG_ENV_ONLY, },
289 { "print-requires-private", no_argument, &state.want_flags, PKG_REQUIRES_PRIVATE, },
290 { "cflags-only-I", no_argument, &state.want_flags, PKG_CFLAGS_ONLY_I|PKG_PRINT_ERRORS, },
291 { "cflags-only-other", no_argument, &state.want_flags, PKG_CFLAGS_ONLY_OTHER|PKG_PRINT_ERRORS, },
292 { "libs-only-L", no_argument, &state.want_flags, PKG_LIBS_ONLY_LDPATH|PKG_PRINT_ERRORS, },
293 { "libs-only-l", no_argument, &state.want_flags, PKG_LIBS_ONLY_LIBNAME|PKG_PRINT_ERRORS, },
294 { "libs-only-other", no_argument, &state.want_flags, PKG_LIBS_ONLY_OTHER|PKG_PRINT_ERRORS, },
295 { "uninstalled", no_argument, &state.want_flags, PKG_UNINSTALLED, },
296 { "no-uninstalled", no_argument, &state.want_flags, PKG_NO_UNINSTALLED, },
297 { "keep-system-cflags", no_argument, &state.want_flags, PKG_KEEP_SYSTEM_CFLAGS, },
298 { "keep-system-libs", no_argument, &state.want_flags, PKG_KEEP_SYSTEM_LIBS, },
299 { "define-variable", required_argument, NULL, 27, },
300 { "exact-version", required_argument, NULL, 28, },
301 { "max-version", required_argument, NULL, 29, },
302 { "ignore-conflicts", no_argument, &state.want_flags, PKG_IGNORE_CONFLICTS, },
303 { "errors-to-stdout", no_argument, &state.want_flags, PKG_ERRORS_ON_STDOUT, },
304 { "silence-errors", no_argument, &state.want_flags, PKG_SILENCE_ERRORS, },
305 { "list-all", no_argument, &state.want_flags, PKG_LIST|PKG_PRINT_ERRORS, },
306 { "list-package-names", no_argument, &state.want_flags, PKG_LIST_PACKAGE_NAMES|PKG_PRINT_ERRORS, },
307 #ifndef PKGCONF_LITE
308 { "simulate", no_argument, &state.want_flags, PKG_SIMULATE, },
309 #endif
310 { "no-cache", no_argument, &state.want_flags, PKG_NO_CACHE, },
311 { "print-provides", no_argument, &state.want_flags, PKG_PROVIDES, },
312 { "no-provides", no_argument, &state.want_flags, PKG_NO_PROVIDES, },
313 { "debug", no_argument, &state.want_flags, PKG_DEBUG|PKG_PRINT_ERRORS, },
314 { "validate", no_argument, &state.want_flags, PKG_VALIDATE|PKG_PRINT_ERRORS|PKG_ERRORS_ON_STDOUT },
315 { "log-file", required_argument, NULL, 40 },
316 { "path", no_argument, &state.want_flags, PKG_PATH },
317 { "with-path", required_argument, NULL, 42 },
318 { "prefix-variable", required_argument, NULL, 43 },
319 { "define-prefix", no_argument, &state.want_flags, PKG_DEFINE_PREFIX },
320 { "dont-define-prefix", no_argument, &state.want_flags, PKG_DONT_DEFINE_PREFIX },
321 { "dont-relocate-paths", no_argument, &state.want_flags, PKG_DONT_RELOCATE_PATHS },
322 { "env", required_argument, NULL, 48 },
323 #ifndef PKGCONF_LITE
324 { "msvc-syntax", no_argument, &state.want_flags, PKG_MSVC_SYNTAX },
325 #endif
326 { "fragment-filter", required_argument, NULL, 50 },
327 { "internal-cflags", no_argument, &state.want_flags, PKG_INTERNAL_CFLAGS },
328 #ifndef PKGCONF_LITE
329 { "dump-personality", no_argument, &state.want_flags, PKG_DUMP_PERSONALITY },
330 { "personality", required_argument, NULL, 53 },
331 #endif
332 { "license", no_argument, &state.want_flags, PKG_DUMP_LICENSE },
333 { "license-file", no_argument, &state.want_flags, PKG_DUMP_LICENSE_FILE },
334 { "link-abi", no_argument, &state.want_flags, PKG_LINK_ABI },
335 { "verbose", no_argument, NULL, 55 },
336 { "exists-cflags", no_argument, &state.want_flags, PKG_EXISTS_CFLAGS },
337 { "fragment-tree", no_argument, &state.want_flags, PKG_FRAGMENT_TREE },
338 { "source", no_argument, &state.want_flags, PKG_DUMP_SOURCE },
339 { "newlines", no_argument, &state.want_flags, PKG_NEWLINES },
340 #ifndef PKGCONF_LITE
341 { "print-digraph-query-nodes", no_argument, &state.want_flags, PKG_PRINT_DIGRAPH_QUERY_NODES },
342 #endif
343 { NULL, 0, NULL, 0 }
344 };
345
346 #ifndef PKGCONF_LITE
347 if (getenv("PKG_CONFIG_EARLY_TRACE"))
348 {
349 state.error_msgout = stderr;
350 pkgconf_client_set_trace_handler(&state.pkg_client, error_handler, NULL);
351 }
352 #endif
353
354 while ((ret = pkg_getopt_long_only(argc, argv, "", options, NULL)) != -1)
355 {
356 switch (ret)
357 {
358 case 2:
359 state.required_module_version = pkg_optarg;
360 break;
361 case 3:
362 state.required_pkgconfig_version = pkg_optarg;
363 break;
364 case 7:
365 state.want_variable = pkg_optarg;
366 break;
367 case 11:
368 state.maximum_traverse_depth = atoi(pkg_optarg);
369 break;
370 case 27:
371 pkgconf_tuple_define_global(&state.pkg_client, pkg_optarg);
372 break;
373 case 28:
374 state.required_exact_module_version = pkg_optarg;
375 break;
376 case 29:
377 state.required_max_module_version = pkg_optarg;
378 break;
379 case 40:
380 logfile_arg = pkg_optarg;
381 break;
382 case 42:
383 pkgconf_path_prepend(pkg_optarg, &dir_list, true);
384 break;
385 case 43:
386 pkgconf_client_set_prefix_varname(&state.pkg_client, pkg_optarg);
387 break;
388 case 48:
389 state.want_env_prefix = pkg_optarg;
390 break;
391 case 50:
392 state.want_fragment_filter = pkg_optarg;
393 break;
394 #ifndef PKGCONF_LITE
395 case 53:
396 personality = pkgconf_cross_personality_find(pkg_optarg);
397 break;
398 #endif
399 case 55:
400 state.verbosity++;
401 break;
402 case '?':
403 case ':':
404 ret = EXIT_FAILURE;
405 goto out;
406 default:
407 break;
408 }
409 }
410
411 if (personality == NULL) {
412 #ifndef PKGCONF_LITE
413 personality = deduce_personality(argv);
414 #else
415 personality = pkgconf_cross_personality_default();
416 #endif
417 }
418
419 /* now, bring up the client. settings are preserved since the client is prealloced */
420 pkgconf_client_init(&state.pkg_client, error_handler, &state, personality, &state, environ_lookup_handler);
421 pkgconf_client_set_unveil_handler(&state.pkg_client, unveil_handler);
422
423 #ifndef PKGCONF_LITE
424 if (getenv("PKG_CONFIG_MSVC_SYNTAX") != NULL)
425 state.want_flags |= PKG_MSVC_SYNTAX;
426 #endif
427
428 if ((env_traverse_depth = getenv("PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH")) != NULL)
429 state.maximum_traverse_depth = atoi(env_traverse_depth);
430
431 if ((state.want_flags & PKG_PRINT_ERRORS) != PKG_PRINT_ERRORS)
432 state.want_flags |= (PKG_SILENCE_ERRORS);
433
434 if ((state.want_flags & PKG_SILENCE_ERRORS) == PKG_SILENCE_ERRORS && !getenv("PKG_CONFIG_DEBUG_SPEW"))
435 state.want_flags |= (PKG_SILENCE_ERRORS);
436 else
437 state.want_flags &= ~(PKG_SILENCE_ERRORS);
438
439 if (getenv("PKG_CONFIG_DONT_RELOCATE_PATHS"))
440 state.want_flags |= (PKG_DONT_RELOCATE_PATHS);
441
442 if ((state.want_flags & PKG_VALIDATE) == PKG_VALIDATE || (state.want_flags & PKG_DEBUG) == PKG_DEBUG)
443 pkgconf_client_set_warn_handler(&state.pkg_client, error_handler, NULL);
444
445 #ifndef PKGCONF_LITE
446 if ((state.want_flags & PKG_DEBUG) == PKG_DEBUG)
447 pkgconf_client_set_trace_handler(&state.pkg_client, error_handler, NULL);
448 #endif
449
450 pkgconf_path_prepend_list(&state.pkg_client.dir_list, &dir_list);
451 pkgconf_path_free(&dir_list);
452
453 if ((state.want_flags & PKG_ABOUT) == PKG_ABOUT)
454 {
455 about();
456
457 ret = EXIT_SUCCESS;
458 goto out;
459 }
460
461 if ((state.want_flags & PKG_VERSION) == PKG_VERSION)
462 {
463 version();
464
465 ret = EXIT_SUCCESS;
466 goto out;
467 }
468
469 if ((state.want_flags & PKG_HELP) == PKG_HELP)
470 {
471 usage();
472
473 ret = EXIT_SUCCESS;
474 goto out;
475 }
476
477 if (logfile_arg == NULL)
478 logfile_arg = getenv("PKG_CONFIG_LOG");
479
480 if (logfile_arg != NULL)
481 {
482 if (pkgconf_unveil(logfile_arg, "rwc") == -1)
483 {
484 pkgconf_output_file_fmt(stderr, "pkgconf: unveil failed: %s\n", strerror(errno));
485 return EXIT_FAILURE;
486 }
487
488 state.logfile_out = fopen(logfile_arg, "a");
489 pkgconf_audit_set_log(&state.pkg_client, state.logfile_out);
490 }
491
492 if (getenv("PKG_CONFIG_ALLOW_SYSTEM_CFLAGS") != NULL)
493 state.want_flags |= PKG_KEEP_SYSTEM_CFLAGS;
494
495 if (getenv("PKG_CONFIG_ALLOW_SYSTEM_LIBS") != NULL)
496 state.want_flags |= PKG_KEEP_SYSTEM_LIBS;
497
498 return pkgconf_cli_run(&state, argc, argv, pkg_optind);
499
500 out:
501 pkgconf_cli_state_reset(&state);
502 return ret;
503 }
504