1 /* Copyright (C) 2000-2007, Luca Padovani <padovani@sti.uniurb.it>.
2  *
3  * This file is part of GtkMathView, a flexible, high-quality rendering
4  * engine for MathML documents.
5  *
6  * GtkMathView is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GtkMathView is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /* WARNING, BEFORE YOU MODIFY PREFIX.C:
21  *
22  * If you make changes to any of the functions in prefix.c, you MUST
23  * change the BR_NAMESPACE macro (in prefix.h).
24  * This way you can avoid symbol table conflicts with other libraries
25  * that also happen to use BinReloc.
26  *
27  * Example:
28  * #define BR_NAMESPACE(funcName) foobar_ ## funcName
29  * --> expands br_locate to foobar_br_locate
30  */
31 
32 #ifndef _PREFIX_C_
33 #define _PREFIX_C_
34 
35 #ifdef HAVE_CONFIG_H
36 	#include "config.h"
37 #endif
38 
39 #ifndef BR_PTHREADS
40 	/* Change 1 to 0 if you don't want pthread support */
41 	#define BR_PTHREADS 1
42 #endif /* BR_PTHREADS */
43 
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <limits.h>
47 #include <string.h>
48 #include "prefix.h"
49 
50 #ifdef __cplusplus
51 extern "C" {
52 #endif /* __cplusplus */
53 
54 
55 #undef NULL
56 #define NULL ((void *) 0)
57 
58 #ifdef __GNUC__
59 	#define br_return_val_if_fail(expr,val) if (!(expr)) {fprintf (stderr, "** BinReloc (%s): assertion %s failed\n", __PRETTY_FUNCTION__, #expr); return val;}
60 #else
61 	#define br_return_val_if_fail(expr,val) if (!(expr)) return val
62 #endif /* __GNUC__ */
63 
64 
65 static br_locate_fallback_func fallback_func = (br_locate_fallback_func) NULL;
66 static void *fallback_data = NULL;
67 
68 
69 #ifdef ENABLE_BINRELOC
70 #include <sys/types.h>
71 #include <sys/stat.h>
72 #include <sys/param.h>
73 #include <unistd.h>
74 
75 
76 /**
77  * br_locate:
78  * symbol: A symbol that belongs to the app/library you want to locate.
79  * Returns: A newly allocated string containing the full path of the
80  *	    app/library that func belongs to, or NULL on error. This
81  *	    string should be freed when not when no longer needed.
82  *
83  * Finds out to which application or library symbol belongs, then locate
84  * the full path of that application or library.
85  * Note that symbol cannot be a pointer to a function. That will not work.
86  *
87  * Example:
88  * --> main.c
89  * #include "prefix.h"
90  * #include "libfoo.h"
91  *
92  * int main (int argc, char *argv[]) {
93  *	printf ("Full path of this app: %s\n", br_locate (&argc));
94  *	libfoo_start ();
95  *	return 0;
96  * }
97  *
98  * --> libfoo.c starts here
99  * #include "prefix.h"
100  *
101  * void libfoo_start () {
102  *	--> "" is a symbol that belongs to libfoo (because it's called
103  *	--> from libfoo_start()); that's why this works.
104  *	printf ("libfoo is located in: %s\n", br_locate (""));
105  * }
106  */
107 char *
br_locate(void * symbol)108 br_locate (void *symbol)
109 {
110 	char line[5000];
111 	FILE *f;
112 	char *path;
113 
114 	br_return_val_if_fail (symbol != NULL, NULL);
115 
116 	f = fopen ("/proc/self/maps", "r");
117 	if (!f) {
118 		if (fallback_func)
119 			return fallback_func(symbol, fallback_data);
120 		else
121 			return NULL;
122 	}
123 
124 	while (!feof (f))
125 	{
126 		unsigned long start, end;
127 
128 		if (!fgets (line, sizeof (line), f))
129 			continue;
130 		if (!strstr (line, " r-xp ") || !strchr (line, '/'))
131 			continue;
132 
133 		sscanf (line, "%lx-%lx ", &start, &end);
134 		if (symbol >= (void *) start && symbol < (void *) end)
135 		{
136 			char *tmp;
137 			size_t len;
138 
139 			/* Extract the filename; it is always an absolute path */
140 			path = strchr (line, '/');
141 
142 			/* Get rid of the newline */
143 			tmp = strrchr (path, '\n');
144 			if (tmp) *tmp = 0;
145 
146 			/* Get rid of "(deleted)" */
147 			len = strlen (path);
148 			if (len > 10 && strcmp (path + len - 10, " (deleted)") == 0)
149 			{
150 				tmp = path + len - 10;
151 				*tmp = 0;
152 			}
153 
154 			fclose(f);
155 			return strdup (path);
156 		}
157 	}
158 
159 	fclose (f);
160 	return NULL;
161 }
162 
163 
164 /**
165  * br_locate_prefix:
166  * symbol: A symbol that belongs to the app/library you want to locate.
167  * Returns: A prefix. This string should be freed when no longer needed.
168  *
169  * Locates the full path of the app/library that symbol belongs to, and return
170  * the prefix of that path, or NULL on error.
171  * Note that symbol cannot be a pointer to a function. That will not work.
172  *
173  * Example:
174  * --> This application is located in /usr/bin/foo
175  * br_locate_prefix (&argc);   --> returns: "/usr"
176  */
177 char *
br_locate_prefix(void * symbol)178 br_locate_prefix (void *symbol)
179 {
180 	char *path, *prefix;
181 
182 	br_return_val_if_fail (symbol != NULL, NULL);
183 
184 	path = br_locate (symbol);
185 	if (!path) return NULL;
186 
187 	prefix = br_extract_prefix (path);
188 	free (path);
189 	return prefix;
190 }
191 
192 
193 /**
194  * br_prepend_prefix:
195  * symbol: A symbol that belongs to the app/library you want to locate.
196  * path: The path that you want to prepend the prefix to.
197  * Returns: The new path, or NULL on error. This string should be freed when no
198  *	    longer needed.
199  *
200  * Gets the prefix of the app/library that symbol belongs to. Prepend that prefix to path.
201  * Note that symbol cannot be a pointer to a function. That will not work.
202  *
203  * Example:
204  * --> The application is /usr/bin/foo
205  * br_prepend_prefix (&argc, "/share/foo/data.png");   --> Returns "/usr/share/foo/data.png"
206  */
207 char *
br_prepend_prefix(void * symbol,char * path)208 br_prepend_prefix (void *symbol, char *path)
209 {
210 	char *tmp, *newpath;
211 
212 	br_return_val_if_fail (symbol != NULL, NULL);
213 	br_return_val_if_fail (path != NULL, NULL);
214 
215 	tmp = br_locate_prefix (symbol);
216 	if (!tmp) return NULL;
217 
218 	if (strcmp (tmp, "/") == 0)
219 		newpath = strdup (path);
220 	else
221 		newpath = br_strcat (tmp, path);
222 
223 	/* Get rid of compiler warning ("br_prepend_prefix never used") */
224 	if (0) br_prepend_prefix (NULL, NULL);
225 
226 	free (tmp);
227 	return newpath;
228 }
229 
230 #endif /* ENABLE_BINRELOC */
231 
232 
233 /* Pthread stuff for thread safetiness */
234 #if BR_PTHREADS && defined(ENABLE_BINRELOC)
235 
236 #include <pthread.h>
237 
238 static pthread_key_t br_thread_key;
239 static pthread_once_t br_thread_key_once = PTHREAD_ONCE_INIT;
240 
241 
242 static void
br_thread_local_store_fini()243 br_thread_local_store_fini ()
244 {
245 	char *specific;
246 
247 	specific = (char *) pthread_getspecific (br_thread_key);
248 	if (specific)
249 	{
250 		free (specific);
251 		pthread_setspecific (br_thread_key, NULL);
252 	}
253 	pthread_key_delete (br_thread_key);
254 	br_thread_key = 0;
255 }
256 
257 
258 static void
br_str_free(void * str)259 br_str_free (void *str)
260 {
261 	if (str)
262 		free (str);
263 }
264 
265 
266 static void
br_thread_local_store_init()267 br_thread_local_store_init ()
268 {
269 	if (pthread_key_create (&br_thread_key, br_str_free) == 0)
270 		atexit (br_thread_local_store_fini);
271 }
272 
273 #else /* BR_PTHREADS */
274 #ifdef ENABLE_BINRELOC
275 
276 static char *br_last_value = (char *) NULL;
277 
278 static void
br_free_last_value()279 br_free_last_value ()
280 {
281 	if (br_last_value)
282 		free (br_last_value);
283 }
284 
285 #endif /* ENABLE_BINRELOC */
286 #endif /* BR_PTHREADS */
287 
288 
289 #ifdef ENABLE_BINRELOC
290 
291 /**
292  * br_thread_local_store:
293  * str: A dynamically allocated string.
294  * Returns: str. This return value must not be freed.
295  *
296  * Store str in a thread-local variable and return str. The next
297  * you run this function, that variable is freed too.
298  * This function is created so you don't have to worry about freeing
299  * strings. Just be careful about doing this sort of thing:
300  *
301  * some_function( BR_DATADIR("/one.png"), BR_DATADIR("/two.png") )
302  *
303  * Examples:
304  * char *foo;
305  * foo = br_thread_local_store (strdup ("hello")); --> foo == "hello"
306  * foo = br_thread_local_store (strdup ("world")); --> foo == "world"; "hello" is now freed.
307  */
308 const char *
br_thread_local_store(char * str)309 br_thread_local_store (char *str)
310 {
311 	#if BR_PTHREADS
312 		char *specific;
313 
314 		pthread_once (&br_thread_key_once, br_thread_local_store_init);
315 
316 		specific = (char *) pthread_getspecific (br_thread_key);
317 		br_str_free (specific);
318 		pthread_setspecific (br_thread_key, str);
319 
320 	#else /* BR_PTHREADS */
321 		static int initialized = 0;
322 
323 		if (!initialized)
324 		{
325 			atexit (br_free_last_value);
326 			initialized = 1;
327 		}
328 
329 		if (br_last_value)
330 			free (br_last_value);
331 		br_last_value = str;
332 	#endif /* BR_PTHREADS */
333 
334 	return (const char *) str;
335 }
336 
337 #endif /* ENABLE_BINRELOC */
338 
339 
340 /**
341  * br_strcat:
342  * str1: A string.
343  * str2: Another string.
344  * Returns: A newly-allocated string. This string should be freed when no longer needed.
345  *
346  * Concatenate str1 and str2 to a newly allocated string.
347  */
348 char *
br_strcat(const char * str1,const char * str2)349 br_strcat (const char *str1, const char *str2)
350 {
351 	char *result;
352 	size_t len1, len2;
353 
354 	if (!str1) str1 = "";
355 	if (!str2) str2 = "";
356 
357 	len1 = strlen (str1);
358 	len2 = strlen (str2);
359 
360 	result = (char *) malloc (len1 + len2 + 1);
361 	memcpy (result, str1, len1);
362 	memcpy (result + len1, str2, len2);
363 	result[len1 + len2] = '\0';
364 
365 	return result;
366 }
367 
368 
369 /* Emulates glibc's strndup() */
370 static char *
br_strndup(char * str,size_t size)371 br_strndup (char *str, size_t size)
372 {
373 	char *result = (char *) NULL;
374 	size_t len;
375 
376 	br_return_val_if_fail (str != (char *) NULL, (char *) NULL);
377 
378 	len = strlen (str);
379 	if (!len) return strdup ("");
380 	if (size > len) size = len;
381 
382 	result = (char *) calloc (sizeof (char), len + 1);
383 	memcpy (result, str, size);
384 	return result;
385 }
386 
387 
388 /**
389  * br_extract_dir:
390  * path: A path.
391  * Returns: A directory name. This string should be freed when no longer needed.
392  *
393  * Extracts the directory component of path. Similar to g_dirname() or the dirname
394  * commandline application.
395  *
396  * Example:
397  * br_extract_dir ("/usr/local/foobar");  --> Returns: "/usr/local"
398  */
399 char *
br_extract_dir(const char * path)400 br_extract_dir (const char *path)
401 {
402 	char *end, *result;
403 
404 	br_return_val_if_fail (path != (char *) NULL, (char *) NULL);
405 
406 	end = strrchr (path, '/');
407 	if (!end) return strdup (".");
408 
409 	while (end > path && *end == '/')
410 		end--;
411 	result = br_strndup ((char *) path, end - path + 1);
412 	if (!*result)
413 	{
414 		free (result);
415 		return strdup ("/");
416 	} else
417 		return result;
418 }
419 
420 
421 /**
422  * br_extract_prefix:
423  * path: The full path of an executable or library.
424  * Returns: The prefix, or NULL on error. This string should be freed when no longer needed.
425  *
426  * Extracts the prefix from path. This function assumes that your executable
427  * or library is installed in an LSB-compatible directory structure.
428  *
429  * Example:
430  * br_extract_prefix ("/usr/bin/gnome-panel");       --> Returns "/usr"
431  * br_extract_prefix ("/usr/local/lib/libfoo.so");   --> Returns "/usr/local"
432  * br_extract_prefix ("/usr/local/libfoo.so");       --> Returns "/usr"
433  */
434 char *
br_extract_prefix(const char * path)435 br_extract_prefix (const char *path)
436 {
437 	char *end, *tmp, *result;
438 
439 	br_return_val_if_fail (path != (char *) NULL, (char *) NULL);
440 
441 	if (!*path) return strdup ("/");
442 	end = strrchr (path, '/');
443 	if (!end) return strdup (path);
444 
445 	tmp = br_strndup ((char *) path, end - path);
446 	if (!*tmp)
447 	{
448 		free (tmp);
449 		return strdup ("/");
450 	}
451 	end = strrchr (tmp, '/');
452 	if (!end) return tmp;
453 
454 	result = br_strndup (tmp, end - tmp);
455 	free (tmp);
456 
457 	if (!*result)
458 	{
459 		free (result);
460 		result = strdup ("/");
461 	}
462 
463 	return result;
464 }
465 
466 
467 /**
468  * br_set_fallback_function:
469  * func: A function to call to find the binary.
470  * data: User data to pass to func.
471  *
472  * Sets a function to call to find the path to the binary, in
473  * case "/proc/self/maps" can't be opened. The function set should
474  * return a string that is safe to free with free().
475  */
476 void
br_set_locate_fallback_func(br_locate_fallback_func func,void * data)477 br_set_locate_fallback_func (br_locate_fallback_func func, void *data)
478 {
479 	fallback_func = func;
480 	fallback_data = data;
481 }
482 
483 
484 #ifdef __cplusplus
485 }
486 #endif /* __cplusplus */
487 
488 #endif /* _PREFIX_C */
489