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

aremmell / libsir / 522

07 Sep 2023 09:49PM UTC coverage: 94.711% (-0.02%) from 94.727%
522

push

gitlab-ci

web-flow
On Windows, use native Win32 API for file I/O (#268)

On Windows, use native Win32 API for file I/O

---------

Signed-off-by: Jeffrey H. Johnson <trnsz@pobox.com>
Co-authored-by: Jeffrey H. Johnson <trnsz@pobox.com>

18 of 18 new or added lines in 2 files covered. (100.0%)

3116 of 3290 relevant lines covered (94.71%)

604866.04 hits per line

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

90.71
/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.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/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,687✔
32
    (void)_sir_seterror(_SIR_E_NOERROR);
36,687✔
33

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

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

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

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

45
    return retval;
36,687✔
46
}
47

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

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

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

58
    return retval;
36,488✔
59
}
60

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

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

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

71
    return retval;
36,436✔
72
}
73

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

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

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

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

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

99
    return sf;
36,452✔
100
}
101

102
bool _sirfile_open(sirfile* sf) {
36,559✔
103
    if (!_sir_validptr(sf) && !_sir_validstr(sf->path))
36,559✔
104
        return false;
×
105

106
#if !defined(__WIN__)
107
    FILE* f  = NULL;
36,559✔
108
    bool open = _sir_openfile(&f, sf->path, SIR_FOPENMODE, SIR_PATH_REL_TO_CWD);
36,559✔
109
    if (!open || !f)
36,559✔
110
        return false;
65✔
111
#else /* __WIN__ */
112
    HANDLE h = NULL;
113
    bool open = _sir_openfilewin32(&h, sf->path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
114
        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, SIR_PATH_REL_TO_CWD);
115
    if (!open || INVALID_HANDLE_VALUE == h)
116
        return false;
117
#endif
118

119
    _sirfile_close(sf);
36,488✔
120

121
#if !defined(__WIN__)
122
    sf->f = f;
36,488✔
123
#else
124
    sf->h = h;
125
#endif
126

127
    sf->id = FNV32_1a((const uint8_t*)sf->path, strnlen(sf->path, SIR_MAXPATH));
36,488✔
128

129
    return true;
36,488✔
130
}
131

132
void _sirfile_close(sirfile* sf) {
73,011✔
133
    if (!_sir_validptrnofail(sf))
73,011✔
134
        return;
×
135

136
#if !defined(__WIN__)
137
    _sir_safefclose(&sf->f);
73,011✔
138
#else /* __WIN__ */
139
    _sir_safeclosehandle(&sf->h);
140
#endif
141
}
142

143
bool _sirfile_write(sirfile* sf, const char* output) {
1,315,409✔
144
    if (!_sirfile_validate(sf) || !_sir_validstr(output))
1,315,409✔
145
        return false;
×
146

147
    if (sf->writes_since_size_chk++ > SIR_FILE_CHK_SIZE_WRITES) {
1,315,409✔
148
        sf->writes_since_size_chk = 0;
95,596✔
149

150
        if (_sirfile_needsroll(sf)) {
95,596✔
151
            bool rolled   = false;
45✔
152
            char* newpath = NULL;
48✔
153

154
            _sir_selflog("file (path: '%s', id: %"PRIx32") reached ~%d bytes in size;"
47✔
155
                         " rolling...", sf->path, sf->id, SIR_FROLLSIZE);
156
#if !defined(__WIN__)
157
            _sir_fflush(sf->f);
48✔
158
#endif
159
            if (_sirfile_roll(sf, &newpath)) {
48✔
160
                char header[SIR_MAXFHEADER] = {0};
36✔
161
                (void)snprintf(header, SIR_MAXFHEADER, SIR_FHROLLED, newpath);
36✔
162
                rolled = _sirfile_writeheader(sf, header);
36✔
163
            }
164

165
            _sir_safefree(&newpath);
48✔
166
            if (!rolled) /* write anyway; don't want to lose data. */
47✔
167
                _sir_selflog("error: failed to roll file (path: '%s', id: %" PRIx32")!",
12✔
168
                    sf->path, sf->id);
169
        }
170
    }
171

172
    size_t writeLen = strnlen(output, SIR_MAXOUTPUT);
1,315,409✔
173
#if !defined(__WIN__)
174
    size_t write = fwrite(output, sizeof(char), writeLen, sf->f);
1,315,409✔
175
    if (write < writeLen) {
1,315,409✔
176
        (void)_sir_handleerr(errno);
×
177
        clearerr(sf->f);
×
178
    }
179
#else /* __WIN__ */
180
    DWORD from_write_file = 0UL;
181
    if (!WriteFile(sf->h, output, (DWORD)writeLen, &from_write_file, NULL))
182
        (void)_sir_handlewin32err(GetLastError());
183
    size_t write = (size_t)from_write_file;
184
#endif
185

186
    SIR_ASSERT(write == writeLen);
1,297,591✔
187
    return write == writeLen;
1,315,409✔
188
}
189

190
bool _sirfile_writeheader(sirfile* sf, const char* msg) {
36,432✔
191
    if (!_sirfile_validate(sf) || !_sir_validstr(msg))
36,432✔
192
        return false;
×
193

194
    time_t now = -1;
36,432✔
195
    time(&now);
36,432✔
196

197
    char timestamp[SIR_MAXTIME] = {0};
36,432✔
198
    bool fmttime = _sir_formattime(now, timestamp, SIR_FHTIMEFORMAT);
36,432✔
199
    if (!fmttime)
36,432✔
200
        return false;
2,071✔
201

202
    char header[SIR_MAXFHEADER] = {0};
34,361✔
203
    (void)snprintf(header, SIR_MAXFHEADER, SIR_FHFORMAT, msg, timestamp);
28,207✔
204

205
    return _sirfile_write(sf, header);
34,361✔
206
}
207

208
bool _sirfile_needsroll(sirfile* sf) {
95,596✔
209
    if (!_sirfile_validate(sf))
95,596✔
210
        return false;
×
211

212
    off_t size = 0;
93,462✔
213
#if !defined (__WIN__)
214
    struct stat st = {0};
95,596✔
215
    int getstat    = fstat(fileno(sf->f), &st);
95,596✔
216

217
    if (0 != getstat) { /* if fstat fails, try stat on the path. */
95,596✔
218
        getstat = stat(sf->path, &st);
620✔
219
        if (0 != getstat)
620✔
220
            return _sir_handleerr(errno);
×
221
    }
222

223
    size = st.st_size;
95,596✔
224
#else /* __WIN__ */
225
    LARGE_INTEGER li = {0};
226
    if (!GetFileSizeEx(sf->h, &li))
227
        (void)_sir_handlewin32err(GetLastError());
228
    size = (off_t)li.QuadPart;
229
#endif
230
    return size + BUFSIZ >= SIR_FROLLSIZE ||
191,167✔
231
        SIR_FROLLSIZE - (size + BUFSIZ) <= BUFSIZ;
95,568✔
232
}
233

234
bool _sirfile_roll(sirfile* sf, char** newpath) {
48✔
235
    if (!_sirfile_validate(sf) || !_sir_validptrptr(newpath))
48✔
236
        return false;
×
237

238
    bool retval = false;
45✔
239
    char* name = NULL;
48✔
240
    char* ext  = NULL;
48✔
241

242
    bool split = _sirfile_splitpath(sf, &name, &ext);
48✔
243
    SIR_ASSERT(split);
47✔
244

245
    if (split) {
46✔
246
        time_t now = -1;
48✔
247

248
        time(&now);
48✔
249
        SIR_ASSERT(-1 != now);
47✔
250

251
        if (-1 != now) {
48✔
252
            char timestamp[SIR_MAXTIME] = {0};
42✔
253
            bool fmttime = _sir_formattime(now, timestamp, SIR_FNAMETIMEFORMAT);
42✔
254
            SIR_ASSERT(fmttime);
41✔
255

256
            if (fmttime) {
40✔
257
                *newpath = (char*)calloc(SIR_MAXPATH, sizeof(char));
36✔
258

259
                if (_sir_validptr(*newpath)) {
36✔
260
                    char seqbuf[7] = {0};
36✔
261
                    bool exists = false;
36✔
262
                    bool resolved = false;
33✔
263
                    uint16_t sequence = 0U;
33✔
264

265
                    do {
266
                        (void)snprintf(*newpath, SIR_MAXPATH, SIR_FNAMEFORMAT, name,
91✔
267
                            timestamp, (sequence > 0U ? seqbuf : ""),
268
                            _sir_validstrnofail(ext) ? ext : "");
88✔
269

270
                        /* if less than one second has elapsed since the last roll
271
                         * operation, then we'll overwrite the last rolled log file,
272
                         * and that = data loss. make sure the target path does not
273
                         * already exist. */
274
                        if (!_sir_pathexists(*newpath, &exists, SIR_PATH_REL_TO_CWD)) {
88✔
275
                            /* failed to determine if the file already exists; it is better
276
                             * to continue logging to the same file than to possibly overwrite
277
                             * another (if it failed this time, it will again, so there's no
278
                             * way to definitively choose a good new path). */
279
                            break;
×
280
                        } else if (exists) {
88✔
281
                            /* the file already exists; add a number to the file name
282
                             * until one that does not exist is found. */
283
                            _sir_selflog("path: '%s' already exists; incrementing sequence", *newpath); //-V576
52✔
284
                            sequence++;
52✔
285
                        } else {
286
                            _sir_selflog("found good path: '%s'", *newpath);
35✔
287
                            resolved = true;
33✔
288
                            break;
33✔
289
                        }
290

291
                        if (sequence > 0)
52✔
292
                            (void)snprintf(seqbuf, 7, SIR_FNAMESEQFORMAT, sequence);
52✔
293

294
                    } while (sequence <= 999U);
52✔
295

296
                    if (!resolved)
33✔
297
                        _sir_selflog("error: unable to determine suitable path for '%s';"
×
298
                                        " not rolling!", sf->path);
299

300
                    retval = resolved && _sirfile_archive(sf, *newpath);
36✔
301
                }
302
            }
303
        }
304
    }
305

306
    _sir_safefree(&name);
48✔
307
    _sir_safefree(&ext);
48✔
308

309
    return retval;
48✔
310

311
}
312

313
bool _sirfile_archive(sirfile* sf, const char* newpath) {
36✔
314
    if (!_sirfile_validate(sf) || !_sir_validstr(newpath))
36✔
315
        return false;
×
316

317
#if defined(__WIN__)
318
    /* apparently, need to close the old file first on windows. */
319
    _sirfile_close(sf);
320
#endif
321

322
    if (0 != rename(sf->path, newpath))
36✔
323
        return _sir_handleerr(errno);
×
324

325
    if (_sirfile_open(sf)) {
36✔
326
        _sir_selflog("archived '%s' " SIR_R_ARROW " '%s'", sf->path, newpath);
35✔
327
        return true;
35✔
328
    }
329

330
    return false;
×
331
}
332

333
bool _sirfile_splitpath(const sirfile* sf, char** name, char** ext) {
48✔
334
    if (_sir_validptrptr(name))
48✔
335
        *name = NULL;
48✔
336
    if (_sir_validptrptr(ext))
48✔
337
        *ext = NULL;
48✔
338

339
    if (!_sirfile_validate(sf) || !_sir_validptrptr(name) || !_sir_validptrptr(ext))
48✔
340
        return false;
×
341

342
    char* tmp = strndup(sf->path, strnlen(sf->path, SIR_MAXPATH));
48✔
343
    if (!tmp)
48✔
344
        return _sir_handleerr(errno);
×
345

346
    const char* lastfullstop = strrchr(tmp, '.');
48✔
347
    if (lastfullstop) {
48✔
348
        uintptr_t namesize = lastfullstop - tmp;
28✔
349
        SIR_ASSERT(namesize < SIR_MAXPATH);
27✔
350

351
        tmp[namesize] = '\0';
28✔
352
        *name = (char*)calloc(namesize + 1, sizeof(char));
28✔
353
        if (!*name) {
28✔
354
            _sir_safefree(&tmp);
×
355
            return _sir_handleerr(errno);
×
356
        }
357

358
        _sir_strncpy(*name, namesize + 1, tmp, namesize);
28✔
359
        *ext = strndup(sf->path + namesize, strnlen(sf->path + namesize, SIR_MAXPATH));
28✔
360
    } else {
361
        *name = strndup(sf->path, strnlen(sf->path, SIR_MAXPATH));
20✔
362
    }
363

364
    _sir_safefree(&tmp);
48✔
365
    return _sir_validstr(*name) && (!lastfullstop || _sir_validstr(*ext));
51✔
366
}
367

368
void _sirfile_destroy(sirfile** sf) {
36,523✔
369
    if (sf && *sf) {
36,523✔
370
        _sirfile_close(*sf);
36,523✔
371
        _sir_safefree(&(*sf)->path);
36,523✔
372
        _sir_safefree(sf);
36,523✔
373
    }
374
}
36,523✔
375

376
bool _sirfile_validate(const sirfile* sf) {
2,916,340✔
377
    return _sir_validptrnofail(sf) &&
5,832,543✔
378
#if !defined(__WIN__)
379
        _sir_validptrnofail(sf->f) &&
5,832,406✔
380
#else /* __WIN__ */
381
        (_sir_validptrnofail(sf->h) && INVALID_HANDLE_VALUE != sf->h) &&
382
#endif
383
        _sir_validstrnofail(sf->path) && _sir_validfileid(sf->id);
8,748,746✔
384
}
385

386
bool _sirfile_update(sirfile* sf, const sir_update_config_data* data) {
36,489✔
387
    if (!_sirfile_validate(sf))
36,489✔
388
        return false;
×
389

390
    if (_sir_bittest(data->fields, SIRU_LEVELS)) {
36,489✔
391
        if (sf->levels != *data->levels) {
200✔
392
            _sir_selflog("updating file (id: %"PRIx32") levels from %04"PRIx16
178✔
393
                         " to %04"PRIx16, sf->id, sf->levels, *data->levels);
394
            sf->levels = *data->levels;
189✔
395
        } else {
396
            _sir_selflog("skipped superfluous update of file (id: %"PRIx32")"
11✔
397
                         " levels: %04"PRIx16, sf->id, sf->levels);
398
        }
399

400
        return true;
200✔
401
    }
402

403
    if (_sir_bittest(data->fields, SIRU_OPTIONS)) {
36,289✔
404
        if (sf->opts != *data->opts) {
36,289✔
405
            _sir_selflog("updating file (id: %"PRIx32") options from %08"PRIx32
17,098✔
406
                         " to %08"PRIx32, sf->id, sf->opts, *data->opts);
407
            sf->opts = *data->opts;
18,129✔
408
        } else {
409
            _sir_selflog("skipped superfluous update of file (id: %"PRIx32")"
17,196✔
410
                         " options: %08"PRIx32, sf->id, sf->opts);
411
        }
412

413
        return true;
36,289✔
414
    }
415

416
    return false;
×
417
}
418

419
sirfileid _sir_fcache_add(sirfcache* sfc, const char* path, sir_levels levels,
36,687✔
420
    sir_options opts) {
421
    if (!_sir_validptr(sfc) || !_sir_validstr(path) || !_sir_validlevels(levels) ||
42,865✔
422
        !_sir_validopts(opts))
36,665✔
423
        return 0U;
22✔
424

425
    if (sfc->count >= SIR_MAXFILES)
36,665✔
426
        return _sir_seterror(_SIR_E_NOROOM);
19✔
427

428
    const sirfile* existing = _sir_fcache_find(sfc, (const void*)path, _sir_fcache_pred_path);
36,646✔
429
    if (NULL != existing) {
36,646✔
430
        _sir_selflog("error: already have file (path: '%s', id: %"PRIx32")",
54✔
431
            path, existing->id);
432
        return _sir_seterror(_SIR_E_DUPITEM);
57✔
433
    }
434

435
    sirfile* sf = _sirfile_create(path, levels, opts);
36,589✔
436
    if (_sirfile_validate(sf)) {
36,589✔
437
        _sir_selflog("adding file (path: '%s', id: %"PRIx32"); count = %zu", //-V522
36,452✔
438
            sf->path, sf->id, sfc->count + 1);
439

440
        sfc->files[sfc->count++] = sf;
36,452✔
441

442
        if (!_sir_bittest(sf->opts, SIRO_NOHDR)) //-V522
36,452✔
443
            _sirfile_writeheader(sf, SIR_FHBEGIN);
36,396✔
444

445
        return sf->id;
36,452✔
446
    }
447

448
    _sir_safefree(&sf);
137✔
449

450
    return 0U;
137✔
451
}
452

453
bool _sir_fcache_update(const sirfcache* sfc, sirfileid id, const sir_update_config_data* data) {
36,489✔
454
    if (!_sir_validptr(sfc) || !_sir_validfileid(id) || !_sir_validupdatedata(data))
36,489✔
455
        return false;
×
456

457
    sirfile* found = _sir_fcache_find(sfc, (const void*)&id, _sir_fcache_pred_id);
36,489✔
458
    return found ? _sirfile_update(found, data) : _sir_seterror(_SIR_E_NOITEM);
36,489✔
459
}
460

461
bool _sir_fcache_rem(sirfcache* sfc, sirfileid id) {
36,436✔
462
    if (!_sir_validptr(sfc) || !_sir_validfileid(id))
36,436✔
463
        return false;
×
464

465
    for (size_t n = 0; n < sfc->count; n++) {
96,333✔
466
        SIR_ASSERT(_sirfile_validate(sfc->files[n]));
91,620✔
467

468
        if (sfc->files[n]->id == id) {
96,311✔
469
            _sir_selflog("removing file (path: '%s', id: %"PRIx32"); count = %zu",
34,372✔
470
                sfc->files[n]->path, sfc->files[n]->id, sfc->count - 1);
471

472
            _sirfile_destroy(&sfc->files[n]);
36,414✔
473

474
            for (size_t i = n; i < sfc->count - 1; i++) {
80,289✔
475
                sfc->files[i] = sfc->files[i + 1];
43,875✔
476
                sfc->files[i + 1] = NULL;
43,875✔
477
            }
478

479
            sfc->count--;
36,414✔
480
            return true;
36,414✔
481
        }
482
    }
483

484
    return _sir_seterror(_SIR_E_NOITEM);
22✔
485
}
486

487
bool _sir_fcache_pred_path(const void* match, const sirfile* iter) {
103,867✔
488
    const char* path = (const char*)match;
86,310✔
489
    bool equal       = false;
86,310✔
490

491
    _sir_selflog("comparing '%s' == '%s'", path, iter->path);
97,937✔
492

493
#if !defined(__WIN__)
494
    /* if we're able to stat both files then we can use that information for the
495
     * comparison. otherwise, fall back on comparing the canonical path strings
496
     * returned by realpath. */
497
    char resolved1[SIR_MAXPATH] = {0};
103,867✔
498
    char resolved2[SIR_MAXPATH] = {0};
103,867✔
499
    struct stat st1             = {0};
103,867✔
500
    struct stat st2             = {0};
103,867✔
501

502
    const char* real1 = realpath(path, resolved1);
86,310✔
503
    const char* real2 = realpath(iter->path, resolved2);
103,867✔
504

505
    SIR_UNUSED(real1);
506
    SIR_UNUSED(real2);
507

508
    if ((!stat(resolved1, &st1) && !stat(resolved2, &st2)) ||
111,939✔
509
        (!stat(path, &st1) && !stat(iter->path, &st2))) {
8,072✔
510
        equal = st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
95,795✔
511
        _sir_selflog("returning %d for '%s' == '%s'", equal, path, iter->path);
95,795✔
512
    } else {
513
        _sir_selflog("falling back to canonical path string compare");
7,943✔
514
        equal = 0 == strncmp(resolved1, resolved2, SIR_MAXPATH);
8,072✔
515
        _sir_selflog("returning %d for '%s' == '%s'", equal, resolved1, resolved2);
7,943✔
516
    }
517

518
    return equal;
103,867✔
519
#else /* __WIN__ */
520
    /* open both files (only if they already exist) and compare their
521
     * filesystem info. failing that, fall back on conversion to canonical path
522
     * and string comparison. */
523
    DWORD sh_flags   = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
524
    DWORD open_type  = OPEN_EXISTING;
525
    DWORD attr_flags = FILE_ATTRIBUTE_NORMAL;
526
    BY_HANDLE_FILE_INFORMATION fi1 = {0};
527
    BY_HANDLE_FILE_INFORMATION fi2 = {0};
528

529
    HANDLE h1 = CreateFileA(path, 0, sh_flags, NULL, open_type, attr_flags, NULL);
530
    HANDLE h2 = CreateFileA(iter->path,0, sh_flags, NULL, open_type, attr_flags, NULL);
531

532
    if (INVALID_HANDLE_VALUE != h1 && INVALID_HANDLE_VALUE != h2 &&
533
        GetFileInformationByHandle(h1, &fi1) && GetFileInformationByHandle(h2, &fi2)) {
534
        equal = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber &&
535
                fi1.nFileIndexLow        == fi2.nFileIndexLow        &&
536
                fi1.nFileIndexHigh       == fi2.nFileIndexHigh;
537
        _sir_selflog("returning %d for '%s' == '%s'", equal, path, iter->path);
538
    } else {
539
        _sir_selflog("falling back to canonical path string compare");
540
        char resolved1[SIR_MAXPATH] = {0}, resolved2[SIR_MAXPATH] = {0};
541
        if (GetFullPathNameA(path, SIR_MAXPATH, resolved1, NULL) &&
542
            GetFullPathNameA(iter->path, SIR_MAXPATH, resolved2, NULL))
543
            equal = 0 == StrCmpIA(resolved1, resolved2);
544
        _sir_selflog("returning %d for '%s' == '%s'", equal, resolved1, resolved2);
545
    }
546

547
    if (INVALID_HANDLE_VALUE != h1)
548
        CloseHandle(h1);
549

550
    if (INVALID_HANDLE_VALUE != h2)
551
        CloseHandle(h2);
552

553
    return equal;
554
#endif
555
}
556

557
bool _sir_fcache_pred_id(const void* match, const sirfile* iter) {
92,241✔
558
    const sirfileid* id = (const sirfileid*)match;
76,314✔
559
    return iter->id == *id;
92,241✔
560
}
561

562
sirfile* _sir_fcache_find(const sirfcache* sfc, const void* match, sir_fcache_pred pred) {
73,135✔
563
    if (!_sir_validptr(sfc) || !_sir_validptr(match) || !_sir_validfnptr(pred))
73,135✔
564
        return NULL;
×
565

566
    for (size_t n = 0; n < sfc->count; n++) {
232,697✔
567
        if (pred(match, sfc->files[n]))
196,108✔
568
            return sfc->files[n];
36,546✔
569
    }
570

571
    return NULL;
30,423✔
572
}
573

574
bool _sir_fcache_destroy(sirfcache* sfc) {
757✔
575
    if (!_sir_validptr(sfc))
757✔
576
        return false;
×
577

578
    while (sfc->count > 0) {
795✔
579
        size_t idx = sfc->count - 1;
38✔
580
        SIR_ASSERT(_sirfile_validate(sfc->files[idx]));
36✔
581
        _sirfile_destroy(&sfc->files[idx]);
38✔
582
        sfc->files[idx] = NULL;
38✔
583
        sfc->count--;
38✔
584
    }
585

586
    memset(sfc, 0, sizeof(sirfcache));
652✔
587
    return true;
757✔
588
}
589

590
bool _sir_fcache_dispatch(const sirfcache* sfc, sir_level level, sirbuf* buf,
2,125,928✔
591
    size_t* dispatched, size_t* wanted) {
592
    if (!_sir_validptr(sfc) || !_sir_validlevel(level) || !_sir_validptr(buf) ||
2,145,439✔
593
        !_sir_validptr(dispatched) || !_sir_validptr(wanted))
2,145,439✔
594
        return false;
×
595

596
    const char* write    = NULL;
2,106,417✔
597
    sir_options lastopts = 0U;
2,106,417✔
598

599
    *dispatched = 0;
2,125,928✔
600
    *wanted     = 0;
2,125,928✔
601

602
    for (size_t n = 0; n < sfc->count; n++) {
3,409,420✔
603
        SIR_ASSERT(_sirfile_validate(sfc->files[n]));
1,267,585✔
604

605
        if (!_sir_bittest(sfc->files[n]->levels, level)) {
1,283,492✔
606
            _sir_selflog("level %04"PRIx16" not set in level mask (%04"PRIx16
2,313✔
607
                         ") for file (path: '%s', id: %"PRIx32"); skipping",
608
                         level, sfc->files[n]->levels, sfc->files[n]->path,
609
                         sfc->files[n]->id);
610
            continue;
2,444✔
611
        }
612

613
        (*wanted)++;
1,281,048✔
614

615
        if (!write || sfc->files[n]->opts != lastopts) {
1,281,048✔
616
            write = _sir_format(false, sfc->files[n]->opts, buf);
1,152,843✔
617
            SIR_ASSERT(write);
1,144,587✔
618
            lastopts = sfc->files[n]->opts;
1,152,843✔
619
        }
620

621
        if (write && _sirfile_write(sfc->files[n], write)) {
1,281,048✔
622
            (*dispatched)++;
1,281,048✔
623
        } else {
624
            _sir_selflog("error: write to file (path: '%s', id: %"PRIx32") failed!",
×
625
                sfc->files[n]->path, sfc->files[n]->id);
626
        }
627
    }
628

629
    return (*dispatched == *wanted);
2,125,928✔
630
}
631

632
#if !defined(__WIN__)
633
void _sir_fflush(FILE* f) {
48✔
634
    if (_sir_validptr(f) && 0 != fflush(f))
48✔
635
        (void)_sir_handleerr(errno);
×
636
}
48✔
637
#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

© 2026 Coveralls, Inc