sourcepawn/compiler/binreloc.c
author David Anderson <dvander@alliedmods.net>
Wed Feb 25 20:07:12 2009 -0500 (2009-02-25)
changeset 2607 f3b8bbe17f85
parent 353 492a8d0d5f59
permissions -rw-r--r--
Backed out changeset fa286305bd3f
     1 /*
     2  * BinReloc - a library for creating relocatable executables
     3  * Written by: Hongli Lai <[email protected]>
     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__ */