Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 1 | /* -*- coding: utf-8 -*- |
2 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
||
3 | // See https://llvm.org/LICENSE.txt for license information. |
||
4 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
||
5 | */ |
||
6 | |||
7 | /** |
||
8 | * This file implements a shared library. This library can be pre-loaded by |
||
9 | * the dynamic linker of the Operating System (OS). It implements a few function |
||
10 | * related to process creation. By pre-load this library the executed process |
||
11 | * uses these functions instead of those from the standard library. |
||
12 | * |
||
13 | * The idea here is to inject a logic before call the real methods. The logic is |
||
14 | * to dump the call into a file. To call the real method this library is doing |
||
15 | * the job of the dynamic linker. |
||
16 | * |
||
17 | * The only input for the log writing is about the destination directory. |
||
18 | * This is passed as environment variable. |
||
19 | */ |
||
20 | |||
21 | // NOLINTNEXTLINE |
||
22 | #include "config.h" |
||
23 | |||
24 | #include <dlfcn.h> |
||
25 | #include <pthread.h> |
||
26 | #include <stdarg.h> |
||
27 | #include <stddef.h> |
||
28 | #include <stdio.h> |
||
29 | #include <stdlib.h> |
||
30 | #include <string.h> |
||
31 | #include <unistd.h> |
||
32 | |||
33 | #if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP |
||
34 | #include <spawn.h> |
||
35 | #endif |
||
36 | |||
37 | #if defined HAVE_NSGETENVIRON |
||
38 | #include <crt_externs.h> |
||
39 | #else |
||
40 | extern char **environ; |
||
41 | #endif |
||
42 | |||
43 | #define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR" |
||
44 | #ifdef APPLE |
||
45 | #define ENV_FLAT "DYLD_FORCE_FLAT_NAMESPACE" |
||
46 | #define ENV_PRELOAD "DYLD_INSERT_LIBRARIES" |
||
47 | #define ENV_SIZE 3 |
||
48 | #else |
||
49 | #define ENV_PRELOAD "LD_PRELOAD" |
||
50 | #define ENV_SIZE 2 |
||
51 | #endif |
||
52 | |||
53 | #define DLSYM(TYPE_, VAR_, SYMBOL_) \ |
||
54 | union { \ |
||
55 | void *from; \ |
||
56 | TYPE_ to; \ |
||
57 | } cast; \ |
||
58 | if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) { \ |
||
59 | perror("bear: dlsym"); \ |
||
60 | exit(EXIT_FAILURE); \ |
||
61 | } \ |
||
62 | TYPE_ const VAR_ = cast.to; |
||
63 | |||
64 | typedef char const *bear_env_t[ENV_SIZE]; |
||
65 | |||
66 | static int bear_capture_env_t(bear_env_t *env); |
||
67 | static int bear_reset_env_t(bear_env_t *env); |
||
68 | static void bear_release_env_t(bear_env_t *env); |
||
69 | static char const **bear_update_environment(char *const envp[], |
||
70 | bear_env_t *env); |
||
71 | static char const **bear_update_environ(char const **in, char const *key, |
||
72 | char const *value); |
||
73 | static char **bear_get_environment(); |
||
74 | static void bear_report_call(char const *fun, char const *const argv[]); |
||
75 | static char const **bear_strings_build(char const *arg, va_list *ap); |
||
76 | static char const **bear_strings_copy(char const **const in); |
||
77 | static char const **bear_strings_append(char const **in, char const *e); |
||
78 | static size_t bear_strings_length(char const *const *in); |
||
79 | static void bear_strings_release(char const **); |
||
80 | |||
81 | static bear_env_t env_names = {ENV_OUTPUT, ENV_PRELOAD |
||
82 | #ifdef ENV_FLAT |
||
83 | , |
||
84 | ENV_FLAT |
||
85 | #endif |
||
86 | }; |
||
87 | |||
88 | static bear_env_t initial_env = {0, 0 |
||
89 | #ifdef ENV_FLAT |
||
90 | , |
||
91 | |||
92 | #endif |
||
93 | }; |
||
94 | |||
95 | static int initialized = 0; |
||
96 | static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; |
||
97 | |||
98 | static void on_load(void) __attribute__((constructor)); |
||
99 | static void on_unload(void) __attribute__((destructor)); |
||
100 | |||
101 | #ifdef HAVE_EXECVE |
||
102 | static int call_execve(const char *path, char *const argv[], |
||
103 | char *const envp[]); |
||
104 | #endif |
||
105 | #ifdef HAVE_EXECVP |
||
106 | static int call_execvp(const char *file, char *const argv[]); |
||
107 | #endif |
||
108 | #ifdef HAVE_EXECVPE |
||
109 | static int call_execvpe(const char *file, char *const argv[], |
||
110 | char *const envp[]); |
||
111 | #endif |
||
112 | #ifdef HAVE_EXECVP2 |
||
113 | static int call_execvP(const char *file, const char *search_path, |
||
114 | char *const argv[]); |
||
115 | #endif |
||
116 | #ifdef HAVE_EXECT |
||
117 | static int call_exect(const char *path, char *const argv[], char *const envp[]); |
||
118 | #endif |
||
119 | #ifdef HAVE_POSIX_SPAWN |
||
120 | static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, |
||
121 | const posix_spawn_file_actions_t *file_actions, |
||
122 | const posix_spawnattr_t *restrict attrp, |
||
123 | char *const argv[restrict], |
||
124 | char *const envp[restrict]); |
||
125 | #endif |
||
126 | #ifdef HAVE_POSIX_SPAWNP |
||
127 | static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, |
||
128 | const posix_spawn_file_actions_t *file_actions, |
||
129 | const posix_spawnattr_t *restrict attrp, |
||
130 | char *const argv[restrict], |
||
131 | char *const envp[restrict]); |
||
132 | #endif |
||
133 | |||
134 | /* Initialization method to Captures the relevant environment variables. |
||
135 | */ |
||
136 | |||
137 | static void on_load(void) { |
||
138 | pthread_mutex_lock(&mutex); |
||
139 | if (!initialized) |
||
140 | initialized = bear_capture_env_t(&initial_env); |
||
141 | pthread_mutex_unlock(&mutex); |
||
142 | } |
||
143 | |||
144 | static void on_unload(void) { |
||
145 | pthread_mutex_lock(&mutex); |
||
146 | bear_release_env_t(&initial_env); |
||
147 | initialized = 0; |
||
148 | pthread_mutex_unlock(&mutex); |
||
149 | } |
||
150 | |||
151 | /* These are the methods we are try to hijack. |
||
152 | */ |
||
153 | |||
154 | #ifdef HAVE_EXECVE |
||
155 | int execve(const char *path, char *const argv[], char *const envp[]) { |
||
156 | bear_report_call(__func__, (char const *const *)argv); |
||
157 | return call_execve(path, argv, envp); |
||
158 | } |
||
159 | #endif |
||
160 | |||
161 | #ifdef HAVE_EXECV |
||
162 | #ifndef HAVE_EXECVE |
||
163 | #error can not implement execv without execve |
||
164 | #endif |
||
165 | int execv(const char *path, char *const argv[]) { |
||
166 | bear_report_call(__func__, (char const *const *)argv); |
||
167 | char *const *envp = bear_get_environment(); |
||
168 | return call_execve(path, argv, envp); |
||
169 | } |
||
170 | #endif |
||
171 | |||
172 | #ifdef HAVE_EXECVPE |
||
173 | int execvpe(const char *file, char *const argv[], char *const envp[]) { |
||
174 | bear_report_call(__func__, (char const *const *)argv); |
||
175 | return call_execvpe(file, argv, envp); |
||
176 | } |
||
177 | #endif |
||
178 | |||
179 | #ifdef HAVE_EXECVP |
||
180 | int execvp(const char *file, char *const argv[]) { |
||
181 | bear_report_call(__func__, (char const *const *)argv); |
||
182 | return call_execvp(file, argv); |
||
183 | } |
||
184 | #endif |
||
185 | |||
186 | #ifdef HAVE_EXECVP2 |
||
187 | int execvP(const char *file, const char *search_path, char *const argv[]) { |
||
188 | bear_report_call(__func__, (char const *const *)argv); |
||
189 | return call_execvP(file, search_path, argv); |
||
190 | } |
||
191 | #endif |
||
192 | |||
193 | #ifdef HAVE_EXECT |
||
194 | int exect(const char *path, char *const argv[], char *const envp[]) { |
||
195 | bear_report_call(__func__, (char const *const *)argv); |
||
196 | return call_exect(path, argv, envp); |
||
197 | } |
||
198 | #endif |
||
199 | |||
200 | #ifdef HAVE_EXECL |
||
201 | #ifndef HAVE_EXECVE |
||
202 | #error can not implement execl without execve |
||
203 | #endif |
||
204 | int execl(const char *path, const char *arg, ...) { |
||
205 | va_list args; |
||
206 | va_start(args, arg); |
||
207 | char const **argv = bear_strings_build(arg, &args); |
||
208 | va_end(args); |
||
209 | |||
210 | bear_report_call(__func__, (char const *const *)argv); |
||
211 | char *const *envp = bear_get_environment(); |
||
212 | int const result = call_execve(path, (char *const *)argv, envp); |
||
213 | |||
214 | bear_strings_release(argv); |
||
215 | return result; |
||
216 | } |
||
217 | #endif |
||
218 | |||
219 | #ifdef HAVE_EXECLP |
||
220 | #ifndef HAVE_EXECVP |
||
221 | #error can not implement execlp without execvp |
||
222 | #endif |
||
223 | int execlp(const char *file, const char *arg, ...) { |
||
224 | va_list args; |
||
225 | va_start(args, arg); |
||
226 | char const **argv = bear_strings_build(arg, &args); |
||
227 | va_end(args); |
||
228 | |||
229 | bear_report_call(__func__, (char const *const *)argv); |
||
230 | int const result = call_execvp(file, (char *const *)argv); |
||
231 | |||
232 | bear_strings_release(argv); |
||
233 | return result; |
||
234 | } |
||
235 | #endif |
||
236 | |||
237 | #ifdef HAVE_EXECLE |
||
238 | #ifndef HAVE_EXECVE |
||
239 | #error can not implement execle without execve |
||
240 | #endif |
||
241 | // int execle(const char *path, const char *arg, ..., char * const envp[]); |
||
242 | int execle(const char *path, const char *arg, ...) { |
||
243 | va_list args; |
||
244 | va_start(args, arg); |
||
245 | char const **argv = bear_strings_build(arg, &args); |
||
246 | char const **envp = va_arg(args, char const **); |
||
247 | va_end(args); |
||
248 | |||
249 | bear_report_call(__func__, (char const *const *)argv); |
||
250 | int const result = |
||
251 | call_execve(path, (char *const *)argv, (char *const *)envp); |
||
252 | |||
253 | bear_strings_release(argv); |
||
254 | return result; |
||
255 | } |
||
256 | #endif |
||
257 | |||
258 | #ifdef HAVE_POSIX_SPAWN |
||
259 | int posix_spawn(pid_t *restrict pid, const char *restrict path, |
||
260 | const posix_spawn_file_actions_t *file_actions, |
||
261 | const posix_spawnattr_t *restrict attrp, |
||
262 | char *const argv[restrict], char *const envp[restrict]) { |
||
263 | bear_report_call(__func__, (char const *const *)argv); |
||
264 | return call_posix_spawn(pid, path, file_actions, attrp, argv, envp); |
||
265 | } |
||
266 | #endif |
||
267 | |||
268 | #ifdef HAVE_POSIX_SPAWNP |
||
269 | int posix_spawnp(pid_t *restrict pid, const char *restrict file, |
||
270 | const posix_spawn_file_actions_t *file_actions, |
||
271 | const posix_spawnattr_t *restrict attrp, |
||
272 | char *const argv[restrict], char *const envp[restrict]) { |
||
273 | bear_report_call(__func__, (char const *const *)argv); |
||
274 | return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp); |
||
275 | } |
||
276 | #endif |
||
277 | |||
278 | /* These are the methods which forward the call to the standard implementation. |
||
279 | */ |
||
280 | |||
281 | #ifdef HAVE_EXECVE |
||
282 | static int call_execve(const char *path, char *const argv[], |
||
283 | char *const envp[]) { |
||
284 | typedef int (*func)(const char *, char *const *, char *const *); |
||
285 | |||
286 | DLSYM(func, fp, "execve"); |
||
287 | |||
288 | char const **const menvp = bear_update_environment(envp, &initial_env); |
||
289 | int const result = (*fp)(path, argv, (char *const *)menvp); |
||
290 | bear_strings_release(menvp); |
||
291 | return result; |
||
292 | } |
||
293 | #endif |
||
294 | |||
295 | #ifdef HAVE_EXECVPE |
||
296 | static int call_execvpe(const char *file, char *const argv[], |
||
297 | char *const envp[]) { |
||
298 | typedef int (*func)(const char *, char *const *, char *const *); |
||
299 | |||
300 | DLSYM(func, fp, "execvpe"); |
||
301 | |||
302 | char const **const menvp = bear_update_environment(envp, &initial_env); |
||
303 | int const result = (*fp)(file, argv, (char *const *)menvp); |
||
304 | bear_strings_release(menvp); |
||
305 | return result; |
||
306 | } |
||
307 | #endif |
||
308 | |||
309 | #ifdef HAVE_EXECVP |
||
310 | static int call_execvp(const char *file, char *const argv[]) { |
||
311 | typedef int (*func)(const char *file, char *const argv[]); |
||
312 | |||
313 | DLSYM(func, fp, "execvp"); |
||
314 | |||
315 | bear_env_t current_env; |
||
316 | bear_capture_env_t(¤t_env); |
||
317 | bear_reset_env_t(&initial_env); |
||
318 | int const result = (*fp)(file, argv); |
||
319 | bear_reset_env_t(¤t_env); |
||
320 | bear_release_env_t(¤t_env); |
||
321 | |||
322 | return result; |
||
323 | } |
||
324 | #endif |
||
325 | |||
326 | #ifdef HAVE_EXECVP2 |
||
327 | static int call_execvP(const char *file, const char *search_path, |
||
328 | char *const argv[]) { |
||
329 | typedef int (*func)(const char *, const char *, char *const *); |
||
330 | |||
331 | DLSYM(func, fp, "execvP"); |
||
332 | |||
333 | bear_env_t current_env; |
||
334 | bear_capture_env_t(¤t_env); |
||
335 | bear_reset_env_t(&initial_env); |
||
336 | int const result = (*fp)(file, search_path, argv); |
||
337 | bear_reset_env_t(¤t_env); |
||
338 | bear_release_env_t(¤t_env); |
||
339 | |||
340 | return result; |
||
341 | } |
||
342 | #endif |
||
343 | |||
344 | #ifdef HAVE_EXECT |
||
345 | static int call_exect(const char *path, char *const argv[], |
||
346 | char *const envp[]) { |
||
347 | typedef int (*func)(const char *, char *const *, char *const *); |
||
348 | |||
349 | DLSYM(func, fp, "exect"); |
||
350 | |||
351 | char const **const menvp = bear_update_environment(envp, &initial_env); |
||
352 | int const result = (*fp)(path, argv, (char *const *)menvp); |
||
353 | bear_strings_release(menvp); |
||
354 | return result; |
||
355 | } |
||
356 | #endif |
||
357 | |||
358 | #ifdef HAVE_POSIX_SPAWN |
||
359 | static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, |
||
360 | const posix_spawn_file_actions_t *file_actions, |
||
361 | const posix_spawnattr_t *restrict attrp, |
||
362 | char *const argv[restrict], |
||
363 | char *const envp[restrict]) { |
||
364 | typedef int (*func)(pid_t *restrict, const char *restrict, |
||
365 | const posix_spawn_file_actions_t *, |
||
366 | const posix_spawnattr_t *restrict, char *const *restrict, |
||
367 | char *const *restrict); |
||
368 | |||
369 | DLSYM(func, fp, "posix_spawn"); |
||
370 | |||
371 | char const **const menvp = bear_update_environment(envp, &initial_env); |
||
372 | int const result = |
||
373 | (*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp); |
||
374 | bear_strings_release(menvp); |
||
375 | return result; |
||
376 | } |
||
377 | #endif |
||
378 | |||
379 | #ifdef HAVE_POSIX_SPAWNP |
||
380 | static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, |
||
381 | const posix_spawn_file_actions_t *file_actions, |
||
382 | const posix_spawnattr_t *restrict attrp, |
||
383 | char *const argv[restrict], |
||
384 | char *const envp[restrict]) { |
||
385 | typedef int (*func)(pid_t *restrict, const char *restrict, |
||
386 | const posix_spawn_file_actions_t *, |
||
387 | const posix_spawnattr_t *restrict, char *const *restrict, |
||
388 | char *const *restrict); |
||
389 | |||
390 | DLSYM(func, fp, "posix_spawnp"); |
||
391 | |||
392 | char const **const menvp = bear_update_environment(envp, &initial_env); |
||
393 | int const result = |
||
394 | (*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp); |
||
395 | bear_strings_release(menvp); |
||
396 | return result; |
||
397 | } |
||
398 | #endif |
||
399 | |||
400 | /* this method is to write log about the process creation. */ |
||
401 | |||
402 | static void bear_report_call(char const *fun, char const *const argv[]) { |
||
403 | static int const GS = 0x1d; |
||
404 | static int const RS = 0x1e; |
||
405 | static int const US = 0x1f; |
||
406 | |||
407 | if (!initialized) |
||
408 | return; |
||
409 | |||
410 | pthread_mutex_lock(&mutex); |
||
411 | const char *cwd = getcwd(NULL, 0); |
||
412 | if (0 == cwd) { |
||
413 | perror("bear: getcwd"); |
||
414 | pthread_mutex_unlock(&mutex); |
||
415 | exit(EXIT_FAILURE); |
||
416 | } |
||
417 | char const *const out_dir = initial_env[0]; |
||
418 | size_t const path_max_length = strlen(out_dir) + 32; |
||
419 | char filename[path_max_length]; |
||
420 | if (-1 == |
||
421 | snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) { |
||
422 | perror("bear: snprintf"); |
||
423 | pthread_mutex_unlock(&mutex); |
||
424 | exit(EXIT_FAILURE); |
||
425 | } |
||
426 | FILE *fd = fopen(filename, "a+"); |
||
427 | if (0 == fd) { |
||
428 | perror("bear: fopen"); |
||
429 | pthread_mutex_unlock(&mutex); |
||
430 | exit(EXIT_FAILURE); |
||
431 | } |
||
432 | fprintf(fd, "%d%c", getpid(), RS); |
||
433 | fprintf(fd, "%d%c", getppid(), RS); |
||
434 | fprintf(fd, "%s%c", fun, RS); |
||
435 | fprintf(fd, "%s%c", cwd, RS); |
||
436 | size_t const argc = bear_strings_length(argv); |
||
437 | for (size_t it = 0; it < argc; ++it) { |
||
438 | fprintf(fd, "%s%c", argv[it], US); |
||
439 | } |
||
440 | fprintf(fd, "%c", GS); |
||
441 | if (fclose(fd)) { |
||
442 | perror("bear: fclose"); |
||
443 | pthread_mutex_unlock(&mutex); |
||
444 | exit(EXIT_FAILURE); |
||
445 | } |
||
446 | free((void *)cwd); |
||
447 | pthread_mutex_unlock(&mutex); |
||
448 | } |
||
449 | |||
450 | /* update environment assure that children processes will copy the desired |
||
451 | * behaviour */ |
||
452 | |||
453 | static int bear_capture_env_t(bear_env_t *env) { |
||
454 | int status = 1; |
||
455 | for (size_t it = 0; it < ENV_SIZE; ++it) { |
||
456 | char const *const env_value = getenv(env_names[it]); |
||
457 | char const *const env_copy = (env_value) ? strdup(env_value) : env_value; |
||
458 | (*env)[it] = env_copy; |
||
459 | status &= (env_copy) ? 1 : 0; |
||
460 | } |
||
461 | return status; |
||
462 | } |
||
463 | |||
464 | static int bear_reset_env_t(bear_env_t *env) { |
||
465 | int status = 1; |
||
466 | for (size_t it = 0; it < ENV_SIZE; ++it) { |
||
467 | if ((*env)[it]) { |
||
468 | setenv(env_names[it], (*env)[it], 1); |
||
469 | } else { |
||
470 | unsetenv(env_names[it]); |
||
471 | } |
||
472 | } |
||
473 | return status; |
||
474 | } |
||
475 | |||
476 | static void bear_release_env_t(bear_env_t *env) { |
||
477 | for (size_t it = 0; it < ENV_SIZE; ++it) { |
||
478 | free((void *)(*env)[it]); |
||
479 | (*env)[it] = 0; |
||
480 | } |
||
481 | } |
||
482 | |||
483 | static char const **bear_update_environment(char *const envp[], |
||
484 | bear_env_t *env) { |
||
485 | char const **result = bear_strings_copy((char const **)envp); |
||
486 | for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it) |
||
487 | result = bear_update_environ(result, env_names[it], (*env)[it]); |
||
488 | return result; |
||
489 | } |
||
490 | |||
491 | static char const **bear_update_environ(char const *envs[], char const *key, |
||
492 | char const *const value) { |
||
493 | // find the key if it's there |
||
494 | size_t const key_length = strlen(key); |
||
495 | char const **it = envs; |
||
496 | for (; (it) && (*it); ++it) { |
||
497 | if ((0 == strncmp(*it, key, key_length)) && (strlen(*it) > key_length) && |
||
498 | ('=' == (*it)[key_length])) |
||
499 | break; |
||
500 | } |
||
501 | // allocate a environment entry |
||
502 | size_t const value_length = strlen(value); |
||
503 | size_t const env_length = key_length + value_length + 2; |
||
504 | char *env = malloc(env_length); |
||
505 | if (0 == env) { |
||
506 | perror("bear: malloc [in env_update]"); |
||
507 | exit(EXIT_FAILURE); |
||
508 | } |
||
509 | if (-1 == snprintf(env, env_length, "%s=%s", key, value)) { |
||
510 | perror("bear: snprintf"); |
||
511 | exit(EXIT_FAILURE); |
||
512 | } |
||
513 | // replace or append the environment entry |
||
514 | if (it && *it) { |
||
515 | free((void *)*it); |
||
516 | *it = env; |
||
517 | return envs; |
||
518 | } |
||
519 | return bear_strings_append(envs, env); |
||
520 | } |
||
521 | |||
522 | static char **bear_get_environment() { |
||
523 | #if defined HAVE_NSGETENVIRON |
||
524 | return *_NSGetEnviron(); |
||
525 | #else |
||
526 | return environ; |
||
527 | #endif |
||
528 | } |
||
529 | |||
530 | /* util methods to deal with string arrays. environment and process arguments |
||
531 | * are both represented as string arrays. */ |
||
532 | |||
533 | static char const **bear_strings_build(char const *const arg, va_list *args) { |
||
534 | char const **result = 0; |
||
535 | size_t size = 0; |
||
536 | for (char const *it = arg; it; it = va_arg(*args, char const *)) { |
||
537 | result = realloc(result, (size + 1) * sizeof(char const *)); |
||
538 | if (0 == result) { |
||
539 | perror("bear: realloc"); |
||
540 | exit(EXIT_FAILURE); |
||
541 | } |
||
542 | char const *copy = strdup(it); |
||
543 | if (0 == copy) { |
||
544 | perror("bear: strdup"); |
||
545 | exit(EXIT_FAILURE); |
||
546 | } |
||
547 | result[size++] = copy; |
||
548 | } |
||
549 | result = realloc(result, (size + 1) * sizeof(char const *)); |
||
550 | if (0 == result) { |
||
551 | perror("bear: realloc"); |
||
552 | exit(EXIT_FAILURE); |
||
553 | } |
||
554 | result[size++] = 0; |
||
555 | |||
556 | return result; |
||
557 | } |
||
558 | |||
559 | static char const **bear_strings_copy(char const **const in) { |
||
560 | size_t const size = bear_strings_length(in); |
||
561 | |||
562 | char const **const result = malloc((size + 1) * sizeof(char const *)); |
||
563 | if (0 == result) { |
||
564 | perror("bear: malloc"); |
||
565 | exit(EXIT_FAILURE); |
||
566 | } |
||
567 | |||
568 | char const **out_it = result; |
||
569 | for (char const *const *in_it = in; (in_it) && (*in_it); ++in_it, ++out_it) { |
||
570 | *out_it = strdup(*in_it); |
||
571 | if (0 == *out_it) { |
||
572 | perror("bear: strdup"); |
||
573 | exit(EXIT_FAILURE); |
||
574 | } |
||
575 | } |
||
576 | *out_it = 0; |
||
577 | return result; |
||
578 | } |
||
579 | |||
580 | static char const **bear_strings_append(char const **const in, |
||
581 | char const *const e) { |
||
582 | size_t size = bear_strings_length(in); |
||
583 | char const **result = realloc(in, (size + 2) * sizeof(char const *)); |
||
584 | if (0 == result) { |
||
585 | perror("bear: realloc"); |
||
586 | exit(EXIT_FAILURE); |
||
587 | } |
||
588 | result[size++] = e; |
||
589 | result[size++] = 0; |
||
590 | return result; |
||
591 | } |
||
592 | |||
593 | static size_t bear_strings_length(char const *const *const in) { |
||
594 | size_t result = 0; |
||
595 | for (char const *const *it = in; (it) && (*it); ++it) |
||
596 | ++result; |
||
597 | return result; |
||
598 | } |
||
599 | |||
600 | static void bear_strings_release(char const **in) { |
||
601 | for (char const *const *it = in; (it) && (*it); ++it) { |
||
602 | free((void *)*it); |
||
603 | } |
||
604 | free((void *)in); |
||
605 | } |