1 /* 2 * personality.c 3 * libpkgconf cross-compile personality database 4 * 5 * SPDX-License-Identifier: pkgconf 6 * 7 * Copyright (c) 2018 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 22 #ifdef __FreeBSD__ 23 # include <sys/param.h> 24 # include <sys/sysctl.h> 25 # include <err.h> 26 #endif 27 28 /* 29 * !doc 30 * 31 * libpkgconf `personality` module 32 * ========================= 33 */ 34 35 /* 36 * Increment each time the default personality is inited, decrement each time 37 * it's deinited. Whenever it is 0, then the deinit frees the personality. In 38 * that case an additional call to init will create it anew. 39 */ 40 static unsigned default_personality_init = 0; 41 42 static pkgconf_cross_personality_t default_personality = { 43 .name = "default", 44 }; 45 46 static inline void 47 build_default_search_path(pkgconf_list_t* dirlist) 48 { 49 #ifdef _WIN32 50 char namebuf[MAX_PATH]; 51 pkgconf_buffer_t pathbuf = PKGCONF_BUFFER_INITIALIZER; 52 char *p; 53 54 /* Reserve one byte for the NUL: GetModuleFileName returns the size passed 55 * to it (nSize) when the path is truncated, so passing sizeof namebuf could 56 * yield sizepath == sizeof namebuf and overflow namebuf[] by one byte below. 57 * 58 * See the GetModuleFileNameA return-value documentation: 59 * https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamea 60 */ 61 int sizepath = GetModuleFileName(NULL, namebuf, sizeof namebuf - 1); 62 char * winslash; 63 namebuf[sizepath] = '\0'; 64 65 while ((winslash = strchr(namebuf, '\\')) != NULL) 66 *winslash = '/'; 67 68 p = strrchr(namebuf, '/'); 69 if (p == NULL) 70 { 71 pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, true); 72 return; 73 } 74 *p = '\0'; 75 76 pkgconf_buffer_append_fmt(&pathbuf, "%s/../lib/pkgconfig", namebuf); 77 pkgconf_path_add(pkgconf_buffer_str(&pathbuf), dirlist, true); 78 pkgconf_buffer_reset(&pathbuf); 79 80 pkgconf_buffer_append_fmt(&pathbuf, "%s/../share/pkgconfig", namebuf); 81 pkgconf_path_add(pkgconf_buffer_str(&pathbuf), dirlist, true); 82 pkgconf_buffer_finalize(&pathbuf); 83 #elif __HAIKU__ 84 char **paths; 85 size_t count; 86 if (find_paths(B_FIND_PATH_DEVELOP_LIB_DIRECTORY, "pkgconfig", &paths, &count) == B_OK) { 87 for (size_t i = 0; i < count; i++) 88 pkgconf_path_add(paths[i], dirlist, true); 89 free(paths); 90 paths = NULL; 91 } 92 if (find_paths(B_FIND_PATH_DATA_DIRECTORY, "pkgconfig", &paths, &count) == B_OK) { 93 for (size_t i = 0; i < count; i++) 94 pkgconf_path_add(paths[i], dirlist, true); 95 free(paths); 96 paths = NULL; 97 } 98 #elif defined(__FreeBSD__) 99 pkgconf_buffer_t path1 = PKGCONF_BUFFER_INITIALIZER; 100 pkgconf_buffer_t path2 = PKGCONF_BUFFER_INITIALIZER; 101 char const *localbase; 102 char buf[MAXPATHLEN]; 103 size_t len; 104 105 localbase = getenv("LOCALBASE"); 106 107 if (localbase == NULL) { 108 len = sizeof(buf); 109 if (sysctlbyname("user.localbase", &buf, &len, NULL, 0) < 0) 110 err(1, "sysctl(user.localbase)"); 111 112 localbase = buf; 113 } 114 115 if (!pkgconf_buffer_append(&path1, localbase) || 116 !pkgconf_buffer_append(&path1, "/libdata/pkgconfig")) 117 err(1, NULL); 118 pkgconf_path_add(pkgconf_buffer_str(&path1), dirlist, false); 119 120 pkgconf_path_add("/usr/libdata/pkgconfig", dirlist, false); 121 122 if (!pkgconf_buffer_append(&path2, localbase) || 123 !pkgconf_buffer_append(&path2, "/share/pkgconfig")) 124 err(1, NULL); 125 pkgconf_path_add(pkgconf_buffer_str(&path2), dirlist, false); 126 #else 127 pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, false); 128 #endif 129 } 130 131 /* 132 * !doc 133 * 134 * .. c:function:: const pkgconf_cross_personality_t *pkgconf_cross_personality_default(void) 135 * 136 * Returns the default cross-compile personality. 137 * 138 * Not thread safe. 139 * 140 * :rtype: pkgconf_cross_personality_t* 141 * :return: the default cross-compile personality 142 */ 143 pkgconf_cross_personality_t * 144 pkgconf_cross_personality_default(void) 145 { 146 if (default_personality_init) { 147 ++default_personality_init; 148 return &default_personality; 149 } 150 151 build_default_search_path(&default_personality.dir_list); 152 153 pkgconf_path_split(SYSTEM_LIBDIR, &default_personality.filter_libdirs, false); 154 pkgconf_path_split(SYSTEM_INCLUDEDIR, &default_personality.filter_includedirs, false); 155 156 ++default_personality_init; 157 return &default_personality; 158 } 159 160 /* 161 * !doc 162 * 163 * .. c:function:: void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *) 164 * 165 * Destroys a cross personality object and/or decreases the reference count on the 166 * default cross personality object. 167 * 168 * Not thread safe. 169 * 170 * :rtype: void 171 */ 172 void 173 pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *personality) 174 { 175 /* allow NULL parameter for API backwards compatibility */ 176 if (personality == NULL) 177 return; 178 179 /* XXX: this hack is rather ugly, but it works for now... */ 180 if (personality == &default_personality && --default_personality_init > 0) 181 return; 182 183 pkgconf_path_free(&personality->dir_list); 184 pkgconf_path_free(&personality->filter_libdirs); 185 pkgconf_path_free(&personality->filter_includedirs); 186 187 if (personality->sysroot_dir != NULL) 188 free(personality->sysroot_dir); 189 190 if (personality == &default_personality) 191 return; 192 193 if (personality->name != NULL) 194 free(personality->name); 195 196 free(personality); 197 } 198 199 #ifndef PKGCONF_LITE 200 static bool 201 valid_triplet(const char *triplet) 202 { 203 const char *c = triplet; 204 205 for (; *c; c++) 206 if (!isalnum((unsigned char)*c) && *c != '-' && *c != '_') 207 return false; 208 209 return true; 210 } 211 212 typedef void (*personality_keyword_func_t)(pkgconf_cross_personality_t *p, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value); 213 typedef struct { 214 const char *keyword; 215 const personality_keyword_func_t func; 216 const ptrdiff_t offset; 217 } personality_keyword_pair_t; 218 219 static void 220 personality_bool_func(pkgconf_cross_personality_t *p, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value) 221 { 222 (void) keyword; 223 (void) warnprefix; 224 225 bool *dest = (bool *)((char *) p + offset); 226 *dest = strcasecmp(value, "true") == 0 || strcasecmp(value, "yes") == 0 || strcasecmp(value, "1") == 0; 227 } 228 229 static void 230 personality_copy_func(pkgconf_cross_personality_t *p, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value) 231 { 232 (void) keyword; 233 (void) warnprefix; 234 235 char **dest = (char **)((char *) p + offset); 236 237 if (*dest != NULL) 238 free(*dest); 239 240 *dest = strdup(value); 241 } 242 243 static void 244 personality_fragment_func(pkgconf_cross_personality_t *p, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value) 245 { 246 (void) keyword; 247 (void) warnprefix; 248 249 pkgconf_list_t *dest = (pkgconf_list_t *)((char *) p + offset); 250 pkgconf_path_split(value, dest, false); 251 } 252 253 /* keep in alphabetical order! */ 254 static const personality_keyword_pair_t personality_keyword_pairs[] = { 255 {"DefaultSearchPaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, dir_list)}, 256 {"SysrootDir", personality_copy_func, offsetof(pkgconf_cross_personality_t, sysroot_dir)}, 257 {"SystemIncludePaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, filter_includedirs)}, 258 {"SystemLibraryPaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, filter_libdirs)}, 259 {"Triplet", personality_copy_func, offsetof(pkgconf_cross_personality_t, name)}, 260 {"WantDefaultPure", personality_bool_func, offsetof(pkgconf_cross_personality_t, want_default_pure)}, 261 {"WantDefaultStatic", personality_bool_func, offsetof(pkgconf_cross_personality_t, want_default_static)}, 262 }; 263 264 static int 265 personality_keyword_pair_cmp(const void *key, const void *ptr) 266 { 267 const personality_keyword_pair_t *pair = ptr; 268 return strcasecmp(key, pair->keyword); 269 } 270 271 static void 272 personality_keyword_set(void *data, const char *warnprefix, const char *keyword, const char *value) 273 { 274 pkgconf_cross_personality_t *p = data; 275 const personality_keyword_pair_t *pair = bsearch(keyword, 276 personality_keyword_pairs, PKGCONF_ARRAY_SIZE(personality_keyword_pairs), 277 sizeof(personality_keyword_pair_t), personality_keyword_pair_cmp); 278 279 if (pair == NULL || pair->func == NULL) 280 return; 281 282 pair->func(p, keyword, warnprefix, pair->offset, value); 283 } 284 285 static const pkgconf_parser_operand_func_t personality_parser_ops[256] = { 286 [':'] = (pkgconf_parser_operand_func_t) personality_keyword_set 287 }; 288 289 static void personality_warn_func(void *p, const char *fmt, ...) PRINTFLIKE(2, 3); 290 291 static void 292 personality_warn_func(void *p, const char *fmt, ...) 293 { 294 va_list va; 295 296 (void) p; 297 298 va_start(va, fmt); 299 pkgconf_output_file_vfmt(stderr, fmt, va); 300 va_end(va); 301 } 302 303 static pkgconf_cross_personality_t * 304 load_personality_with_path(const char *path, const char *triplet, bool datadir) 305 { 306 pkgconf_buffer_t pathbuf = PKGCONF_BUFFER_INITIALIZER; 307 FILE *f; 308 pkgconf_cross_personality_t *p; 309 310 /* if triplet is null, assume that path is a direct path to the personality file */ 311 if (triplet == NULL) 312 pkgconf_buffer_append(&pathbuf, path); 313 else if (datadir) 314 pkgconf_buffer_append_fmt(&pathbuf, "%s/pkgconfig/personality.d/%s.personality", path, triplet); 315 else 316 pkgconf_buffer_append_fmt(&pathbuf, "%s/%s.personality", path, triplet); 317 318 p = calloc(1, sizeof(pkgconf_cross_personality_t)); 319 if (p == NULL) 320 { 321 pkgconf_buffer_finalize(&pathbuf); 322 return NULL; 323 } 324 325 if (triplet != NULL) 326 p->name = strdup(triplet); 327 328 f = fopen(pkgconf_buffer_str(&pathbuf), "rb"); 329 if (f == NULL) 330 { 331 pkgconf_buffer_finalize(&pathbuf); 332 pkgconf_cross_personality_deinit(p); 333 return NULL; 334 } 335 336 pkgconf_parser_parse(f, p, personality_parser_ops, personality_warn_func, pkgconf_buffer_str(&pathbuf)); 337 pkgconf_buffer_finalize(&pathbuf); 338 339 return p; 340 } 341 342 /* 343 * !doc 344 * 345 * .. c:function:: pkgconf_cross_personality_t *pkgconf_cross_personality_find(const char *triplet) 346 * 347 * Attempts to find a cross-compile personality given a triplet. 348 * 349 * :rtype: pkgconf_cross_personality_t* 350 * :return: the default cross-compile personality 351 */ 352 pkgconf_cross_personality_t * 353 pkgconf_cross_personality_find(const char *triplet) 354 { 355 pkgconf_list_t plist = PKGCONF_LIST_INITIALIZER; 356 pkgconf_node_t *n; 357 pkgconf_cross_personality_t *out = NULL; 358 #if ! defined(_WIN32) && ! defined(__HAIKU__) 359 const char *envvar; 360 #endif 361 #if defined(__FreeBSD__) 362 pkgconf_buffer_t path = PKGCONF_BUFFER_INITIALIZER; 363 char const *localbase; 364 char buf[MAXPATHLEN]; 365 size_t len; 366 #endif 367 368 out = load_personality_with_path(triplet, NULL, false); 369 if (out != NULL) 370 return out; 371 372 if (!valid_triplet(triplet)) 373 return NULL; 374 375 #if ! defined(_WIN32) && ! defined(__HAIKU__) 376 envvar = getenv("XDG_DATA_HOME"); 377 if (envvar != NULL) 378 pkgconf_path_add(envvar, &plist, true); 379 else 380 { 381 envvar = getenv("HOME"); 382 if (envvar != NULL) 383 { 384 pkgconf_buffer_t pathbuf = PKGCONF_BUFFER_INITIALIZER; 385 386 pkgconf_buffer_append_fmt(&pathbuf, "%s/.local/share", envvar); 387 pkgconf_path_add(pkgconf_buffer_str(&pathbuf), &plist, true); 388 pkgconf_buffer_finalize(&pathbuf); 389 } 390 } 391 392 pkgconf_path_build_from_environ(NULL, "XDG_DATA_DIRS", "/usr/local/share" PKG_CONFIG_PATH_SEP_S "/usr/share", &plist, true); 393 394 PKGCONF_FOREACH_LIST_ENTRY(plist.head, n) 395 { 396 pkgconf_path_t *pn = n->data; 397 398 out = load_personality_with_path(pn->path, triplet, true); 399 if (out != NULL) 400 goto finish; 401 } 402 pkgconf_path_free(&plist); 403 #endif 404 405 #ifdef __FreeBSD__ 406 localbase = getenv("LOCALBASE"); 407 408 if (localbase == NULL) { 409 len = sizeof(buf); 410 if (sysctlbyname("user.localbase", &buf, &len, NULL, 0) < 0) 411 err(1, "sysctl(user.localbase)"); 412 413 localbase = buf; 414 } 415 416 pkgconf_path_add("/usr/share/pkgconfig/personality.d", &plist, true); 417 pkgconf_path_add("/etc/pkgconfig/personality.d", &plist, true); 418 419 if (!pkgconf_buffer_append(&path, localbase) || 420 !pkgconf_buffer_append(&path, 421 "/etc/pkgconfig/personality.d")) 422 err(1, NULL); 423 pkgconf_path_add(pkgconf_buffer_str(&path), &plist, true); 424 #else 425 pkgconf_path_split(PERSONALITY_PATH, &plist, true); 426 #endif 427 428 PKGCONF_FOREACH_LIST_ENTRY(plist.head, n) 429 { 430 pkgconf_path_t *pn = n->data; 431 432 out = load_personality_with_path(pn->path, triplet, false); 433 if (out != NULL) 434 goto finish; 435 } 436 437 finish: 438 pkgconf_path_free(&plist); 439 return out != NULL ? out : pkgconf_cross_personality_default(); 440 } 441 #endif 442