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

aremmell / libsir / 861

24 Sep 2023 11:22AM UTC coverage: 94.895%. First build
861

push

gitlab-ci

aremmell
sirfilecache coverage

163 of 163 new or added lines in 1 file covered. (100.0%)

3606 of 3800 relevant lines covered (94.89%)

533797.26 hits per line

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

93.77
/src/sirfilecache.c
1
/*
2
 * sirfilecache.c
3
 *
4
 * Author:    Ryan M. Lederman <lederman@gmail.com>
5
 * Copyright: Copyright (c) 2018-2023
6
 * Version:   2.2.4
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/filecache.h"
27
#include "sir/filesystem.h"
28
#include "sir/internal.h"
29
#include "sir/defaults.h"
30

31
sirfileid _sir_addfile(const char* path, sir_levels levels, sir_options opts) {
36,913✔
32
    (void)_sir_seterror(_SIR_E_NOERROR);
36,913✔
33

34
    if (!_sir_sanity())
36,913✔
35
        return 0U;
×
36

37
    _SIR_LOCK_SECTION(sirfcache, sfc, SIRMI_FILECACHE, 0U);
36,913✔
38

39
    _sir_defaultlevels(&levels, sir_file_def_lvls);
36,914✔
40
    _sir_defaultopts(&opts, sir_file_def_opts);
36,914✔
41

42
    sirfileid retval = _sir_fcache_add(sfc, path, levels, opts);
36,914✔
43
    _SIR_UNLOCK_SECTION(SIRMI_FILECACHE);
36,914✔
44

45
    return retval;
36,914✔
46
}
47

48
bool _sir_updatefile(sirfileid id, const sir_update_config_data* data) {
36,266✔
49
    (void)_sir_seterror(_SIR_E_NOERROR);
36,266✔
50

51
    if (!_sir_sanity() || !_sir_validfileid(id) || !_sir_validupdatedata(data))
36,266✔
52
        return false;
3✔
53

54
    _SIR_LOCK_SECTION(sirfcache, sfc, SIRMI_FILECACHE, false);
36,262✔
55
    bool retval = _sir_fcache_update(sfc, id, data);
36,263✔
56
    _SIR_UNLOCK_SECTION(SIRMI_FILECACHE);
36,263✔
57

58
    return retval;
36,261✔
59
}
60

61
bool _sir_remfile(sirfileid id) {
36,740✔
62
    (void)_sir_seterror(_SIR_E_NOERROR);
36,740✔
63

64
    if (!_sir_sanity() || !_sir_validfileid(id))
36,741✔
65
        return false;
81✔
66

67
    _SIR_LOCK_SECTION(sirfcache, sfc, SIRMI_FILECACHE, false);
36,655✔
68
    bool retval = _sir_fcache_rem(sfc, id);
36,662✔
69
    _SIR_UNLOCK_SECTION(SIRMI_FILECACHE);
36,662✔
70

71
    return retval;
36,662✔
72
}
73

74
sirfile* _sirfile_create(const char* path, sir_levels levels, sir_options opts) {
36,815✔
75
    if (!_sir_validstr(path) || !_sir_validlevels(levels) || !_sir_validopts(opts))
36,815✔
76
        return NULL;
×
77

78
    sirfile* sf = (sirfile*)calloc(1, sizeof(sirfile));
36,815✔
79
    if (!sf) {
36,815✔
80
        (void)_sir_handleerr(errno);
31✔
81
        return NULL;
31✔
82
    }
83

84
    sf->path = strndup(path, strnlen(path, SIR_MAXPATH));
36,784✔
85
    if (!sf->path) {
36,784✔
86
        (void)_sir_handleerr(errno);
35✔
87
        _sir_safefree(&sf);
35✔
88
        return NULL;
35✔
89
    }
90

91
    sf->levels = levels;
36,749✔
92
    sf->opts   = opts;
36,749✔
93

94
    if (!_sirfile_open(sf) || !_sirfile_validate(sf)) {
36,749✔
95
        _sirfile_destroy(&sf);
71✔
96
        return NULL;
71✔
97
    }
98

99
    return sf;
36,678✔
100
}
101

102
bool _sirfile_open(sirfile* sf) {
36,765✔
103
    bool retval = _sir_validptr(sf) && _sir_validstr(sf->path);
36,765✔
104

105
    if (retval) {
30,578✔
106
        FILE* f  = NULL;
36,765✔
107
        retval = _sir_openfile(&f, sf->path, SIR_FOPENMODE, SIR_PATH_REL_TO_CWD);
36,765✔
108
        if (retval && NULL != f) {
36,765✔
109
            _sirfile_close(sf);
36,694✔
110

111
            sf->f  = f;
36,694✔
112
            sf->id = FNV32_1a((const uint8_t*)sf->path, strnlen(sf->path, SIR_MAXPATH));
36,694✔
113
        }
114
    }
115

116
    return retval;
36,765✔
117
}
118

119
void _sirfile_close(sirfile* sf) {
73,443✔
120
    if (_sir_validptrnofail(sf))
73,443✔
121
        _sir_safefclose(&sf->f);
73,443✔
122
}
73,443✔
123

124
bool _sirfile_write(sirfile* sf, const char* output) {
1,320,064✔
125
    bool retval = _sirfile_validate(sf) && _sir_validstr(output);
1,320,064✔
126

127
    if (retval) {
1,266,474✔
128
        if (sf->writes_since_size_chk++ > SIR_FILE_CHK_SIZE_WRITES) {
1,320,064✔
129
            sf->writes_since_size_chk = 0;
93,966✔
130

131
            if (_sirfile_needsroll(sf)) {
93,966✔
132
                bool rolled   = false;
79,319✔
133
                char* newpath = NULL;
79,322✔
134

135
                _sir_selflog("file (path: '%s', id: %"PRIx32") reached ~%d bytes"
79,321✔
136
                    " in size; rolling...", sf->path, sf->id, SIR_FROLLSIZE);
137

138
                _sir_fflush(sf->f);
79,322✔
139

140
                if (_sirfile_roll(sf, &newpath)) {
79,322✔
141
                    char header[SIR_MAXFHEADER] = {0};
16✔
142
                    (void)snprintf(header, SIR_MAXFHEADER, SIR_FHROLLED, newpath);
16✔
143
                    rolled = _sirfile_writeheader(sf, header);
16✔
144
                }
145

146
                _sir_safefree(&newpath);
79,322✔
147
                if (!rolled) /* write anyway; don't want to lose data. */
79,321✔
148
                    _sir_selflog("error: failed to roll file (path: '%s', id: %"
79,306✔
149
                        PRIx32")!", sf->path, sf->id);
150
            }
151
        }
152

153
        size_t writeLen = strnlen(output, SIR_MAXOUTPUT);
1,320,064✔
154
        size_t write    = fwrite(output, sizeof(char), writeLen, sf->f);
1,320,064✔
155

156
        SIR_ASSERT(write == writeLen);
1,302,405✔
157

158
        if (write < writeLen) {
1,320,064✔
159
            (void)_sir_handleerr(errno);
×
160
            clearerr(sf->f);
×
161
        }
162

163
        retval = write == writeLen;
1,320,064✔
164
    }
165

166
    return retval;
1,320,064✔
167
}
168

169
bool _sirfile_writeheader(sirfile* sf, const char* msg) {
36,638✔
170
    bool retval = _sirfile_validate(sf) && _sir_validstr(msg);
36,638✔
171

172
    if (retval) {
30,466✔
173
        time_t now = -1;
36,638✔
174
        time(&now);
36,638✔
175

176
        char timestamp[SIR_MAXTIME] = {0};
36,638✔
177
        bool fmttime = _sir_formattime(now, timestamp, SIR_FHTIMEFORMAT);
36,638✔
178
        if (!fmttime)
36,638✔
179
            return false;
2,061✔
180

181
        char header[SIR_MAXFHEADER] = {0};
34,577✔
182
        (void)snprintf(header, SIR_MAXFHEADER, SIR_FHFORMAT, msg, timestamp);
28,405✔
183

184
        retval = _sirfile_write(sf, header);
34,577✔
185
    }
186

187
    return retval;
28,405✔
188
}
189

190
bool _sirfile_needsroll(sirfile* sf) {
93,966✔
191
    bool retval = _sirfile_validate(sf);
93,966✔
192

193
    if (retval) {
93,966✔
194
        struct stat st = {0};
93,966✔
195
        int getstat    = fstat(fileno(sf->f), &st);
93,966✔
196

197
        if (0 != getstat) { /* if fstat fails, try stat on the path. */
93,966✔
198
            getstat = stat(sf->path, &st);
600✔
199
            if (0 != getstat)
600✔
200
                return _sir_handleerr(errno);
×
201
        }
202

203
        retval = st.st_size + BUFSIZ >= SIR_FROLLSIZE ||
108,620✔
204
            SIR_FROLLSIZE - (st.st_size + BUFSIZ) <= BUFSIZ;
14,651✔
205
    }
206

207
    return retval;
92,243✔
208
}
209

210
bool _sirfile_roll(sirfile* sf, char** newpath) {
79,322✔
211
    if (!_sirfile_validate(sf) || !_sir_validptrptr(newpath))
79,322✔
212
        return false;
×
213

214
    bool retval = false;
79,319✔
215
    char* name = NULL;
79,322✔
216
    char* ext  = NULL;
79,322✔
217

218
    bool split = _sirfile_splitpath(sf, &name, &ext);
79,322✔
219
    SIR_ASSERT(split);
79,321✔
220

221
    if (split) {
79,320✔
222
        time_t now = -1;
28✔
223

224
        time(&now);
28✔
225
        SIR_ASSERT(-1 != now);
27✔
226

227
        if (-1 != now) {
28✔
228
            char timestamp[SIR_MAXTIME] = {0};
22✔
229
            bool fmttime = _sir_formattime(now, timestamp, SIR_FNAMETIMEFORMAT);
22✔
230
            SIR_ASSERT(fmttime);
21✔
231

232
            if (fmttime) {
20✔
233
                *newpath = (char*)calloc(SIR_MAXPATH, sizeof(char));
16✔
234

235
                if (_sir_validptr(*newpath)) {
16✔
236
                    char seqbuf[7] = {0};
16✔
237
                    bool exists = false;
16✔
238
                    bool resolved = false;
13✔
239
                    uint16_t sequence = 0U;
13✔
240

241
                    do {
242
                        (void)snprintf(*newpath, SIR_MAXPATH, SIR_FNAMEFORMAT, name,
19✔
243
                            timestamp, (sequence > 0U ? seqbuf : ""),
244
                            _sir_validstrnofail(ext) ? ext : "");
16✔
245

246
                        /* if less than one second has elapsed since the last roll
247
                         * operation, then we'll overwrite the last rolled log file,
248
                         * and that = data loss. make sure the target path does not
249
                         * already exist. */
250
                        if (!_sir_pathexists(*newpath, &exists, SIR_PATH_REL_TO_CWD)) {
16✔
251
                            /* failed to determine if the file already exists; it is better
252
                             * to continue logging to the same file than to possibly overwrite
253
                             * another (if it failed this time, it will again, so there's no
254
                             * way to definitively choose a good new path). */
255
                            break;
×
256
                        } else if (exists) {
16✔
257
                            /* the file already exists; add a number to the file name
258
                             * until one that does not exist is found. */
259
                            _sir_selflog("path: '%s' already exists; incrementing sequence", *newpath); //-V576
×
260
                            sequence++;
×
261
                        } else {
262
                            _sir_selflog("found good path: '%s'", *newpath);
15✔
263
                            resolved = true;
13✔
264
                            break;
13✔
265
                        }
266

267
                        if (sequence > 0)
×
268
                            (void)snprintf(seqbuf, 7, SIR_FNAMESEQFORMAT, sequence);
×
269

270
                    } while (sequence <= 999U);
×
271

272
                    if (!resolved)
13✔
273
                        _sir_selflog("error: unable to determine suitable path for '%s';"
×
274
                                        " not rolling!", sf->path);
275

276
                    retval = resolved && _sirfile_archive(sf, *newpath);
16✔
277
                }
278
            }
279
        }
280
    }
281

282
    _sir_safefree(&name);
79,322✔
283
    _sir_safefree(&ext);
79,322✔
284

285
    return retval;
79,322✔
286
}
287

288
bool _sirfile_archive(sirfile* sf, const char* newpath) {
16✔
289
    bool retval = _sirfile_validate(sf) && _sir_validstr(newpath);
16✔
290

291
    if (retval) {
13✔
292
#if defined(__WIN__)
293
        /* apparently, need to close the old file first on windows. */
294
        _sirfile_close(sf);
295
#endif
296
        if (0 != rename(sf->path, newpath)) {
16✔
297
            retval = _sir_handleerr(errno);
×
298
        } else {
299
            retval = _sirfile_open(sf);
16✔
300
            if (retval)
16✔
301
                _sir_selflog("archived '%s' " SIR_R_ARROW " '%s'", sf->path, newpath);
15✔
302
        }
303
    }
304

305
    return retval;
16✔
306
}
307

308
bool _sirfile_splitpath(const sirfile* sf, char** name, char** ext) {
79,322✔
309
    if (_sir_validptrptr(name))
79,322✔
310
        *name = NULL;
79,322✔
311
    if (_sir_validptrptr(ext))
79,322✔
312
        *ext = NULL;
79,322✔
313

314
    bool retval = _sirfile_validate(sf) && _sir_validptrptr(name) && _sir_validptrptr(ext);
79,322✔
315

316
    if (retval) {
79,319✔
317
        char* tmp = strndup(sf->path, strnlen(sf->path, SIR_MAXPATH));
79,322✔
318
        if (!tmp)
79,322✔
319
            return _sir_handleerr(errno);
×
320

321
        const char* lastfullstop = strrchr(tmp, '.');
79,322✔
322
        if (lastfullstop) {
79,322✔
323
            uintptr_t namesize = lastfullstop - tmp;
79,322✔
324
            SIR_ASSERT(namesize < SIR_MAXPATH);
79,321✔
325

326
            tmp[namesize] = '\0';
79,322✔
327
            *name = (char*)calloc(namesize + 1, sizeof(char));
79,322✔
328
            if (!*name) {
79,322✔
329
                _sir_safefree(&tmp);
×
330
                return _sir_handleerr(errno);
×
331
            }
332

333
            _sir_strncpy(*name, namesize + 1, tmp, namesize);
79,322✔
334
            *ext = strndup(sf->path + namesize, strnlen(sf->path + namesize, SIR_MAXPATH));
79,322✔
335
        } else {
336
            *name = strndup(sf->path, strnlen(sf->path, SIR_MAXPATH));
×
337
        }
338

339
        _sir_safefree(&tmp);
79,322✔
340
        retval = _sir_validstr(*name) && (!lastfullstop || _sir_validstr(*ext));
79,325✔
341
    }
342

343
    return retval;
79,319✔
344
}
345

346
void _sirfile_destroy(sirfile** sf) {
36,749✔
347
    if (sf && *sf) {
36,749✔
348
        _sirfile_close(*sf);
36,749✔
349
        _sir_safefree(&(*sf)->path);
36,749✔
350
        _sir_safefree(sf);
36,749✔
351
    }
352
}
36,749✔
353

354
bool _sirfile_validate(const sirfile* sf) {
3,069,532✔
355
    return _sir_validptrnofail(sf) && _sir_validptrnofail(sf->f) &&
9,208,322✔
356
           _sir_validstrnofail(sf->path) && _sir_validfileid(sf->id);
9,208,322✔
357
}
358

359
bool _sirfile_update(sirfile* sf, const sir_update_config_data* data) {
36,263✔
360
    bool retval = _sirfile_validate(sf);
36,263✔
361

362
    if (retval) {
36,263✔
363
        bool updated = false;
30,291✔
364
        if (_sir_bittest(data->fields, SIRU_LEVELS)) {
36,263✔
365
            if (sf->levels != *data->levels) {
200✔
366
                _sir_selflog("updating file (id: %"PRIx32") levels from %04"PRIx16
180✔
367
                            " to %04"PRIx16, sf->id, sf->levels, *data->levels);
368
                sf->levels = *data->levels;
191✔
369
            } else {
370
                _sir_selflog("skipped superfluous update of file (id: %"PRIx32")"
9✔
371
                            " levels: %04"PRIx16, sf->id, sf->levels);
372
            }
373

374
            updated = true;
167✔
375
        }
376

377
        if (_sir_bittest(data->fields, SIRU_OPTIONS)) {
36,263✔
378
            if (sf->opts != *data->opts) {
36,063✔
379
                _sir_selflog("updating file (id: %"PRIx32") options from %08"PRIx32
17,139✔
380
                            " to %08"PRIx32, sf->id, sf->opts, *data->opts);
381
                sf->opts = *data->opts;
18,145✔
382
            } else {
383
                _sir_selflog("skipped superfluous update of file (id: %"PRIx32")"
16,947✔
384
                            " options: %08"PRIx32, sf->id, sf->opts);
385
            }
386

387
            updated = true;
30,124✔
388
        }
389

390
        retval = updated;
30,291✔
391
    }
392

393
    return retval;
36,263✔
394
}
395

396
sirfileid _sir_fcache_add(sirfcache* sfc, const char* path, sir_levels levels,
36,914✔
397
    sir_options opts) {
398
    if (!_sir_validptr(sfc) || !_sir_validstr(path) || !_sir_validlevels(levels) ||
43,110✔
399
        !_sir_validopts(opts))
36,891✔
400
        return 0U;
23✔
401

402
    if (sfc->count >= SIR_MAXFILES)
36,891✔
403
        return _sir_seterror(_SIR_E_NOROOM);
19✔
404

405
    const sirfile* existing = _sir_fcache_find(sfc, (const void*)path, _sir_fcache_pred_path);
36,872✔
406
    if (NULL != existing) {
36,872✔
407
        _sir_selflog("error: already have file (path: '%s', id: %"PRIx32")",
54✔
408
            path, existing->id);
409
        return _sir_seterror(_SIR_E_DUPITEM);
57✔
410
    }
411

412
    sirfile* sf = _sirfile_create(path, levels, opts);
36,815✔
413
    if (_sirfile_validate(sf)) {
36,815✔
414
        _sir_selflog("adding file (path: '%s', id: %"PRIx32"); count = %zu", //-V522
36,678✔
415
            sf->path, sf->id, sfc->count + 1);
416

417
        sfc->files[sfc->count++] = sf;
36,678✔
418

419
        if (!_sir_bittest(sf->opts, SIRO_NOHDR))
36,678✔
420
            _sirfile_writeheader(sf, SIR_FHBEGIN);
36,622✔
421

422
        return sf->id;
36,678✔
423
    }
424

425
    _sir_safefree(&sf);
137✔
426

427
    return 0U;
137✔
428
}
429

430
bool _sir_fcache_update(const sirfcache* sfc, sirfileid id, const sir_update_config_data* data) {
36,263✔
431
    bool retval = _sir_validptr(sfc) && _sir_validfileid(id) && _sir_validupdatedata(data);
36,263✔
432

433
    if (retval) {
30,291✔
434
        sirfile* found = _sir_fcache_find(sfc, (const void*)&id, _sir_fcache_pred_id);
36,263✔
435
        retval = found ? _sirfile_update(found, data) : _sir_seterror(_SIR_E_NOITEM);
36,263✔
436
    }
437

438
    return retval;
36,263✔
439
}
440

441
bool _sir_fcache_rem(sirfcache* sfc, sirfileid id) {
36,662✔
442
    bool retval = _sir_validptr(sfc) && _sir_validfileid(id);
36,662✔
443

444
    if (retval) {
30,487✔
445
        bool found = false;
30,487✔
446
        for (size_t n = 0; n < sfc->count; n++) {
82,931✔
447
            SIR_ASSERT(_sirfile_validate(sfc->files[n]));
78,319✔
448

449
            if (sfc->files[n]->id == id) {
82,909✔
450
                _sir_selflog("removing file (path: '%s', id: %"PRIx32"); count = %zu",
34,580✔
451
                    sfc->files[n]->path, sfc->files[n]->id, sfc->count - 1);
452

453
                _sirfile_destroy(&sfc->files[n]);
36,640✔
454

455
                for (size_t i = n; i < sfc->count - 1; i++) {
97,067✔
456
                    sfc->files[i] = sfc->files[i + 1];
60,427✔
457
                    sfc->files[i + 1] = NULL;
60,427✔
458
                }
459

460
                sfc->count--;
36,640✔
461
                found = true;
30,468✔
462
                break;
30,468✔
463
            }
464
        }
465

466
        if (!found)
30,487✔
467
            retval = _sir_seterror(_SIR_E_NOITEM);
22✔
468
    }
469

470
    return retval;
36,662✔
471
}
472

473
bool _sir_fcache_pred_path(const void* match, const sirfile* iter) {
106,791✔
474
    const char* path = (const char*)match;
88,845✔
475
    bool equal       = false;
88,845✔
476

477
    _sir_selflog("comparing '%s' == '%s'", path, iter->path);
100,914✔
478

479
#if !defined(__WIN__)
480
    /* if we're able to stat both files then we can use that information for the
481
     * comparison. otherwise, fall back on comparing the canonical path strings
482
     * returned by realpath. */
483
    char resolved1[SIR_MAXPATH] = {0};
106,791✔
484
    char resolved2[SIR_MAXPATH] = {0};
106,791✔
485
    struct stat st1             = {0};
106,791✔
486
    struct stat st2             = {0};
106,791✔
487

488
    const char* real1 = realpath(path, resolved1);
88,845✔
489
    const char* real2 = realpath(iter->path, resolved2);
106,791✔
490

491
    SIR_UNUSED(real1);
492
    SIR_UNUSED(real2);
493

494
    if ((!stat(resolved1, &st1) && !stat(resolved2, &st2)) ||
115,086✔
495
        (!stat(path, &st1) && !stat(iter->path, &st2))) {
8,295✔
496
        equal = st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
98,496✔
497
        _sir_selflog("returning %d for '%s' == '%s'", equal, path, iter->path);
98,496✔
498
    } else {
499
        _sir_selflog("falling back to canonical path string compare");
8,166✔
500
        equal = 0 == strncmp(resolved1, resolved2, SIR_MAXPATH);
8,295✔
501
        _sir_selflog("returning %d for '%s' == '%s'", equal, resolved1, resolved2);
8,166✔
502
    }
503

504
    return equal;
106,791✔
505
#else /* __WIN__ */
506
    /* open both files (only if they already exist) and compare their
507
     * filesystem info. failing that, fall back on conversion to canonical path
508
     * and string comparison. */
509
    DWORD sh_flags   = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
510
    DWORD open_type  = OPEN_EXISTING;
511
    DWORD attr_flags = FILE_ATTRIBUTE_NORMAL;
512
    BY_HANDLE_FILE_INFORMATION fi1 = {0};
513
    BY_HANDLE_FILE_INFORMATION fi2 = {0};
514

515
    HANDLE h1 = CreateFileA(path, 0, sh_flags, NULL, open_type, attr_flags, NULL);
516
    HANDLE h2 = CreateFileA(iter->path,0, sh_flags, NULL, open_type, attr_flags, NULL);
517

518
    if (INVALID_HANDLE_VALUE != h1 && INVALID_HANDLE_VALUE != h2 &&
519
        GetFileInformationByHandle(h1, &fi1) && GetFileInformationByHandle(h2, &fi2)) {
520
        equal = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber &&
521
                fi1.nFileIndexLow        == fi2.nFileIndexLow        &&
522
                fi1.nFileIndexHigh       == fi2.nFileIndexHigh;
523
        _sir_selflog("returning %d for '%s' == '%s'", equal, path, iter->path);
524
    } else {
525
        _sir_selflog("falling back to canonical path string compare");
526
        char resolved1[SIR_MAXPATH] = {0}, resolved2[SIR_MAXPATH] = {0};
527
        if (GetFullPathNameA(path, SIR_MAXPATH, resolved1, NULL) &&
528
            GetFullPathNameA(iter->path, SIR_MAXPATH, resolved2, NULL))
529
            equal = 0 == StrCmpIA(resolved1, resolved2);
530
        _sir_selflog("returning %d for '%s' == '%s'", equal, resolved1, resolved2);
531
    }
532

533
    if (INVALID_HANDLE_VALUE != h1)
534
        CloseHandle(h1);
535

536
    if (INVALID_HANDLE_VALUE != h2)
537
        CloseHandle(h2);
538

539
    return equal;
540
#endif
541
}
542

543
bool _sir_fcache_pred_id(const void* match, const sirfile* iter) {
79,383✔
544
    const sirfileid* id = (const sirfileid*)match;
66,840✔
545
    return iter->id == *id;
79,383✔
546
}
547

548
sirfile* _sir_fcache_find(const sirfcache* sfc, const void* match, sir_fcache_pred pred) {
73,135✔
549
    bool valid = _sir_validptr(sfc) && _sir_validptr(match) && _sir_validfnptr(pred);
73,135✔
550

551
    if (valid) {
60,970✔
552
        for (size_t n = 0; n < sfc->count; n++) {
222,989✔
553
            if (pred(match, sfc->files[n]))
186,174✔
554
                return sfc->files[n];
36,320✔
555
        }
556
    }
557

558
    return NULL;
30,631✔
559
}
560

561
bool _sir_fcache_destroy(sirfcache* sfc) {
786✔
562
    bool retval = _sir_validptr(sfc);
786✔
563

564
    if (retval) {
786✔
565
        while (sfc->count > 0) {
824✔
566
            size_t idx = sfc->count - 1;
38✔
567
            SIR_ASSERT(_sirfile_validate(sfc->files[idx]));
36✔
568
            _sirfile_destroy(&sfc->files[idx]);
38✔
569
            sfc->files[idx] = NULL;
38✔
570
            sfc->count--;
38✔
571
        }
572

573
        memset(sfc, 0, sizeof(sirfcache));
678✔
574
    }
575

576
    return retval;
786✔
577
}
578

579
bool _sir_fcache_dispatch(const sirfcache* sfc, sir_level level, sirbuf* buf,
2,126,107✔
580
    size_t* dispatched, size_t* wanted) {
581
    bool retval = _sir_validptr(sfc) && _sir_validlevel(level) &&
6,378,321✔
582
                  _sir_validptr(buf) && _sir_validptr(dispatched) &&
8,504,428✔
583
                  _sir_validptr(wanted);
2,126,107✔
584

585
    if (retval) {
2,106,568✔
586
        const char* write    = NULL;
2,106,568✔
587
        sir_options lastopts = 0U;
2,106,568✔
588

589
        *dispatched = 0;
2,126,107✔
590
        *wanted     = 0;
2,126,107✔
591

592
        for (size_t n = 0; n < sfc->count; n++) {
3,413,912✔
593
            SIR_ASSERT(_sirfile_validate(sfc->files[n]));
1,272,093✔
594

595
            if (!_sir_bittest(sfc->files[n]->levels, level)) {
1,287,805✔
596
                _sir_selflog("level %04"PRIx16" not set in level mask (%04"PRIx16
2,205✔
597
                            ") for file (path: '%s', id: %"PRIx32"); skipping",
598
                            level, sfc->files[n]->levels, sfc->files[n]->path,
599
                            sfc->files[n]->id);
600
                continue;
2,318✔
601
            }
602

603
            (*wanted)++;
1,285,487✔
604

605
            if (!write || sfc->files[n]->opts != lastopts) {
1,285,487✔
606
                write = _sir_format(false, sfc->files[n]->opts, buf);
1,147,545✔
607
                SIR_ASSERT(write);
1,139,293✔
608
                lastopts = sfc->files[n]->opts;
1,147,545✔
609
            }
610

611
            if (write && _sirfile_write(sfc->files[n], write)) {
1,285,487✔
612
                (*dispatched)++;
1,285,487✔
613
            } else {
614
                _sir_selflog("error: write to file (path: '%s', id: %"PRIx32") failed!",
×
615
                    sfc->files[n]->path, sfc->files[n]->id);
616
            }
617
        }
618

619
        retval = (*dispatched == *wanted);
2,126,107✔
620
    }
621

622
    return retval;
2,126,107✔
623
}
624

625
void _sir_fflush(FILE* f) {
79,322✔
626
    if (_sir_validptr(f) && 0 != fflush(f))
79,322✔
627
        (void)_sir_handleerr(errno);
×
628
}
79,322✔
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