dvander@353: /* dvander@353: * BinReloc - a library for creating relocatable executables dvander@353: * Written by: Hongli Lai dvander@353: * http://autopackage.org/ dvander@353: * dvander@353: * This source code is public domain. You can relicense this code dvander@353: * under whatever license you want. dvander@353: * dvander@353: * See http://autopackage.org/docs/binreloc/ for dvander@353: * more information and how to use this. dvander@353: */ dvander@353: dvander@353: #ifndef __BINRELOC_C__ dvander@353: #define __BINRELOC_C__ dvander@353: dvander@353: #ifdef ENABLE_BINRELOC dvander@353: #include dvander@353: #include dvander@353: #include dvander@353: #endif /* ENABLE_BINRELOC */ dvander@353: #include dvander@353: #include dvander@353: #include dvander@353: #include dvander@353: #include "binreloc.h" dvander@353: dvander@353: #ifdef __cplusplus dvander@353: extern "C" { dvander@353: #endif /* __cplusplus */ dvander@353: dvander@353: dvander@353: dvander@353: /** @internal dvander@353: * Find the canonical filename of the executable. Returns the filename dvander@353: * (which must be freed) or NULL on error. If the parameter 'error' is dvander@353: * not NULL, the error code will be stored there, if an error occured. dvander@353: */ dvander@353: static char * dvander@353: _br_find_exe (BrInitError *error) dvander@353: { dvander@353: #ifndef ENABLE_BINRELOC dvander@353: if (error) dvander@353: *error = BR_INIT_ERROR_DISABLED; dvander@353: return NULL; dvander@353: #else dvander@353: char *path, *path2, *line, *result; dvander@353: size_t buf_size; dvander@353: ssize_t size; dvander@353: struct stat stat_buf; dvander@353: FILE *f; dvander@353: dvander@353: /* Read from /proc/self/exe (symlink) */ dvander@353: if (sizeof (path) > SSIZE_MAX) dvander@353: buf_size = SSIZE_MAX - 1; dvander@353: else dvander@353: buf_size = PATH_MAX - 1; dvander@353: path = (char *) malloc (buf_size); dvander@353: if (path == NULL) { dvander@353: /* Cannot allocate memory. */ dvander@353: if (error) dvander@353: *error = BR_INIT_ERROR_NOMEM; dvander@353: return NULL; dvander@353: } dvander@353: path2 = (char *) malloc (buf_size); dvander@353: if (path2 == NULL) { dvander@353: /* Cannot allocate memory. */ dvander@353: if (error) dvander@353: *error = BR_INIT_ERROR_NOMEM; dvander@353: free (path); dvander@353: return NULL; dvander@353: } dvander@353: dvander@353: strncpy (path2, "/proc/self/exe", buf_size - 1); dvander@353: dvander@353: while (1) { dvander@353: int i; dvander@353: dvander@353: size = readlink (path2, path, buf_size - 1); dvander@353: if (size == -1) { dvander@353: /* Error. */ dvander@353: free (path2); dvander@353: break; dvander@353: } dvander@353: dvander@353: /* readlink() success. */ dvander@353: path[size] = '\0'; dvander@353: dvander@353: /* Check whether the symlink's target is also a symlink. dvander@353: * We want to get the final target. */ dvander@353: i = stat (path, &stat_buf); dvander@353: if (i == -1) { dvander@353: /* Error. */ dvander@353: free (path2); dvander@353: break; dvander@353: } dvander@353: dvander@353: /* stat() success. */ dvander@353: if (!S_ISLNK (stat_buf.st_mode)) { dvander@353: /* path is not a symlink. Done. */ dvander@353: free (path2); dvander@353: return path; dvander@353: } dvander@353: dvander@353: /* path is a symlink. Continue loop and resolve this. */ dvander@353: strncpy (path, path2, buf_size - 1); dvander@353: } dvander@353: dvander@353: dvander@353: /* readlink() or stat() failed; this can happen when the program is dvander@353: * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */ dvander@353: dvander@353: buf_size = PATH_MAX + 128; dvander@353: line = (char *) realloc (path, buf_size); dvander@353: if (line == NULL) { dvander@353: /* Cannot allocate memory. */ dvander@353: free (path); dvander@353: if (error) dvander@353: *error = BR_INIT_ERROR_NOMEM; dvander@353: return NULL; dvander@353: } dvander@353: dvander@353: f = fopen ("/proc/self/maps", "r"); dvander@353: if (f == NULL) { dvander@353: free (line); dvander@353: if (error) dvander@353: *error = BR_INIT_ERROR_OPEN_MAPS; dvander@353: return NULL; dvander@353: } dvander@353: dvander@353: /* The first entry should be the executable name. */ dvander@353: result = fgets (line, (int) buf_size, f); dvander@353: if (result == NULL) { dvander@353: fclose (f); dvander@353: free (line); dvander@353: if (error) dvander@353: *error = BR_INIT_ERROR_READ_MAPS; dvander@353: return NULL; dvander@353: } dvander@353: dvander@353: /* Get rid of newline character. */ dvander@353: buf_size = strlen (line); dvander@353: if (buf_size <= 0) { dvander@353: /* Huh? An empty string? */ dvander@353: fclose (f); dvander@353: free (line); dvander@353: if (error) dvander@353: *error = BR_INIT_ERROR_INVALID_MAPS; dvander@353: return NULL; dvander@353: } dvander@353: if (line[buf_size - 1] == 10) dvander@353: line[buf_size - 1] = 0; dvander@353: dvander@353: /* Extract the filename; it is always an absolute path. */ dvander@353: path = strchr (line, '/'); dvander@353: dvander@353: /* Sanity check. */ dvander@353: if (strstr (line, " r-xp ") == NULL || path == NULL) { dvander@353: fclose (f); dvander@353: free (line); dvander@353: if (error) dvander@353: *error = BR_INIT_ERROR_INVALID_MAPS; dvander@353: return NULL; dvander@353: } dvander@353: dvander@353: path = strdup (path); dvander@353: free (line); dvander@353: fclose (f); dvander@353: return path; dvander@353: #endif /* ENABLE_BINRELOC */ dvander@353: } dvander@353: dvander@353: dvander@353: /** @internal dvander@353: * Find the canonical filename of the executable which owns symbol. dvander@353: * Returns a filename which must be freed, or NULL on error. dvander@353: */ dvander@353: static char * dvander@353: _br_find_exe_for_symbol (const void *symbol, BrInitError *error) dvander@353: { dvander@353: #ifndef ENABLE_BINRELOC dvander@353: if (error) dvander@353: *error = BR_INIT_ERROR_DISABLED; dvander@353: return (char *) NULL; dvander@353: #else dvander@353: #define SIZE PATH_MAX + 100 dvander@353: FILE *f; dvander@353: size_t address_string_len; dvander@353: char *address_string, line[SIZE], *found; dvander@353: dvander@353: if (symbol == NULL) dvander@353: return (char *) NULL; dvander@353: dvander@353: f = fopen ("/proc/self/maps", "r"); dvander@353: if (f == NULL) dvander@353: return (char *) NULL; dvander@353: dvander@353: address_string_len = 4; dvander@353: address_string = (char *) malloc (address_string_len); dvander@353: found = (char *) NULL; dvander@353: dvander@353: while (!feof (f)) { dvander@353: char *start_addr, *end_addr, *end_addr_end, *file; dvander@353: void *start_addr_p, *end_addr_p; dvander@353: size_t len; dvander@353: dvander@353: if (fgets (line, SIZE, f) == NULL) dvander@353: break; dvander@353: dvander@353: /* Sanity check. */ dvander@353: if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL) dvander@353: continue; dvander@353: dvander@353: /* Parse line. */ dvander@353: start_addr = line; dvander@353: end_addr = strchr (line, '-'); dvander@353: file = strchr (line, '/'); dvander@353: dvander@353: /* More sanity check. */ dvander@353: if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-')) dvander@353: continue; dvander@353: dvander@353: end_addr[0] = '\0'; dvander@353: end_addr++; dvander@353: end_addr_end = strchr (end_addr, ' '); dvander@353: if (end_addr_end == NULL) dvander@353: continue; dvander@353: dvander@353: end_addr_end[0] = '\0'; dvander@353: len = strlen (file); dvander@353: if (len == 0) dvander@353: continue; dvander@353: if (file[len - 1] == '\n') dvander@353: file[len - 1] = '\0'; dvander@353: dvander@353: /* Get rid of "(deleted)" from the filename. */ dvander@353: len = strlen (file); dvander@353: if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0) dvander@353: file[len - 10] = '\0'; dvander@353: dvander@353: /* I don't know whether this can happen but better safe than sorry. */ dvander@353: len = strlen (start_addr); dvander@353: if (len != strlen (end_addr)) dvander@353: continue; dvander@353: dvander@353: dvander@353: /* Transform the addresses into a string in the form of 0xdeadbeef, dvander@353: * then transform that into a pointer. */ dvander@353: if (address_string_len < len + 3) { dvander@353: address_string_len = len + 3; dvander@353: address_string = (char *) realloc (address_string, address_string_len); dvander@353: } dvander@353: dvander@353: memcpy (address_string, "0x", 2); dvander@353: memcpy (address_string + 2, start_addr, len); dvander@353: address_string[2 + len] = '\0'; dvander@353: sscanf (address_string, "%p", &start_addr_p); dvander@353: dvander@353: memcpy (address_string, "0x", 2); dvander@353: memcpy (address_string + 2, end_addr, len); dvander@353: address_string[2 + len] = '\0'; dvander@353: sscanf (address_string, "%p", &end_addr_p); dvander@353: dvander@353: dvander@353: if (symbol >= start_addr_p && symbol < end_addr_p) { dvander@353: found = file; dvander@353: break; dvander@353: } dvander@353: } dvander@353: dvander@353: free (address_string); dvander@353: fclose (f); dvander@353: dvander@353: if (found == NULL) dvander@353: return (char *) NULL; dvander@353: else dvander@353: return strdup (found); dvander@353: #endif /* ENABLE_BINRELOC */ dvander@353: } dvander@353: dvander@353: dvander@353: #ifndef BINRELOC_RUNNING_DOXYGEN dvander@353: #undef NULL dvander@353: #define NULL ((void *) 0) /* typecasted as char* for C++ type safeness */ dvander@353: #endif dvander@353: dvander@353: static char *exe = (char *) NULL; dvander@353: dvander@353: dvander@353: /** Initialize the BinReloc library (for applications). dvander@353: * dvander@353: * This function must be called before using any other BinReloc functions. dvander@353: * It attempts to locate the application's canonical filename. dvander@353: * dvander@353: * @note If you want to use BinReloc for a library, then you should call dvander@353: * br_init_lib() instead. dvander@353: * dvander@353: * @param error If BinReloc failed to initialize, then the error code will dvander@353: * be stored in this variable. Set to NULL if you want to dvander@353: * ignore this. See #BrInitError for a list of error codes. dvander@353: * dvander@353: * @returns 1 on success, 0 if BinReloc failed to initialize. dvander@353: */ dvander@353: int dvander@353: br_init (BrInitError *error) dvander@353: { dvander@353: exe = _br_find_exe (error); dvander@353: return exe != NULL; dvander@353: } dvander@353: dvander@353: dvander@353: /** Initialize the BinReloc library (for libraries). dvander@353: * dvander@353: * This function must be called before using any other BinReloc functions. dvander@353: * It attempts to locate the calling library's canonical filename. dvander@353: * dvander@353: * @note The BinReloc source code MUST be included in your library, or this dvander@353: * function won't work correctly. dvander@353: * dvander@353: * @param error If BinReloc failed to initialize, then the error code will dvander@353: * be stored in this variable. Set to NULL if you want to dvander@353: * ignore this. See #BrInitError for a list of error codes. dvander@353: * dvander@353: * @returns 1 on success, 0 if a filename cannot be found. dvander@353: */ dvander@353: int dvander@353: br_init_lib (BrInitError *error) dvander@353: { dvander@353: exe = _br_find_exe_for_symbol ((const void *) "", error); dvander@353: return exe != NULL; dvander@353: } dvander@353: dvander@353: dvander@353: /** Find the canonical filename of the current application. dvander@353: * dvander@353: * @param default_exe A default filename which will be used as fallback. dvander@353: * @returns A string containing the application's canonical filename, dvander@353: * which must be freed when no longer necessary. If BinReloc is dvander@353: * not initialized, or if br_init() failed, then a copy of dvander@353: * default_exe will be returned. If default_exe is NULL, then dvander@353: * NULL will be returned. dvander@353: */ dvander@353: char * dvander@353: br_find_exe (const char *default_exe) dvander@353: { dvander@353: if (exe == (char *) NULL) { dvander@353: /* BinReloc is not initialized. */ dvander@353: if (default_exe != (const char *) NULL) dvander@353: return strdup (default_exe); dvander@353: else dvander@353: return (char *) NULL; dvander@353: } dvander@353: return strdup (exe); dvander@353: } dvander@353: dvander@353: dvander@353: /** Locate the directory in which the current application is installed. dvander@353: * dvander@353: * The prefix is generated by the following pseudo-code evaluation: dvander@353: * \code dvander@353: * dirname(exename) dvander@353: * \endcode dvander@353: * dvander@353: * @param default_dir A default directory which will used as fallback. dvander@353: * @return A string containing the directory, which must be freed when no dvander@353: * longer necessary. If BinReloc is not initialized, or if the dvander@353: * initialization function failed, then a copy of default_dir dvander@353: * will be returned. If default_dir is NULL, then NULL will be dvander@353: * returned. dvander@353: */ dvander@353: char * dvander@353: br_find_exe_dir (const char *default_dir) dvander@353: { dvander@353: if (exe == NULL) { dvander@353: /* BinReloc not initialized. */ dvander@353: if (default_dir != NULL) dvander@353: return strdup (default_dir); dvander@353: else dvander@353: return NULL; dvander@353: } dvander@353: dvander@353: return br_dirname (exe); dvander@353: } dvander@353: dvander@353: dvander@353: /** Locate the prefix in which the current application is installed. dvander@353: * dvander@353: * The prefix is generated by the following pseudo-code evaluation: dvander@353: * \code dvander@353: * dirname(dirname(exename)) dvander@353: * \endcode dvander@353: * dvander@353: * @param default_prefix A default prefix which will used as fallback. dvander@353: * @return A string containing the prefix, which must be freed when no dvander@353: * longer necessary. If BinReloc is not initialized, or if dvander@353: * the initialization function failed, then a copy of default_prefix dvander@353: * will be returned. If default_prefix is NULL, then NULL will be returned. dvander@353: */ dvander@353: char * dvander@353: br_find_prefix (const char *default_prefix) dvander@353: { dvander@353: char *dir1, *dir2; dvander@353: dvander@353: if (exe == (char *) NULL) { dvander@353: /* BinReloc not initialized. */ dvander@353: if (default_prefix != (const char *) NULL) dvander@353: return strdup (default_prefix); dvander@353: else dvander@353: return (char *) NULL; dvander@353: } dvander@353: dvander@353: dir1 = br_dirname (exe); dvander@353: dir2 = br_dirname (dir1); dvander@353: free (dir1); dvander@353: return dir2; dvander@353: } dvander@353: dvander@353: dvander@353: /** Locate the application's binary folder. dvander@353: * dvander@353: * The path is generated by the following pseudo-code evaluation: dvander@353: * \code dvander@353: * prefix + "/bin" dvander@353: * \endcode dvander@353: * dvander@353: * @param default_bin_dir A default path which will used as fallback. dvander@353: * @return A string containing the bin folder's path, which must be freed when dvander@353: * no longer necessary. If BinReloc is not initialized, or if dvander@353: * the initialization function failed, then a copy of default_bin_dir will dvander@353: * be returned. If default_bin_dir is NULL, then NULL will be returned. dvander@353: */ dvander@353: char * dvander@353: br_find_bin_dir (const char *default_bin_dir) dvander@353: { dvander@353: char *prefix, *dir; dvander@353: dvander@353: prefix = br_find_prefix ((const char *) NULL); dvander@353: if (prefix == (char *) NULL) { dvander@353: /* BinReloc not initialized. */ dvander@353: if (default_bin_dir != (const char *) NULL) dvander@353: return strdup (default_bin_dir); dvander@353: else dvander@353: return (char *) NULL; dvander@353: } dvander@353: dvander@353: dir = br_build_path (prefix, "bin"); dvander@353: free (prefix); dvander@353: return dir; dvander@353: } dvander@353: dvander@353: dvander@353: /** Locate the application's superuser binary folder. dvander@353: * dvander@353: * The path is generated by the following pseudo-code evaluation: dvander@353: * \code dvander@353: * prefix + "/sbin" dvander@353: * \endcode dvander@353: * dvander@353: * @param default_sbin_dir A default path which will used as fallback. dvander@353: * @return A string containing the sbin folder's path, which must be freed when dvander@353: * no longer necessary. If BinReloc is not initialized, or if the dvander@353: * initialization function failed, then a copy of default_sbin_dir will dvander@353: * be returned. If default_bin_dir is NULL, then NULL will be returned. dvander@353: */ dvander@353: char * dvander@353: br_find_sbin_dir (const char *default_sbin_dir) dvander@353: { dvander@353: char *prefix, *dir; dvander@353: dvander@353: prefix = br_find_prefix ((const char *) NULL); dvander@353: if (prefix == (char *) NULL) { dvander@353: /* BinReloc not initialized. */ dvander@353: if (default_sbin_dir != (const char *) NULL) dvander@353: return strdup (default_sbin_dir); dvander@353: else dvander@353: return (char *) NULL; dvander@353: } dvander@353: dvander@353: dir = br_build_path (prefix, "sbin"); dvander@353: free (prefix); dvander@353: return dir; dvander@353: } dvander@353: dvander@353: dvander@353: /** Locate the application's data folder. dvander@353: * dvander@353: * The path is generated by the following pseudo-code evaluation: dvander@353: * \code dvander@353: * prefix + "/share" dvander@353: * \endcode dvander@353: * dvander@353: * @param default_data_dir A default path which will used as fallback. dvander@353: * @return A string containing the data folder's path, which must be freed when dvander@353: * no longer necessary. If BinReloc is not initialized, or if the dvander@353: * initialization function failed, then a copy of default_data_dir dvander@353: * will be returned. If default_data_dir is NULL, then NULL will be dvander@353: * returned. dvander@353: */ dvander@353: char * dvander@353: br_find_data_dir (const char *default_data_dir) dvander@353: { dvander@353: char *prefix, *dir; dvander@353: dvander@353: prefix = br_find_prefix ((const char *) NULL); dvander@353: if (prefix == (char *) NULL) { dvander@353: /* BinReloc not initialized. */ dvander@353: if (default_data_dir != (const char *) NULL) dvander@353: return strdup (default_data_dir); dvander@353: else dvander@353: return (char *) NULL; dvander@353: } dvander@353: dvander@353: dir = br_build_path (prefix, "share"); dvander@353: free (prefix); dvander@353: return dir; dvander@353: } dvander@353: dvander@353: dvander@353: /** Locate the application's localization folder. dvander@353: * dvander@353: * The path is generated by the following pseudo-code evaluation: dvander@353: * \code dvander@353: * prefix + "/share/locale" dvander@353: * \endcode dvander@353: * dvander@353: * @param default_locale_dir A default path which will used as fallback. dvander@353: * @return A string containing the localization folder's path, which must be freed when dvander@353: * no longer necessary. If BinReloc is not initialized, or if the dvander@353: * initialization function failed, then a copy of default_locale_dir will be returned. dvander@353: * If default_locale_dir is NULL, then NULL will be returned. dvander@353: */ dvander@353: char * dvander@353: br_find_locale_dir (const char *default_locale_dir) dvander@353: { dvander@353: char *data_dir, *dir; dvander@353: dvander@353: data_dir = br_find_data_dir ((const char *) NULL); dvander@353: if (data_dir == (char *) NULL) { dvander@353: /* BinReloc not initialized. */ dvander@353: if (default_locale_dir != (const char *) NULL) dvander@353: return strdup (default_locale_dir); dvander@353: else dvander@353: return (char *) NULL; dvander@353: } dvander@353: dvander@353: dir = br_build_path (data_dir, "locale"); dvander@353: free (data_dir); dvander@353: return dir; dvander@353: } dvander@353: dvander@353: dvander@353: /** Locate the application's library folder. dvander@353: * dvander@353: * The path is generated by the following pseudo-code evaluation: dvander@353: * \code dvander@353: * prefix + "/lib" dvander@353: * \endcode dvander@353: * dvander@353: * @param default_lib_dir A default path which will used as fallback. dvander@353: * @return A string containing the library folder's path, which must be freed when dvander@353: * no longer necessary. If BinReloc is not initialized, or if the initialization dvander@353: * function failed, then a copy of default_lib_dir will be returned. dvander@353: * If default_lib_dir is NULL, then NULL will be returned. dvander@353: */ dvander@353: char * dvander@353: br_find_lib_dir (const char *default_lib_dir) dvander@353: { dvander@353: char *prefix, *dir; dvander@353: dvander@353: prefix = br_find_prefix ((const char *) NULL); dvander@353: if (prefix == (char *) NULL) { dvander@353: /* BinReloc not initialized. */ dvander@353: if (default_lib_dir != (const char *) NULL) dvander@353: return strdup (default_lib_dir); dvander@353: else dvander@353: return (char *) NULL; dvander@353: } dvander@353: dvander@353: dir = br_build_path (prefix, "lib"); dvander@353: free (prefix); dvander@353: return dir; dvander@353: } dvander@353: dvander@353: dvander@353: /** Locate the application's libexec folder. dvander@353: * dvander@353: * The path is generated by the following pseudo-code evaluation: dvander@353: * \code dvander@353: * prefix + "/libexec" dvander@353: * \endcode dvander@353: * dvander@353: * @param default_libexec_dir A default path which will used as fallback. dvander@353: * @return A string containing the libexec folder's path, which must be freed when dvander@353: * no longer necessary. If BinReloc is not initialized, or if the initialization dvander@353: * function failed, then a copy of default_libexec_dir will be returned. dvander@353: * If default_libexec_dir is NULL, then NULL will be returned. dvander@353: */ dvander@353: char * dvander@353: br_find_libexec_dir (const char *default_libexec_dir) dvander@353: { dvander@353: char *prefix, *dir; dvander@353: dvander@353: prefix = br_find_prefix ((const char *) NULL); dvander@353: if (prefix == (char *) NULL) { dvander@353: /* BinReloc not initialized. */ dvander@353: if (default_libexec_dir != (const char *) NULL) dvander@353: return strdup (default_libexec_dir); dvander@353: else dvander@353: return (char *) NULL; dvander@353: } dvander@353: dvander@353: dir = br_build_path (prefix, "libexec"); dvander@353: free (prefix); dvander@353: return dir; dvander@353: } dvander@353: dvander@353: dvander@353: /** Locate the application's configuration files folder. dvander@353: * dvander@353: * The path is generated by the following pseudo-code evaluation: dvander@353: * \code dvander@353: * prefix + "/etc" dvander@353: * \endcode dvander@353: * dvander@353: * @param default_etc_dir A default path which will used as fallback. dvander@353: * @return A string containing the etc folder's path, which must be freed when dvander@353: * no longer necessary. If BinReloc is not initialized, or if the initialization dvander@353: * function failed, then a copy of default_etc_dir will be returned. dvander@353: * If default_etc_dir is NULL, then NULL will be returned. dvander@353: */ dvander@353: char * dvander@353: br_find_etc_dir (const char *default_etc_dir) dvander@353: { dvander@353: char *prefix, *dir; dvander@353: dvander@353: prefix = br_find_prefix ((const char *) NULL); dvander@353: if (prefix == (char *) NULL) { dvander@353: /* BinReloc not initialized. */ dvander@353: if (default_etc_dir != (const char *) NULL) dvander@353: return strdup (default_etc_dir); dvander@353: else dvander@353: return (char *) NULL; dvander@353: } dvander@353: dvander@353: dir = br_build_path (prefix, "etc"); dvander@353: free (prefix); dvander@353: return dir; dvander@353: } dvander@353: dvander@353: dvander@353: /*********************** dvander@353: * Utility functions dvander@353: ***********************/ dvander@353: dvander@353: /** Concatenate str1 and str2 to a newly allocated string. dvander@353: * dvander@353: * @param str1 A string. dvander@353: * @param str2 Another string. dvander@353: * @returns A newly-allocated string. This string should be freed when no longer needed. dvander@353: */ dvander@353: char * dvander@353: br_strcat (const char *str1, const char *str2) dvander@353: { dvander@353: char *result; dvander@353: size_t len1, len2; dvander@353: dvander@353: if (str1 == NULL) dvander@353: str1 = ""; dvander@353: if (str2 == NULL) dvander@353: str2 = ""; dvander@353: dvander@353: len1 = strlen (str1); dvander@353: len2 = strlen (str2); dvander@353: dvander@353: result = (char *) malloc (len1 + len2 + 1); dvander@353: memcpy (result, str1, len1); dvander@353: memcpy (result + len1, str2, len2); dvander@353: result[len1 + len2] = '\0'; dvander@353: dvander@353: return result; dvander@353: } dvander@353: dvander@353: dvander@353: char * dvander@353: br_build_path (const char *dir, const char *file) dvander@353: { dvander@353: char *dir2, *result; dvander@353: size_t len; dvander@353: int must_free = 0; dvander@353: dvander@353: len = strlen (dir); dvander@353: if (len > 0 && dir[len - 1] != '/') { dvander@353: dir2 = br_strcat (dir, "/"); dvander@353: must_free = 1; dvander@353: } else dvander@353: dir2 = (char *) dir; dvander@353: dvander@353: result = br_strcat (dir2, file); dvander@353: if (must_free) dvander@353: free (dir2); dvander@353: return result; dvander@353: } dvander@353: dvander@353: dvander@353: /* Emulates glibc's strndup() */ dvander@353: static char * dvander@353: br_strndup (const char *str, size_t size) dvander@353: { dvander@353: char *result = (char *) NULL; dvander@353: size_t len; dvander@353: dvander@353: if (str == (const char *) NULL) dvander@353: return (char *) NULL; dvander@353: dvander@353: len = strlen (str); dvander@353: if (len == 0) dvander@353: return strdup (""); dvander@353: if (size > len) dvander@353: size = len; dvander@353: dvander@353: result = (char *) malloc (len + 1); dvander@353: memcpy (result, str, size); dvander@353: result[size] = '\0'; dvander@353: return result; dvander@353: } dvander@353: dvander@353: dvander@353: /** Extracts the directory component of a path. dvander@353: * dvander@353: * Similar to g_dirname() or the dirname commandline application. dvander@353: * dvander@353: * Example: dvander@353: * \code dvander@353: * br_dirname ("/usr/local/foobar"); --> Returns: "/usr/local" dvander@353: * \endcode dvander@353: * dvander@353: * @param path A path. dvander@353: * @returns A directory name. This string should be freed when no longer needed. dvander@353: */ dvander@353: char * dvander@353: br_dirname (const char *path) dvander@353: { dvander@353: char *end, *result; dvander@353: dvander@353: if (path == (const char *) NULL) dvander@353: return (char *) NULL; dvander@353: dvander@353: end = strrchr (path, '/'); dvander@353: if (end == (const char *) NULL) dvander@353: return strdup ("."); dvander@353: dvander@353: while (end > path && *end == '/') dvander@353: end--; dvander@353: result = br_strndup (path, end - path + 1); dvander@353: if (result[0] == 0) { dvander@353: free (result); dvander@353: return strdup ("/"); dvander@353: } else dvander@353: return result; dvander@353: } dvander@353: dvander@353: dvander@353: #ifdef __cplusplus dvander@353: } dvander@353: #endif /* __cplusplus */ dvander@353: dvander@353: #endif /* __BINRELOC_C__ */