svn merge -r 13240:13323 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[blender.git] / extern / binreloc / binreloc.c
1 /*
2  * BinReloc - a library for creating relocatable executables
3  * Written by: Hongli Lai <h.lai@chello.nl>
4  * http://autopackage.org/
5  *
6  * This source code is public domain. You can relicense this code
7  * under whatever license you want.
8  *
9  * See http://autopackage.org/docs/binreloc/ for
10  * more information and how to use this.
11  */
12
13 #ifndef __BINRELOC_C__
14 #define __BINRELOC_C__
15
16 #ifdef ENABLE_BINRELOC
17         #include <sys/types.h>
18         #include <sys/stat.h>
19         #include <unistd.h>
20 #endif /* ENABLE_BINRELOC */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <limits.h>
24 #include <string.h>
25 #include "binreloc.h"
26
27 #ifdef __cplusplus
28 extern "C" {
29 #endif /* __cplusplus */
30
31
32
33 /** @internal
34  * Find the canonical filename of the executable. Returns the filename
35  * (which must be freed) or NULL on error. If the parameter 'error' is
36  * not NULL, the error code will be stored there, if an error occured.
37  */
38 static char *
39 _br_find_exe (BrInitError *error)
40 {
41 #ifndef ENABLE_BINRELOC
42         if (error)
43                 *error = BR_INIT_ERROR_DISABLED;
44         return NULL;
45 #else
46         char *path, *path2, *line, *result;
47         size_t buf_size;
48         ssize_t size;
49         struct stat stat_buf;
50         FILE *f;
51
52         /* Read from /proc/self/exe (symlink) */
53         if (sizeof (path) > SSIZE_MAX)
54                 buf_size = SSIZE_MAX - 1;
55         else
56                 buf_size = PATH_MAX - 1;
57         path = (char *) malloc (buf_size);
58         if (path == NULL) {
59                 /* Cannot allocate memory. */
60                 if (error)
61                         *error = BR_INIT_ERROR_NOMEM;
62                 return NULL;
63         }
64         path2 = (char *) malloc (buf_size);
65         if (path2 == NULL) {
66                 /* Cannot allocate memory. */
67                 if (error)
68                         *error = BR_INIT_ERROR_NOMEM;
69                 free (path);
70                 return NULL;
71         }
72
73         strncpy (path2, "/proc/self/exe", buf_size - 1);
74
75         while (1) {
76                 int i;
77
78                 size = readlink (path2, path, buf_size - 1);
79                 if (size == -1) {
80                         /* Error. */
81                         free (path2);
82                         break;
83                 }
84
85                 /* readlink() success. */
86                 path[size] = '\0';
87
88                 /* Check whether the symlink's target is also a symlink.
89                  * We want to get the final target. */
90                 i = stat (path, &stat_buf);
91                 if (i == -1) {
92                         /* Error. */
93                         free (path2);
94                         break;
95                 }
96
97                 /* stat() success. */
98                 if (!S_ISLNK (stat_buf.st_mode)) {
99                         /* path is not a symlink. Done. */
100                         free (path2);
101                         return path;
102                 }
103
104                 /* path is a symlink. Continue loop and resolve this. */
105                 strncpy (path, path2, buf_size - 1);
106         }
107
108
109         /* readlink() or stat() failed; this can happen when the program is
110          * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
111
112         buf_size = PATH_MAX + 128;
113         line = (char *) realloc (path, buf_size);
114         if (line == NULL) {
115                 /* Cannot allocate memory. */
116                 free (path);
117                 if (error)
118                         *error = BR_INIT_ERROR_NOMEM;
119                 return NULL;
120         }
121
122         f = fopen ("/proc/self/maps", "r");
123         if (f == NULL) {
124                 free (line);
125                 if (error)
126                         *error = BR_INIT_ERROR_OPEN_MAPS;
127                 return NULL;
128         }
129
130         /* The first entry should be the executable name. */
131         result = fgets (line, (int) buf_size, f);
132         if (result == NULL) {
133                 fclose (f);
134                 free (line);
135                 if (error)
136                         *error = BR_INIT_ERROR_READ_MAPS;
137                 return NULL;
138         }
139
140         /* Get rid of newline character. */
141         buf_size = strlen (line);
142         if (buf_size <= 0) {
143                 /* Huh? An empty string? */
144                 fclose (f);
145                 free (line);
146                 if (error)
147                         *error = BR_INIT_ERROR_INVALID_MAPS;
148                 return NULL;
149         }
150         if (line[buf_size - 1] == 10)
151                 line[buf_size - 1] = 0;
152
153         /* Extract the filename; it is always an absolute path. */
154         path = strchr (line, '/');
155
156         /* Sanity check. */
157         if (strstr (line, " r-xp ") == NULL || path == NULL) {
158                 fclose (f);
159                 free (line);
160                 if (error)
161                         *error = BR_INIT_ERROR_INVALID_MAPS;
162                 return NULL;
163         }
164
165         path = strdup (path);
166         free (line);
167         fclose (f);
168         return path;
169 #endif /* ENABLE_BINRELOC */
170 }
171
172
173 /** @internal
174  * Find the canonical filename of the executable which owns symbol.
175  * Returns a filename which must be freed, or NULL on error.
176  */
177 static char *
178 _br_find_exe_for_symbol (const void *symbol, BrInitError *error)
179 {
180 #ifndef ENABLE_BINRELOC
181         if (error)
182                 *error = BR_INIT_ERROR_DISABLED;
183         return (char *) NULL;
184 #else
185         #define SIZE PATH_MAX + 100
186         FILE *f;
187         size_t address_string_len;
188         char *address_string, line[SIZE], *found;
189
190         if (symbol == NULL)
191                 return (char *) NULL;
192
193         f = fopen ("/proc/self/maps", "r");
194         if (f == NULL)
195                 return (char *) NULL;
196
197         address_string_len = 4;
198         address_string = (char *) malloc (address_string_len);
199         found = (char *) NULL;
200
201         while (!feof (f)) {
202                 char *start_addr, *end_addr, *end_addr_end, *file;
203                 void *start_addr_p, *end_addr_p;
204                 size_t len;
205
206                 if (fgets (line, SIZE, f) == NULL)
207                         break;
208
209                 /* Sanity check. */
210                 if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
211                         continue;
212
213                 /* Parse line. */
214                 start_addr = line;
215                 end_addr = strchr (line, '-');
216                 file = strchr (line, '/');
217
218                 /* More sanity check. */
219                 if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
220                         continue;
221
222                 end_addr[0] = '\0';
223                 end_addr++;
224                 end_addr_end = strchr (end_addr, ' ');
225                 if (end_addr_end == NULL)
226                         continue;
227
228                 end_addr_end[0] = '\0';
229                 len = strlen (file);
230                 if (len == 0)
231                         continue;
232                 if (file[len - 1] == '\n')
233                         file[len - 1] = '\0';
234
235                 /* Get rid of "(deleted)" from the filename. */
236                 len = strlen (file);
237                 if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
238                         file[len - 10] = '\0';
239
240                 /* I don't know whether this can happen but better safe than sorry. */
241                 len = strlen (start_addr);
242                 if (len != strlen (end_addr))
243                         continue;
244
245
246                 /* Transform the addresses into a string in the form of 0xdeadbeef,
247                  * then transform that into a pointer. */
248                 if (address_string_len < len + 3) {
249                         address_string_len = len + 3;
250                         address_string = (char *) realloc (address_string, address_string_len);
251                 }
252
253                 memcpy (address_string, "0x", 2);
254                 memcpy (address_string + 2, start_addr, len);
255                 address_string[2 + len] = '\0';
256                 sscanf (address_string, "%p", &start_addr_p);
257
258                 memcpy (address_string, "0x", 2);
259                 memcpy (address_string + 2, end_addr, len);
260                 address_string[2 + len] = '\0';
261                 sscanf (address_string, "%p", &end_addr_p);
262
263
264                 if (symbol >= start_addr_p && symbol < end_addr_p) {
265                         found = file;
266                         break;
267                 }
268         }
269
270         free (address_string);
271         fclose (f);
272
273         if (found == NULL)
274                 return (char *) NULL;
275         else
276                 return strdup (found);
277 #endif /* ENABLE_BINRELOC */
278 }
279
280
281 #ifndef BINRELOC_RUNNING_DOXYGEN
282         #undef NULL
283         #define NULL ((void *) 0) /* typecasted as char* for C++ type safeness */
284 #endif
285
286 static char *exe = (char *) NULL;
287
288
289 /** Initialize the BinReloc library (for applications).
290  *
291  * This function must be called before using any other BinReloc functions.
292  * It attempts to locate the application's canonical filename.
293  *
294  * @note If you want to use BinReloc for a library, then you should call
295  *       br_init_lib() instead.
296  *
297  * @param error  If BinReloc failed to initialize, then the error code will
298  *               be stored in this variable. Set to NULL if you want to
299  *               ignore this. See #BrInitError for a list of error codes.
300  *
301  * @returns 1 on success, 0 if BinReloc failed to initialize.
302  */
303 int
304 br_init (BrInitError *error)
305 {
306         exe = _br_find_exe (error);
307         return exe != NULL;
308 }
309
310
311 /** Initialize the BinReloc library (for libraries).
312  *
313  * This function must be called before using any other BinReloc functions.
314  * It attempts to locate the calling library's canonical filename.
315  *
316  * @note The BinReloc source code MUST be included in your library, or this
317  *       function won't work correctly.
318  *
319  * @param error  If BinReloc failed to initialize, then the error code will
320  *               be stored in this variable. Set to NULL if you want to
321  *               ignore this. See #BrInitError for a list of error codes.
322  *
323  * @returns 1 on success, 0 if a filename cannot be found.
324  */
325 int
326 br_init_lib (BrInitError *error)
327 {
328         exe = _br_find_exe_for_symbol ((const void *) "", error);
329         return exe != NULL;
330 }
331
332
333 /** Find the canonical filename of the current application.
334  *
335  * @param default_exe  A default filename which will be used as fallback.
336  * @returns A string containing the application's canonical filename,
337  *          which must be freed when no longer necessary. If BinReloc is
338  *          not initialized, or if br_init() failed, then a copy of
339  *          default_exe will be returned. If default_exe is NULL, then
340  *          NULL will be returned.
341  */
342 char *
343 br_find_exe (const char *default_exe)
344 {
345         if (exe == (char *) NULL) {
346                 /* BinReloc is not initialized. */
347                 if (default_exe != (const char *) NULL)
348                         return strdup (default_exe);
349                 else
350                         return (char *) NULL;
351         }
352         return strdup (exe);
353 }
354
355
356 /** Locate the directory in which the current application is installed.
357  *
358  * The prefix is generated by the following pseudo-code evaluation:
359  * \code
360  * dirname(exename)
361  * \endcode
362  *
363  * @param default_dir  A default directory which will used as fallback.
364  * @return A string containing the directory, which must be freed when no
365  *         longer necessary. If BinReloc is not initialized, or if the
366  *         initialization function failed, then a copy of default_dir
367  *         will be returned. If default_dir is NULL, then NULL will be
368  *         returned.
369  */
370 char *
371 br_find_exe_dir (const char *default_dir)
372 {
373         if (exe == NULL) {
374                 /* BinReloc not initialized. */
375                 if (default_dir != NULL)
376                         return strdup (default_dir);
377                 else
378                         return NULL;
379         }
380
381         return br_dirname (exe);
382 }
383
384
385 /** Locate the prefix in which the current application is installed.
386  *
387  * The prefix is generated by the following pseudo-code evaluation:
388  * \code
389  * dirname(dirname(exename))
390  * \endcode
391  *
392  * @param default_prefix  A default prefix which will used as fallback.
393  * @return A string containing the prefix, which must be freed when no
394  *         longer necessary. If BinReloc is not initialized, or if
395  *         the initialization function failed, then a copy of default_prefix
396  *         will be returned. If default_prefix is NULL, then NULL will be returned.
397  */
398 char *
399 br_find_prefix (const char *default_prefix)
400 {
401         char *dir1, *dir2;
402
403         if (exe == (char *) NULL) {
404                 /* BinReloc not initialized. */
405                 if (default_prefix != (const char *) NULL)
406                         return strdup (default_prefix);
407                 else
408                         return (char *) NULL;
409         }
410
411         dir1 = br_dirname (exe);
412         dir2 = br_dirname (dir1);
413         free (dir1);
414         return dir2;
415 }
416
417
418 /** Locate the application's binary folder.
419  *
420  * The path is generated by the following pseudo-code evaluation:
421  * \code
422  * prefix + "/bin"
423  * \endcode
424  *
425  * @param default_bin_dir  A default path which will used as fallback.
426  * @return A string containing the bin folder's path, which must be freed when
427  *         no longer necessary. If BinReloc is not initialized, or if
428  *         the initialization function failed, then a copy of default_bin_dir will
429  *         be returned. If default_bin_dir is NULL, then NULL will be returned.
430  */
431 char *
432 br_find_bin_dir (const char *default_bin_dir)
433 {
434         char *prefix, *dir;
435
436         prefix = br_find_prefix ((const char *) NULL);
437         if (prefix == (char *) NULL) {
438                 /* BinReloc not initialized. */
439                 if (default_bin_dir != (const char *) NULL)
440                         return strdup (default_bin_dir);
441                 else
442                         return (char *) NULL;
443         }
444
445         dir = br_build_path (prefix, "bin");
446         free (prefix);
447         return dir;
448 }
449
450
451 /** Locate the application's superuser binary folder.
452  *
453  * The path is generated by the following pseudo-code evaluation:
454  * \code
455  * prefix + "/sbin"
456  * \endcode
457  *
458  * @param default_sbin_dir  A default path which will used as fallback.
459  * @return A string containing the sbin folder's path, which must be freed when
460  *         no longer necessary. If BinReloc is not initialized, or if the
461  *         initialization function failed, then a copy of default_sbin_dir will
462  *         be returned. If default_bin_dir is NULL, then NULL will be returned.
463  */
464 char *
465 br_find_sbin_dir (const char *default_sbin_dir)
466 {
467         char *prefix, *dir;
468
469         prefix = br_find_prefix ((const char *) NULL);
470         if (prefix == (char *) NULL) {
471                 /* BinReloc not initialized. */
472                 if (default_sbin_dir != (const char *) NULL)
473                         return strdup (default_sbin_dir);
474                 else
475                         return (char *) NULL;
476         }
477
478         dir = br_build_path (prefix, "sbin");
479         free (prefix);
480         return dir;
481 }
482
483
484 /** Locate the application's data folder.
485  *
486  * The path is generated by the following pseudo-code evaluation:
487  * \code
488  * prefix + "/share"
489  * \endcode
490  *
491  * @param default_data_dir  A default path which will used as fallback.
492  * @return A string containing the data folder's path, which must be freed when
493  *         no longer necessary. If BinReloc is not initialized, or if the
494  *         initialization function failed, then a copy of default_data_dir
495  *         will be returned. If default_data_dir is NULL, then NULL will be
496  *         returned.
497  */
498 char *
499 br_find_data_dir (const char *default_data_dir)
500 {
501         char *prefix, *dir;
502
503         prefix = br_find_prefix ((const char *) NULL);
504         if (prefix == (char *) NULL) {
505                 /* BinReloc not initialized. */
506                 if (default_data_dir != (const char *) NULL)
507                         return strdup (default_data_dir);
508                 else
509                         return (char *) NULL;
510         }
511
512         dir = br_build_path (prefix, "share");
513         free (prefix);
514         return dir;
515 }
516
517
518 /** Locate the application's localization folder.
519  *
520  * The path is generated by the following pseudo-code evaluation:
521  * \code
522  * prefix + "/share/locale"
523  * \endcode
524  *
525  * @param default_locale_dir  A default path which will used as fallback.
526  * @return A string containing the localization folder's path, which must be freed when
527  *         no longer necessary. If BinReloc is not initialized, or if the
528  *         initialization function failed, then a copy of default_locale_dir will be returned.
529  *         If default_locale_dir is NULL, then NULL will be returned.
530  */
531 char *
532 br_find_locale_dir (const char *default_locale_dir)
533 {
534         char *data_dir, *dir;
535
536         data_dir = br_find_data_dir ((const char *) NULL);
537         if (data_dir == (char *) NULL) {
538                 /* BinReloc not initialized. */
539                 if (default_locale_dir != (const char *) NULL)
540                         return strdup (default_locale_dir);
541                 else
542                         return (char *) NULL;
543         }
544
545         dir = br_build_path (data_dir, "locale");
546         free (data_dir);
547         return dir;
548 }
549
550
551 /** Locate the application's library folder.
552  *
553  * The path is generated by the following pseudo-code evaluation:
554  * \code
555  * prefix + "/lib"
556  * \endcode
557  *
558  * @param default_lib_dir  A default path which will used as fallback.
559  * @return A string containing the library folder's path, which must be freed when
560  *         no longer necessary. If BinReloc is not initialized, or if the initialization
561  *         function failed, then a copy of default_lib_dir will be returned.
562  *         If default_lib_dir is NULL, then NULL will be returned.
563  */
564 char *
565 br_find_lib_dir (const char *default_lib_dir)
566 {
567         char *prefix, *dir;
568
569         prefix = br_find_prefix ((const char *) NULL);
570         if (prefix == (char *) NULL) {
571                 /* BinReloc not initialized. */
572                 if (default_lib_dir != (const char *) NULL)
573                         return strdup (default_lib_dir);
574                 else
575                         return (char *) NULL;
576         }
577
578         dir = br_build_path (prefix, "lib");
579         free (prefix);
580         return dir;
581 }
582
583
584 /** Locate the application's libexec folder.
585  *
586  * The path is generated by the following pseudo-code evaluation:
587  * \code
588  * prefix + "/libexec"
589  * \endcode
590  *
591  * @param default_libexec_dir  A default path which will used as fallback.
592  * @return A string containing the libexec folder's path, which must be freed when
593  *         no longer necessary. If BinReloc is not initialized, or if the initialization
594  *         function failed, then a copy of default_libexec_dir will be returned.
595  *         If default_libexec_dir is NULL, then NULL will be returned.
596  */
597 char *
598 br_find_libexec_dir (const char *default_libexec_dir)
599 {
600         char *prefix, *dir;
601
602         prefix = br_find_prefix ((const char *) NULL);
603         if (prefix == (char *) NULL) {
604                 /* BinReloc not initialized. */
605                 if (default_libexec_dir != (const char *) NULL)
606                         return strdup (default_libexec_dir);
607                 else
608                         return (char *) NULL;
609         }
610
611         dir = br_build_path (prefix, "libexec");
612         free (prefix);
613         return dir;
614 }
615
616
617 /** Locate the application's configuration files folder.
618  *
619  * The path is generated by the following pseudo-code evaluation:
620  * \code
621  * prefix + "/etc"
622  * \endcode
623  *
624  * @param default_etc_dir  A default path which will used as fallback.
625  * @return A string containing the etc folder's path, which must be freed when
626  *         no longer necessary. If BinReloc is not initialized, or if the initialization
627  *         function failed, then a copy of default_etc_dir will be returned.
628  *         If default_etc_dir is NULL, then NULL will be returned.
629  */
630 char *
631 br_find_etc_dir (const char *default_etc_dir)
632 {
633         char *prefix, *dir;
634
635         prefix = br_find_prefix ((const char *) NULL);
636         if (prefix == (char *) NULL) {
637                 /* BinReloc not initialized. */
638                 if (default_etc_dir != (const char *) NULL)
639                         return strdup (default_etc_dir);
640                 else
641                         return (char *) NULL;
642         }
643
644         dir = br_build_path (prefix, "etc");
645         free (prefix);
646         return dir;
647 }
648
649
650 /***********************
651  * Utility functions
652  ***********************/
653
654 /** Concatenate str1 and str2 to a newly allocated string.
655  *
656  * @param str1 A string.
657  * @param str2 Another string.
658  * @returns A newly-allocated string. This string should be freed when no longer needed.
659  */
660 char *
661 br_strcat (const char *str1, const char *str2)
662 {
663         char *result;
664         size_t len1, len2;
665
666         if (str1 == NULL)
667                 str1 = "";
668         if (str2 == NULL)
669                 str2 = "";
670
671         len1 = strlen (str1);
672         len2 = strlen (str2);
673
674         result = (char *) malloc (len1 + len2 + 1);
675         memcpy (result, str1, len1);
676         memcpy (result + len1, str2, len2);
677         result[len1 + len2] = '\0';
678
679         return result;
680 }
681
682
683 char *
684 br_build_path (const char *dir, const char *file)
685 {
686         char *dir2, *result;
687         size_t len;
688         int must_free = 0;
689
690         len = strlen (dir);
691         if (len > 0 && dir[len - 1] != '/') {
692                 dir2 = br_strcat (dir, "/");
693                 must_free = 1;
694         } else
695                 dir2 = (char *) dir;
696
697         result = br_strcat (dir2, file);
698         if (must_free)
699                 free (dir2);
700         return result;
701 }
702
703
704 /* Emulates glibc's strndup() */
705 static char *
706 br_strndup (const char *str, size_t size)
707 {
708         char *result = (char *) NULL;
709         size_t len;
710
711         if (str == (const char *) NULL)
712                 return (char *) NULL;
713
714         len = strlen (str);
715         if (len == 0)
716                 return strdup ("");
717         if (size > len)
718                 size = len;
719
720         result = (char *) malloc (len + 1);
721         memcpy (result, str, size);
722         result[size] = '\0';
723         return result;
724 }
725
726
727 /** Extracts the directory component of a path.
728  *
729  * Similar to g_dirname() or the dirname commandline application.
730  *
731  * Example:
732  * \code
733  * br_dirname ("/usr/local/foobar");  --> Returns: "/usr/local"
734  * \endcode
735  *
736  * @param path  A path.
737  * @returns     A directory name. This string should be freed when no longer needed.
738  */
739 char *
740 br_dirname (const char *path)
741 {
742         char *end, *result;
743
744         if (path == (const char *) NULL)
745                 return (char *) NULL;
746
747         end = strrchr (path, '/');
748         if (end == (const char *) NULL)
749                 return strdup (".");
750
751         while (end > path && *end == '/')
752                 end--;
753         result = br_strndup (path, end - path + 1);
754         if (result[0] == 0) {
755                 free (result);
756                 return strdup ("/");
757         } else
758                 return result;
759 }
760
761
762 #ifdef __cplusplus
763 }
764 #endif /* __cplusplus */
765
766 #endif /* __BINRELOC_C__ */