1 /*
2 * personality.c
3 * libpkgconf cross-compile personality database
4 *
5 * Copyright (c) 2018 pkgconf authors (see AUTHORS).
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * This software is provided 'as is' and without any warranty, express or
12 * implied. In no event shall the authors be liable for any damages arising
13 * from the use of this software.
14 */
15
16 #include <libpkgconf/config.h>
17 #include <libpkgconf/stdinc.h>
18 #include <libpkgconf/libpkgconf.h>
19
20 /*
21 * !doc
22 *
23 * libpkgconf `personality` module
24 * =========================
25 */
26
27 #ifdef _WIN32
28 # define strcasecmp _stricmp
29 #endif
30
31 /*
32 * Increment each time the default personality is inited, decrement each time
33 * it's deinited. Whenever it is 0, then the deinit frees the personality. In
34 * that case an additional call to init will create it anew.
35 */
36 static unsigned default_personality_init = 0;
37
38 static pkgconf_cross_personality_t default_personality = {
39 .name = "default",
40 };
41
42 static inline void
build_default_search_path(pkgconf_list_t * dirlist)43 build_default_search_path(pkgconf_list_t* dirlist)
44 {
45 #ifdef _WIN32
46 char namebuf[MAX_PATH];
47 char outbuf[MAX_PATH];
48 char *p;
49
50 int sizepath = GetModuleFileName(NULL, namebuf, sizeof namebuf);
51 char * winslash;
52 namebuf[sizepath] = '\0';
53 while ((winslash = strchr (namebuf, '\\')) != NULL)
54 {
55 *winslash = '/';
56 }
57 p = strrchr(namebuf, '/');
58 if (p == NULL)
59 pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, true);
60
61 *p = '\0';
62 pkgconf_strlcpy(outbuf, namebuf, sizeof outbuf);
63 pkgconf_strlcat(outbuf, "/", sizeof outbuf);
64 pkgconf_strlcat(outbuf, "../lib/pkgconfig", sizeof outbuf);
65 pkgconf_path_add(outbuf, dirlist, true);
66 pkgconf_strlcpy(outbuf, namebuf, sizeof outbuf);
67 pkgconf_strlcat(outbuf, "/", sizeof outbuf);
68 pkgconf_strlcat(outbuf, "../share/pkgconfig", sizeof outbuf);
69 pkgconf_path_add(outbuf, dirlist, true);
70 #elif __HAIKU__
71 char **paths;
72 size_t count;
73 if (find_paths(B_FIND_PATH_DEVELOP_LIB_DIRECTORY, "pkgconfig", &paths, &count) == B_OK) {
74 for (size_t i = 0; i < count; i++)
75 pkgconf_path_add(paths[i], dirlist, true);
76 free(paths);
77 paths = NULL;
78 }
79 if (find_paths(B_FIND_PATH_DATA_DIRECTORY, "pkgconfig", &paths, &count) == B_OK) {
80 for (size_t i = 0; i < count; i++)
81 pkgconf_path_add(paths[i], dirlist, true);
82 free(paths);
83 paths = NULL;
84 }
85 #else
86 pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, true);
87 #endif
88 }
89
90 /*
91 * !doc
92 *
93 * .. c:function:: const pkgconf_cross_personality_t *pkgconf_cross_personality_default(void)
94 *
95 * Returns the default cross-compile personality.
96 *
97 * Not thread safe.
98 *
99 * :rtype: pkgconf_cross_personality_t*
100 * :return: the default cross-compile personality
101 */
102 pkgconf_cross_personality_t *
pkgconf_cross_personality_default(void)103 pkgconf_cross_personality_default(void)
104 {
105 if (default_personality_init) {
106 ++default_personality_init;
107 return &default_personality;
108 }
109
110 build_default_search_path(&default_personality.dir_list);
111
112 pkgconf_path_split(SYSTEM_LIBDIR, &default_personality.filter_libdirs, false);
113 pkgconf_path_split(SYSTEM_INCLUDEDIR, &default_personality.filter_includedirs, false);
114
115 ++default_personality_init;
116 return &default_personality;
117 }
118
119 /*
120 * !doc
121 *
122 * .. c:function:: void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *)
123 *
124 * Destroys a cross personality object and/or decreases the reference count on the
125 * default cross personality object.
126 *
127 * Not thread safe.
128 *
129 * :rtype: void
130 */
131 void
pkgconf_cross_personality_deinit(pkgconf_cross_personality_t * personality)132 pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *personality)
133 {
134 /* allow NULL parameter for API backwards compatibility */
135 if (personality == NULL)
136 return;
137
138 /* XXX: this hack is rather ugly, but it works for now... */
139 if (personality == &default_personality && --default_personality_init > 0)
140 return;
141
142 pkgconf_path_free(&personality->dir_list);
143 pkgconf_path_free(&personality->filter_libdirs);
144 pkgconf_path_free(&personality->filter_includedirs);
145
146 if (personality->sysroot_dir != NULL)
147 free(personality->sysroot_dir);
148
149 if (personality == &default_personality)
150 return;
151
152 if (personality->name != NULL)
153 free(personality->name);
154
155 free(personality);
156 }
157
158 #ifndef PKGCONF_LITE
159 static bool
valid_triplet(const char * triplet)160 valid_triplet(const char *triplet)
161 {
162 const char *c = triplet;
163
164 for (; *c; c++)
165 if (!isalnum((unsigned char)*c) && *c != '-' && *c != '_')
166 return false;
167
168 return true;
169 }
170
171 typedef void (*personality_keyword_func_t)(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value);
172 typedef struct {
173 const char *keyword;
174 const personality_keyword_func_t func;
175 const ptrdiff_t offset;
176 } personality_keyword_pair_t;
177
178 static void
personality_bool_func(pkgconf_cross_personality_t * p,const char * keyword,const size_t lineno,const ptrdiff_t offset,char * value)179 personality_bool_func(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value)
180 {
181 (void) keyword;
182 (void) lineno;
183
184 bool *dest = (bool *)((char *) p + offset);
185 *dest = strcasecmp(value, "true") || strcasecmp(value, "yes") || *value == '1';
186 }
187
188 static void
personality_copy_func(pkgconf_cross_personality_t * p,const char * keyword,const size_t lineno,const ptrdiff_t offset,char * value)189 personality_copy_func(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value)
190 {
191 (void) keyword;
192 (void) lineno;
193
194 char **dest = (char **)((char *) p + offset);
195 *dest = strdup(value);
196 }
197
198 static void
personality_fragment_func(pkgconf_cross_personality_t * p,const char * keyword,const size_t lineno,const ptrdiff_t offset,char * value)199 personality_fragment_func(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value)
200 {
201 (void) keyword;
202 (void) lineno;
203
204 pkgconf_list_t *dest = (pkgconf_list_t *)((char *) p + offset);
205 pkgconf_path_split(value, dest, false);
206 }
207
208 /* keep in alphabetical order! */
209 static const personality_keyword_pair_t personality_keyword_pairs[] = {
210 {"DefaultSearchPaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, dir_list)},
211 {"SysrootDir", personality_copy_func, offsetof(pkgconf_cross_personality_t, sysroot_dir)},
212 {"SystemIncludePaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, filter_includedirs)},
213 {"SystemLibraryPaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, filter_libdirs)},
214 {"Triplet", personality_copy_func, offsetof(pkgconf_cross_personality_t, name)},
215 {"WantDefaultPure", personality_bool_func, offsetof(pkgconf_cross_personality_t, want_default_pure)},
216 {"WantDefaultStatic", personality_bool_func, offsetof(pkgconf_cross_personality_t, want_default_static)},
217 };
218
219 static int
personality_keyword_pair_cmp(const void * key,const void * ptr)220 personality_keyword_pair_cmp(const void *key, const void *ptr)
221 {
222 const personality_keyword_pair_t *pair = ptr;
223 return strcasecmp(key, pair->keyword);
224 }
225
226 static void
personality_keyword_set(pkgconf_cross_personality_t * p,const size_t lineno,const char * keyword,char * value)227 personality_keyword_set(pkgconf_cross_personality_t *p, const size_t lineno, const char *keyword, char *value)
228 {
229 const personality_keyword_pair_t *pair = bsearch(keyword,
230 personality_keyword_pairs, PKGCONF_ARRAY_SIZE(personality_keyword_pairs),
231 sizeof(personality_keyword_pair_t), personality_keyword_pair_cmp);
232
233 if (pair == NULL || pair->func == NULL)
234 return;
235
236 pair->func(p, keyword, lineno, pair->offset, value);
237 }
238
239 static const pkgconf_parser_operand_func_t personality_parser_ops[256] = {
240 [':'] = (pkgconf_parser_operand_func_t) personality_keyword_set
241 };
242
243 static void personality_warn_func(void *p, const char *fmt, ...) PRINTFLIKE(2, 3);
244
245 static void
personality_warn_func(void * p,const char * fmt,...)246 personality_warn_func(void *p, const char *fmt, ...)
247 {
248 va_list va;
249
250 (void) p;
251
252 va_start(va, fmt);
253 vfprintf(stderr, fmt, va);
254 va_end(va);
255 }
256
257 static pkgconf_cross_personality_t *
load_personality_with_path(const char * path,const char * triplet,bool datadir)258 load_personality_with_path(const char *path, const char *triplet, bool datadir)
259 {
260 char pathbuf[PKGCONF_ITEM_SIZE];
261 FILE *f;
262 pkgconf_cross_personality_t *p;
263
264 /* if triplet is null, assume that path is a direct path to the personality file */
265 if (triplet == NULL)
266 pkgconf_strlcpy(pathbuf, path, sizeof pathbuf);
267 else if (datadir)
268 snprintf(pathbuf, sizeof pathbuf, "%s/pkgconfig/personality.d/%s.personality", path, triplet);
269 else
270 snprintf(pathbuf, sizeof pathbuf, "%s/%s.personality", path, triplet);
271
272 p = calloc(1, sizeof(pkgconf_cross_personality_t));
273 if (p == NULL)
274 return NULL;
275
276 if (triplet != NULL)
277 p->name = strdup(triplet);
278
279 f = fopen(pathbuf, "r");
280 if (f == NULL) {
281 pkgconf_cross_personality_deinit(p);
282 return NULL;
283 }
284
285 pkgconf_parser_parse(f, p, personality_parser_ops, personality_warn_func, pathbuf);
286
287 return p;
288 }
289
290 /*
291 * !doc
292 *
293 * .. c:function:: pkgconf_cross_personality_t *pkgconf_cross_personality_find(const char *triplet)
294 *
295 * Attempts to find a cross-compile personality given a triplet.
296 *
297 * :rtype: pkgconf_cross_personality_t*
298 * :return: the default cross-compile personality
299 */
300 pkgconf_cross_personality_t *
pkgconf_cross_personality_find(const char * triplet)301 pkgconf_cross_personality_find(const char *triplet)
302 {
303 pkgconf_list_t plist = PKGCONF_LIST_INITIALIZER;
304 pkgconf_node_t *n;
305 pkgconf_cross_personality_t *out = NULL;
306 #if ! defined(_WIN32) && ! defined(__HAIKU__)
307 char pathbuf[PKGCONF_ITEM_SIZE];
308 const char *envvar;
309 #endif
310
311 out = load_personality_with_path(triplet, NULL, false);
312 if (out != NULL)
313 return out;
314
315 if (!valid_triplet(triplet))
316 return NULL;
317
318 #if ! defined(_WIN32) && ! defined(__HAIKU__)
319 envvar = getenv("XDG_DATA_HOME");
320 if (envvar != NULL)
321 pkgconf_path_add(envvar, &plist, true);
322 else {
323 envvar = getenv("HOME");
324 if (envvar != NULL) {
325 pkgconf_strlcpy(pathbuf, envvar, sizeof pathbuf);
326 pkgconf_strlcat(pathbuf, "/.local/share", sizeof pathbuf);
327 pkgconf_path_add(pathbuf, &plist, true);
328 }
329 }
330
331 pkgconf_path_build_from_environ("XDG_DATA_DIRS", "/usr/local/share" PKG_CONFIG_PATH_SEP_S "/usr/share", &plist, true);
332
333 PKGCONF_FOREACH_LIST_ENTRY(plist.head, n)
334 {
335 pkgconf_path_t *pn = n->data;
336
337 out = load_personality_with_path(pn->path, triplet, true);
338 if (out != NULL)
339 goto finish;
340 }
341 pkgconf_path_free(&plist);
342 #endif
343
344 pkgconf_path_split(PERSONALITY_PATH, &plist, true);
345
346 PKGCONF_FOREACH_LIST_ENTRY(plist.head, n)
347 {
348 pkgconf_path_t *pn = n->data;
349
350 out = load_personality_with_path(pn->path, triplet, false);
351 if (out != NULL)
352 goto finish;
353 }
354
355 finish:
356 pkgconf_path_free(&plist);
357 return out != NULL ? out : pkgconf_cross_personality_default();
358 }
359 #endif
360