• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aremmell / libsir / 428

04 Sep 2023 10:15PM UTC coverage: 94.486% (-0.4%) from 94.865%
428

Pull #257

gitlab-ci

aremmell
don't need check for OrangeC-test is disabled
Pull Request #257: WIP

238 of 238 new or added lines in 12 files covered. (100.0%)

3016 of 3192 relevant lines covered (94.49%)

629655.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.8
/src/sirfilesystem.c
1
/*
2
 * sirfilesystem.c
3
 *
4
 * Author:    Ryan M. Lederman <lederman@gmail.com>
5
 * Copyright: Copyright (c) 2018-2023
6
 * Version:   2.2.3
7
 * License:   The MIT License (MIT)
8
 *
9
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
10
 * this software and associated documentation files (the "Software"), to deal in
11
 * the Software without restriction, including without limitation the rights to
12
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
13
 * the Software, and to permit persons to whom the Software is furnished to do so,
14
 * subject to the following conditions:
15
 *
16
 * The above copyright notice and this permission notice shall be included in all
17
 * copies or substantial portions of the Software.
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
21
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
22
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
23
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
 */
26
#include "sir/filesystem.h"
27
#include "sir/internal.h"
28

29
#if defined(__WIN__)
30
# if defined(__EMBARCADEROC__) && defined(_WIN64)
31
#  pragma comment(lib, "shlwapi.a")
32
# else
33
#  pragma comment(lib, "shlwapi.lib")
34
# endif
35
#endif
36

37
bool _sir_pathgetstat(const char* restrict path, struct stat* restrict st, sir_rel_to rel_to) {
289✔
38
    if (!_sir_validstr(path) || !_sir_validptr(st))
289✔
39
        return false;
×
40

41
    memset(st, 0, sizeof(struct stat));
256✔
42

43
    int stat_ret          = -1;
256✔
44
    bool relative         = false;
289✔
45
    const char* base_path = NULL;
289✔
46

47
    if (!_sir_getrelbasepath(path, &relative, &base_path, rel_to))
289✔
48
        return false;
28✔
49

50
    if (relative) {
261✔
51
#if !defined(__WIN__)
52
# if defined(__MACOS__) || defined(_AIX)
53
#  if !defined(O_SEARCH)
54
        int open_flags = O_DIRECTORY;
55
#  else
56
        int open_flags = O_SEARCH;
57
#  endif
58
# elif defined(__linux__) || defined(__HURD__)
59
#  if !defined(__SUNPRO_C) && !defined(__SUNPRO_CC) && defined(O_PATH)
60
        int open_flags = O_PATH | O_DIRECTORY;
171✔
61
#  else
62
        int open_flags = O_DIRECTORY;
63
#  endif
64
# elif defined(__FreeBSD__) || defined(__CYGWIN__) || defined(__serenity__)
65
        int open_flags = O_EXEC | O_DIRECTORY;
66
# elif defined(__SOLARIS__) || defined(__DragonFly__) || \
67
       defined(__NetBSD__) || defined(__HAIKU__) || defined(__OpenBSD__)
68
        int open_flags = O_DIRECTORY;
69
# else
70
#  error "unknown open_flags for your platform; please contact the author."
71
# endif
72

73
        int fd = open(base_path, open_flags);
195✔
74
        if (-1 == fd) {
195✔
75
            _sir_safefree(&base_path);
×
76
            return _sir_handleerr(errno);
×
77
        }
78

79
        stat_ret = fstatat(fd, path, st, AT_SYMLINK_NOFOLLOW);
195✔
80
        _sir_safeclose(&fd);
195✔
81
        _sir_safefree(&base_path);
195✔
82
    } else {
83
        stat_ret = stat(path, st);
66✔
84
    }
85
#else /* __WIN__ */
86
        char abs_path[SIR_MAXPATH] = {0};
87
        (void)snprintf(abs_path, SIR_MAXPATH, "%s\\%s", base_path, path);
88

89
# if defined(__EMBARCADEROC__)
90
        /* Embarcadero does not like paths that end in slashes, nor does it appreciate
91
         * paths like './' and '../'; this is a hack until those defects are resolved. */
92
        char resolved_path[SIR_MAXPATH] = {0};
93

94
        if (!GetFullPathNameA(abs_path, SIR_MAXPATH, resolved_path, NULL)) {
95
            _sir_safefree(&base_path);
96
            return _sir_handlewin32err(GetLastError());
97
        }
98

99
        PathRemoveBackslashA(resolved_path);
100
        (void)_sir_strlcpy(abs_path, resolved_path, SIR_MAXPATH);
101
# endif
102

103
        stat_ret = stat(abs_path, st);
104
        _sir_safefree(&base_path);
105
    } else {
106
        stat_ret = stat(path, st);
107
    }
108
#endif
109
    if (-1 == stat_ret && (ENOENT == errno || ENOTDIR == errno))
261✔
110
        st->st_size = SIR_STAT_NONEXISTENT;
76✔
111

112
    return (-1 != stat_ret || ENOENT == errno || ENOTDIR == errno) ? true : _sir_handleerr(errno);
237✔
113
}
114

115
bool _sir_pathexists(const char* path, bool* exists, sir_rel_to rel_to) {
289✔
116
    if (!_sir_validstr(path) || !_sir_validptr(exists))
289✔
117
        return false;
×
118

119
    *exists = false;
289✔
120

121
    struct stat st = {0};
289✔
122
    bool stat_ret  = _sir_pathgetstat(path, &st, rel_to);
289✔
123
    if (!stat_ret)
289✔
124
        return false;
28✔
125

126
    *exists = (st.st_size != SIR_STAT_NONEXISTENT);
261✔
127
    return true;
261✔
128
}
129

130
bool _sir_openfile(FILE* restrict* restrict f, const char* restrict path,
35,482✔
131
    const char* restrict mode, sir_rel_to rel_to) {
132
    if (!_sir_validptrptr(f) || !_sir_validstr(path) || !_sir_validstr(mode))
35,482✔
133
        return false;
×
134

135
    bool relative         = false;
35,482✔
136
    const char* base_path = NULL;
35,482✔
137

138
    if (!_sir_getrelbasepath(path, &relative, &base_path, rel_to))
35,482✔
139
        return false;
4✔
140

141
    if (relative) {
35,478✔
142
        char abs_path[SIR_MAXPATH] = {0};
35,458✔
143
        (void)snprintf(abs_path, SIR_MAXPATH, "%s/%s", base_path, path);
35,458✔
144

145
        int ret = _sir_fopen(f, abs_path, mode);
35,458✔
146
        _sir_safefree(&base_path);
35,458✔
147
        return 0 == ret;
35,458✔
148
    }
149

150
    return 0 == _sir_fopen(f, path, mode);
20✔
151
}
152

153
#if defined(_AIX)
154
static char cur_cwd[SIR_MAXPATH];
155
#endif
156
char* _sir_getcwd(void) {
35,531✔
157
#if !defined(__WIN__)
158
# if defined(__linux__) && (defined(__GLIBC__) && defined(_GNU_SOURCE))
159
    char* cur = get_current_dir_name();
35,531✔
160
    if (!_sir_validptrnofail(cur))
35,531✔
161
        (void)_sir_handleerr(errno);
×
162
    return cur;
35,531✔
163
# elif defined(_AIX)
164
    if (getcwd(cur_cwd, sizeof(cur_cwd)) == 0) {
165
        (void)_sir_handleerr(errno);
166
        return NULL;
167
    } else {
168
        return strndup(cur_cwd, SIR_MAXPATH);
169
    }
170
# else
171
    char* cur = getcwd(NULL, 0);
172
    if (NULL == cur)
173
        (void)_sir_handleerr(errno);
174
    return cur;
175
# endif
176
#else /* __WIN__ */
177
    DWORD size = GetCurrentDirectoryA(0UL, NULL);
178
    if (0UL == size) {
179
        _sir_handlewin32err(GetLastError());
180
        return NULL;
181
    }
182

183
    char* cur = calloc(size, sizeof(char));
184
    if (!cur) {
185
        _sir_handleerr(errno);
186
        return NULL;
187
    }
188

189
    if (!GetCurrentDirectoryA(size, cur)) {
190
        _sir_handlewin32err(GetLastError());
191
        _sir_safefree(cur);
192
        return NULL;
193
    }
194

195
    return cur;
196
#endif
197
}
198

199
char* _sir_getappfilename(void) {
239✔
200
#if defined(__linux__) || defined(__NetBSD__) || defined(__SOLARIS__) || \
201
    defined(__DragonFly__) || defined(__CYGWIN__) || defined(__serenity__) || \
202
    defined(__HURD__)
203
# define __READLINK_OS__
204
# if defined(__linux__) || defined(__CYGWIN__) || \
205
     defined(__serenity__) || defined(__HURD__)
206
#  define PROC_SELF "/proc/self/exe"
207
# elif defined(__NetBSD__)
208
#  define PROC_SELF "/proc/curproc/exe"
209
# elif defined(__DragonFly__)
210
#  define PROC_SELF "/proc/curproc/file"
211
# elif defined(__SOLARIS__)
212
#  define PROC_SELF "/proc/self/path/a.out"
213
# endif
214
    struct stat st;
215
    if (-1 == lstat(PROC_SELF, &st)) {
239✔
216
        (void)_sir_handleerr(errno);
9✔
217
        return NULL;
9✔
218
    }
219

220
    size_t size = (st.st_size > 0) ? st.st_size + 2 : SIR_MAXPATH;
230✔
221
#else
222
    size_t size = SIR_MAXPATH;
223
#endif
224

225
    char* buffer  = NULL;
230✔
226
    bool resolved = false;
197✔
227

228
    do {
×
229
        _sir_safefree(&buffer);
230✔
230
        buffer = (char*)calloc(size, sizeof(char));
230✔
231
        if (NULL == buffer) {
230✔
232
            resolved = _sir_handleerr(errno);
12✔
233
            break;
12✔
234
        }
235

236
#if !defined(__WIN__)
237
# if defined(__READLINK_OS__)
238
        ssize_t read = _sir_readlink(PROC_SELF, buffer, size - 1);
218✔
239
        if (-1L != read && read < (ssize_t)size - 1) {
218✔
240
            resolved = true;
176✔
241
            break;
176✔
242
        } else if (-1L == read) {
9✔
243
            resolved = _sir_handleerr(errno);
9✔
244
            break;
9✔
245
        } else if (read == (ssize_t)size - 1L) {
×
246
            /* it is possible that truncation occurred. as a security
247
             * precaution, fail; someone may have tampered with the link. */
248
            _sir_selflog("warning: readlink reported truncation; not using result!");
×
249
            resolved = false;
×
250
            break;
×
251
        }
252
# elif defined(_AIX)
253
        if (size <= SIR_MAXPATH) {
254
            size = size + SIR_MAXPATH + 1L;
255
            continue;
256
        }
257
        int ret = _sir_aixself(buffer, &size);
258
        if (ret == 0) {
259
            resolved = true;
260
            break;
261
        } else {
262
            resolved = _sir_handleerr(errno);
263
            break;
264
        }
265
# elif defined(__OpenBSD__)
266
        size_t length;
267
        int dirname_length;
268
        length = _sir_openbsdself(NULL, 0, &dirname_length);
269
        if (length < 1) {
270
            resolved = _sir_handleerr(errno);
271
            break;
272
        }
273
        if (length > size) {
274
            size = length + 1;
275
            continue;
276
        }
277
        (void)_sir_openbsdself(buffer, length, &dirname_length);
278
        if (!buffer) {
279
            resolved = false;
280
            break;
281
        }
282
        resolved = true;
283
        break;
284
# elif defined(__BSD__)
285
        int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
286
        int ret = sysctl(mib, 4, buffer, &size, NULL, 0);
287
        if (0 == ret) {
288
            resolved = true;
289
            break;
290
        } else {
291
            if (ENOMEM == errno && 0 == sysctl(mib, 4, NULL, &size, NULL, 0))
292
                continue; /* grow buffer. */
293

294
            resolved = _sir_handleerr(errno);
295
            break;
296
        }
297
# elif defined(__HAIKU__)
298
        status_t ret =
299
            find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, buffer, SIR_MAXPATH);
300
        if (B_OK == ret) {
301
            resolved = true;
302
            break;
303
        } else if (B_BUFFER_OVERFLOW == ret) {
304
            /* grow buffer. */
305
            continue;
306
        } else {
307
            resolved = _sir_handleerr(errno);
308
            break;
309
        }
310
# elif defined(__MACOS__)
311
        int ret = _NSGetExecutablePath(buffer, (uint32_t*)&size);
312
        if (0 == ret) {
313
            resolved = true;
314
            break;
315
        } else if (-1 == ret) {
316
            /* grow buffer. */
317
            continue;
318
        } else {
319
            resolved = _sir_handleerr(errno);
320
            break;
321
        }
322
# else
323
#  error "no implementation for your platform; please contact the author."
324
# endif
325
#else /* __WIN__ */
326
        DWORD ret = GetModuleFileNameA(NULL, buffer, (DWORD)size);
327
        if (0UL != ret && ret < (DWORD)size) {
328
            resolved = true;
329
            break;
330
        } else if (0UL == ret) {
331
            resolved = _sir_handlewin32err(GetLastError());
332
            break;
333
        } else if (ret == (DWORD)size || ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
334
            /* Windows has no concept of letting you know how much larger
335
             * your buffer needed to be; it just truncates the string and
336
             * returns size. So, we'll guess. */
337
            size += SIR_PATH_BUFFER_GROW_BY;
338
            continue;
339
        }
340
#endif
341

342
    } while (true);
343

344
    if (!resolved)
230✔
345
        _sir_safefree(&buffer);
21✔
346

347
    return buffer;
230✔
348
}
349

350
char* _sir_getappbasename(void) {
22✔
351
    char* filename = _sir_getappfilename();
22✔
352
    if (!_sir_validstr(filename)) {
22✔
353
        _sir_safefree(&filename);
3✔
354
        return NULL;
3✔
355
    }
356

357
    char* retval = _sir_getbasename(filename);
19✔
358
    char* bname  = strndup(retval, strnlen(retval, SIR_MAXPATH));
19✔
359

360
    _sir_safefree(&filename);
19✔
361
    return bname;
19✔
362
}
363

364
char* _sir_getappdir(void) {
195✔
365
    char* filename = _sir_getappfilename();
195✔
366
    if (!_sir_validstr(filename)) {
195✔
367
        _sir_safefree(&filename);
24✔
368
        return NULL;
24✔
369
    }
370

371
    char* retval  = _sir_getdirname(filename);
171✔
372
    char* dirname = strndup(retval, strnlen(retval, SIR_MAXPATH));
171✔
373

374
    _sir_safefree(&filename);
171✔
375
    return dirname;
171✔
376
}
377

378
char* _sir_getbasename(char* restrict path) {
142✔
379
    if (!_sir_validstr(path))
142✔
380
        return ".";
18✔
381

382
#if !defined(__WIN__)
383
    return basename(path);
121✔
384
#else /* __WIN__ */
385
    return PathFindFileNameA(path);
386
#endif
387
}
388

389
char* _sir_getdirname(char* restrict path) {
315✔
390
    if (!_sir_validstr(path))
315✔
391
        return ".";
18✔
392

393
#if !defined(__WIN__)
394
    return dirname(path);
294✔
395
#else /* __WIN__ */
396
    (void)PathRemoveFileSpecA((LPSTR)path);
397
    return path;
398
#endif
399
}
400

401
bool _sir_ispathrelative(const char* restrict path, bool* restrict relative) {
35,947✔
402
    bool valid = _sir_validstr(path) && _sir_validptr(relative);
35,947✔
403

404
    if (valid) {
29,662✔
405
#if !defined(__WIN__)
406
        if (path[0] == '/' || (path[0] == '~' && path[1] == '/'))
35,947✔
407
            *relative = false;
174✔
408
        else
409
            *relative = true;
35,773✔
410
#else /* __WIN__ */
411
        *relative = (TRUE == PathIsRelativeA(path));
412
#endif
413
    }
414

415
    return valid;
35,947✔
416
}
417

418
bool _sir_getrelbasepath(const char* restrict path, bool* restrict relative,
35,771✔
419
    const char* restrict* restrict base_path, sir_rel_to rel_to) {
420
    if (!_sir_validstr(path) || !_sir_validptr(relative) ||
42,032✔
421
        !_sir_validptrptr(base_path) || !_sir_ispathrelative(path, relative))
42,032✔
422
        return false;
×
423

424
    if (*relative) {
35,771✔
425
        switch (rel_to) {
35,685✔
426
            case SIR_PATH_REL_TO_APP: return NULL != (*base_path = _sir_getappdir());
176✔
427
            case SIR_PATH_REL_TO_CWD: return NULL != (*base_path = _sir_getcwd());
35,509✔
428
            default: return _sir_seterror(_SIR_E_INVALID);
×
429
        }
430
    }
431

432
    return true;
74✔
433
}
434

435
#if defined(_AIX)
436
# define SIR_MAXSLPATH (SIR_MAXPATH + 16)
437
int _sir_aixself(char* buffer, size_t* size) {
438
    ssize_t res;
439
    char cwd[SIR_MAXPATH], cwdl[SIR_MAXPATH];
440
    char symlink[SIR_MAXSLPATH], temp_buffer[SIR_MAXPATH];
441
    char pp[64];
442
    struct psinfo ps;
443
    int fd;
444
    char** argv;
445
    char* tokptr;
446

447
    if ((buffer == NULL) || (size == NULL))
448
        return -1;
449

450
    (void)snprintf(pp, sizeof(pp), "/proc/%llu/psinfo", (unsigned long long)_sir_getpid());
451

452
    fd = open(pp, O_RDONLY);
453
    if (fd < 0)
454
        return -1;
455

456
    res = read(fd, &ps, sizeof(ps));
457
    close(fd);
458
    if (res < 0)
459
        return -1;
460

461
    if (ps.pr_argv == 0)
462
        return -1;
463

464
    argv = (char**)*((char***)(intptr_t)ps.pr_argv);
465

466
    if ((argv == NULL) || (argv[0] == NULL))
467
        return -1;
468

469
    if (argv[0][0] == '/') {
470
        (void)snprintf(symlink, SIR_MAXPATH, "%s", argv[0]);
471

472
        res = _sir_readlink(symlink, temp_buffer, SIR_MAXPATH);
473
        if (res < 0)
474
            _sir_strncpy(buffer, SIR_MAXPATH, symlink, SIR_MAXPATH);
475
        else
476
            (void)snprintf(buffer, *size - 1, "%s/%s", (char*)dirname(symlink),
477
                temp_buffer);
478

479
        *size = strnlen(buffer, SIR_MAXPATH);
480
        return 0;
481
    } else if (argv[0][0] == '.') {
482
        char* relative = strchr(argv[0], '/');
483
        if (relative == NULL)
484
            return -1;
485

486
        (void)snprintf(cwd, SIR_MAXPATH, "/proc/%llu/cwd", (unsigned long long)_sir_getpid());
487
        res = _sir_readlink(cwd, cwdl, sizeof(cwdl) - 1);
488
        if (res < 0)
489
            return -1;
490

491
        (void)snprintf(symlink, SIR_MAXPATH, "%s%s", cwdl, relative + 1);
492

493
        res = _sir_readlink(symlink, temp_buffer, SIR_MAXPATH);
494
        if (res < 0)
495
            _sir_strncpy(buffer, SIR_MAXPATH, symlink, SIR_MAXPATH);
496
        else
497
            (void)snprintf(buffer, *size - 1, "%s/%s", (char*)dirname(symlink), temp_buffer);
498

499
        *size = strnlen(buffer, SIR_MAXPATH);
500
        return 0;
501
    } else if (strchr(argv[0], '/') != NULL) {
502
        (void)snprintf(cwd, SIR_MAXPATH, "/proc/%llu/cwd", (unsigned long long)_sir_getpid());
503

504
        res = _sir_readlink(cwd, cwdl, sizeof(cwdl) - 1);
505
        if (res < 0)
506
            return -1;
507

508
        (void)snprintf(symlink, SIR_MAXPATH, "%s%s", cwdl, argv[0]);
509

510
        res = _sir_readlink(symlink, temp_buffer, SIR_MAXPATH);
511
        if (res < 0)
512
            _sir_strncpy(buffer, SIR_MAXPATH, symlink, SIR_MAXPATH);
513
        else
514
            (void)snprintf(buffer, *size - 1, "%s/%s", (char*)dirname(symlink), temp_buffer);
515

516
        *size = strnlen(buffer, SIR_MAXPATH);
517
        return 0;
518
    } else {
519
        char clonedpath[16384];
520
        char* token = NULL;
521
        struct stat statstruct;
522

523
        char* path = getenv("PATH");
524
        if (sizeof(clonedpath) <= strnlen(path, SIR_MAXPATH))
525
            return -1;
526

527
        _sir_strncpy(clonedpath, SIR_MAXPATH, path, SIR_MAXPATH);
528

529
        token = strtok_r(clonedpath, ":", &tokptr);
530

531
        (void)snprintf(cwd, SIR_MAXPATH, "/proc/%llu/cwd", (unsigned long long)_sir_getpid());
532

533
        res = _sir_readlink(cwd, cwdl, sizeof(cwdl) - 1);
534
        if (res < 0)
535
            return -1;
536

537
        while (token != NULL) {
538
            if (token[0] == '.') {
539
                char* relative = strchr(token, '/');
540
                if (relative != NULL) {
541
                    (void)snprintf(symlink, SIR_MAXSLPATH, "%s%s/%s", cwdl, relative + 1,
542
                        ps.pr_fname);
543
                } else {
544
                    (void)snprintf(symlink, SIR_MAXSLPATH, "%s%s", cwdl, ps.pr_fname);
545
                }
546

547
                if (stat(symlink, &statstruct) != -1) {
548
                    res = _sir_readlink(symlink, temp_buffer, SIR_MAXPATH);
549
                    if (res < 0)
550
                        _sir_strncpy(buffer, SIR_MAXPATH, symlink, SIR_MAXPATH);
551
                    else
552
                        (void)snprintf(buffer, *size - 1, "%s/%s", (char*)dirname(symlink), temp_buffer);
553

554
                    *size = strnlen(buffer, SIR_MAXPATH);
555
                    return 0;
556
                }
557
            } else {
558
                (void)snprintf(symlink, SIR_MAXPATH, "%s/%s", token, ps.pr_fname);
559
                if (stat(symlink, &statstruct) != -1) {
560
                    res = _sir_readlink(symlink, temp_buffer, SIR_MAXPATH);
561
                    if (res < 0)
562
                        _sir_strncpy(buffer, SIR_MAXPATH, symlink, SIR_MAXPATH);
563
                    else
564
                        (void)snprintf(buffer, *size - 1, "%s/%s", (char*)dirname(symlink), temp_buffer);
565

566
                    *size = strnlen(buffer, SIR_MAXPATH);
567
                    return 0;
568
                }
569
            }
570

571
            token = strtok_r(NULL, ":", &tokptr);
572
        }
573
        return -1;
574
    }
575
}
576
#endif
577

578
bool _sir_deletefile(const char* restrict path) {
472✔
579
    if (!_sir_validstr(path))
472✔
580
        return false;
×
581

582
#if !defined(__WIN__)
583
    return (0 == unlink(path)) ? true : _sir_handleerr(errno);
472✔
584
#else /* __WIN__ */
585
    return (FALSE != DeleteFileA(path)) ? true : _sir_handlewin32err(GetLastError());
586
#endif
587
}
588

589
#if defined(__OpenBSD__)
590
int _sir_openbsdself(char* out, int capacity, int* dirname_length) {
591
    char buffer1[4096];
592
    char buffer2[SIR_MAXPATH];
593
    char buffer3[SIR_MAXPATH];
594
    char** argv    = (char**)buffer1;
595
    char* resolved = NULL;
596
    int length     = -1;
597

598
    while (1) {
599
        int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
600
        size_t size;
601

602
        if (sysctl(mib, 4, NULL, &size, NULL, 0) != 0)
603
            break;
604

605
        if (size > sizeof(buffer1)) {
606
            argv = (char**)malloc(size);
607
            if (!argv)
608
                break;
609
        }
610

611
        if (sysctl(mib, 4, argv, &size, NULL, 0) != 0)
612
            break;
613

614
        if (strchr(argv[0], '/')) {
615
            resolved = realpath(argv[0], buffer2);
616
            if (!resolved)
617
                break;
618
        } else {
619
            const char* PATH = getenv("PATH");
620
            if (!PATH)
621
                break;
622
            size_t argv0_length = strnlen(argv[0], SIR_MAXPATH);
623
            const char* begin   = PATH;
624
            while (1) {
625
                const char* separator = strchr(begin, ':');
626
                const char* end = separator ? separator : begin + strnlen(begin, SIR_MAXPATH);
627
                if (end - begin > 0) {
628
                    if (*(end - 1) == '/')
629
                        --end;
630
                    if (((end - begin) + 1UL + argv0_length + 1UL) <= sizeof(buffer2)) {
631
                        memcpy(buffer2, begin, end - begin);
632
                        buffer2[end - begin] = '/';
633
                        memcpy(buffer2 + (end - begin) + 1, argv[0], argv0_length + 1);
634
                        resolved = realpath(buffer2, buffer3);
635
                        if (resolved)
636
                            break;
637
                    }
638
                }
639
                if (!separator)
640
                    break;
641
                begin = ++separator;
642
            }
643
            if (!resolved)
644
                break;
645
        }
646

647
        length = (int)strnlen(resolved, SIR_MAXPATH);
648
        if (length <= capacity) {
649
            memcpy(out, resolved, (unsigned long)length);
650
            if (dirname_length) {
651
                int i;
652
                for (i = length - 1; i >= 0; --i) {
653
                    if (out[i] == '/') {
654
                        *dirname_length = i;
655
                        break;
656
                    }
657
                }
658
            }
659
        }
660
        break;
661
    }
662
    if (argv != (char**)buffer1)
663
        free(argv);
664

665
    return length;
666
}
667
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc