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