xref: /freebsd/contrib/pkgconf/libpkgconf/personality.c (revision a3cefe7f2b4df0f70ff92d4570ce18e517af43ec)
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