]>
Commit | Line | Data |
---|---|---|
0caf84a5 | 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 | ||
03f930e4 | 31 | extern char* strdup(const char*); |
0caf84a5 | 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__ */ |