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

saitoha / libsixel / 25651620612

11 May 2026 04:12AM UTC coverage: 86.747% (-0.002%) from 86.749%
25651620612

push

github

saitoha
fix: gate img2sixel AFL unsetenv API

127374 of 257244 branches covered (49.51%)

153078 of 176465 relevant lines covered (86.75%)

9509470.93 hits per line

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

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

25
#if defined(HAVE_CONFIG_H)
26
# include "config.h"
27
#endif
28

29
#include "loader-manager.h"
30

31
#include "allocator.h"
32
#include "cms.h"
33
#include "compat_stub.h"
34
#include "factory.h"
35
#include "loader-common.h"
36
#include "loader-order-schema.h"
37
#include "options.h"
38
#include "sixel_atomic.h"
39

40
#include <ctype.h>
41
#include <limits.h>
42
#if HAVE_ERRNO_H
43
# include <errno.h>
44
#endif
45
#if HAVE_STDLIB_H
46
# include <stdlib.h>
47
#endif
48
#if HAVE_STRING_H
49
# include <string.h>
50
#endif
51

52
struct sixel_loader_manager {
53
    sixel_loader_manager_t base;
54
    sixel_atomic_u32_t ref;
55
    sixel_allocator_t *allocator;
56
    sixel_factory_t *factory;
57
    sixel_loader_component_interface_t **chain;
58
    size_t chain_count;
59
    size_t chain_capacity;
60
    int skip_predicate_gate;
61
    sixel_timeline_logger_t *timeline_logger;
62
    int *timeline_job_seq;
63
    sixel_loader_t *timeline_loader;
64
};
65

66
static sixel_loader_entry_t const g_sixel_loader_entries[] = {
67
#if HAVE_LIBPNG
68
    { "libpng", "loader/libpng", 1 },
69
#endif
70
#if HAVE_JPEG
71
    { "libjpeg", "loader/libjpeg", 1 },
72
#endif
73
#if HAVE_WEBP
74
    { "libwebp", "loader/libwebp", 1 },
75
#endif
76
#if HAVE_LIBTIFF
77
    { "libtiff", "loader/libtiff", 1 },
78
#endif
79
#if HAVE_LIBRSVG
80
    { "librsvg", "loader/librsvg", 1 },
81
#endif
82
    { "builtin", "loader/builtin", 1 },
83
#if HAVE_WIC
84
    { "wic", "loader/wic", 1 },
85
#endif
86
#if HAVE_COREGRAPHICS
87
    { "coregraphics", "loader/coregraphics", 1 },
88
#endif
89
#ifdef HAVE_GDK_PIXBUF2
90
    { "gdk-pixbuf2", "loader/gdk-pixbuf2", 1 },
91
#endif
92
#if HAVE_GD
93
    { "gd", "loader/gd", 1 },
94
#endif
95
#if HAVE_COREGRAPHICS && HAVE_QUICKLOOK
96
    { "quicklook", "loader/quicklook", 1 },
97
#endif
98
#if HAVE_FREEDESKTOP_THUMBNAILING
99
    { "gnome-thumbnailer", "loader/gnome-thumbnailer", 0 },
100
#endif
101
};
102

103

104
#if HAVE_WIC
105
static int
106
loader_manager_parse_positive_int(char const *text,
20✔
107
                                  size_t length,
108
                                  int *value_out)
109
{
110
    size_t index;
10✔
111
    int value;
10✔
112
    unsigned char digit;
10✔
113

114
    index = 0u;
20✔
115
    value = 0;
20✔
116
    digit = 0u;
20✔
117
    if (text == NULL || value_out == NULL || length == 0u) {
20✔
118
        return 0;
119
    }
120

121
    for (index = 0u; index < length; ++index) {
60✔
122
        digit = (unsigned char)text[index];
40✔
123
        if (digit < (unsigned char)'0' || digit > (unsigned char)'9') {
40✔
124
            return 0;
125
        }
126
        if (value > (INT_MAX - 9) / 10) {
40✔
127
            return 0;
128
        }
129
        value = value * 10 + (digit - (unsigned char)'0');
40✔
130
    }
4✔
131

132
    if (value <= 0) {
20✔
133
        return 0;
134
    }
135

136
    *value_out = value;
20✔
137
    return 1;
20✔
138
}
2✔
139
#endif
140

141
static int
142
loader_manager_parse_cms_engine(char const *text,
17,562✔
143
                                size_t length,
144
                                int *value_out)
145
{
146
    sixel_cms_engine_t engine;
9,353✔
147

148
    if (text == NULL || value_out == NULL || length == 0u) {
17,562!
149
        return 0;
150
    }
151
    if (strlen(text) != length) {
17,562!
152
        return 0;
153
    }
154
    if (!sixel_cms_engine_from_string(text, &engine)) {
17,562!
155
        return 0;
16✔
156
    }
157

158
    *value_out = (int)engine;
17,546✔
159
    return 1;
17,546✔
160
}
8,968✔
161

162
static int
163
loader_manager_read_env_cms_engine(char const *name,
1,325,664✔
164
                                   int fallback_value)
165
{
166
    char const *env_value;
680,406✔
167
    int parsed_value;
680,406✔
168

169
    env_value = NULL;
1,325,664✔
170
    parsed_value = fallback_value;
1,325,664✔
171
    if (name == NULL) {
1,325,664!
172
        return fallback_value;
173
    }
174

175
    env_value = sixel_compat_getenv(name);
1,325,664✔
176
    if (env_value == NULL || env_value[0] == '\0') {
1,325,664!
177
        return fallback_value;
967,056✔
178
    }
179

180
    if (!loader_manager_parse_cms_engine(env_value,
3,896!
181
                                         strlen(env_value),
1,738✔
182
                                         &parsed_value)) {
183
        return fallback_value;
16✔
184
    }
185

186
    return parsed_value;
2,142✔
187
}
563,058✔
188

189
static int
190
loader_manager_span_equals_nocase(char const *text,
5,743✔
191
                                  size_t length,
192
                                  char const *token)
193
{
194
    size_t index;
3,193✔
195
    unsigned char left;
3,193✔
196
    unsigned char right;
3,193✔
197

198
    index = 0u;
5,743✔
199
    left = 0u;
5,743✔
200
    right = 0u;
5,743✔
201
    if (text == NULL || token == NULL) {
5,743!
202
        return 0;
203
    }
204

205
    while (index < length && token[index] != '\0') {
16,965!
206
        left = (unsigned char)text[index];
13,454✔
207
        right = (unsigned char)token[index];
13,454✔
208
        if (tolower(left) != tolower(right)) {
13,454✔
209
            return 0;
1,945✔
210
        }
211
        ++index;
11,222✔
212
    }
213
    if (index != length || token[index] != '\0') {
3,511!
214
        return 0;
215
    }
216

217
    return 1;
2,874✔
218
}
3,763✔
219

220
static int
221
loader_manager_parse_orientation(char const *text,
3,259✔
222
                                 size_t length,
223
                                 int *value_out,
224
                                 int allow_numeric)
225
{
226
    if (text == NULL || value_out == NULL || length == 0u) {
3,259!
227
        return 0;
228
    }
229
    if (loader_manager_span_equals_nocase(text, length, "on")) {
3,259✔
230
        *value_out = 1;
1,477✔
231
        return 1;
1,477✔
232
    }
233
    if (loader_manager_span_equals_nocase(text, length, "off")) {
1,782!
234
        *value_out = 0;
1,734✔
235
        return 1;
1,734✔
236
    }
237
    if (allow_numeric &&
48!
238
        loader_manager_span_equals_nocase(text, length, "1")) {
48!
239
        *value_out = 1;
18✔
240
        return 1;
18✔
241
    }
242
    if (allow_numeric &&
30!
243
        loader_manager_span_equals_nocase(text, length, "0")) {
30!
244
        *value_out = 0;
18✔
245
        return 1;
18✔
246
    }
247

248
    return 0;
12✔
249
}
2,059✔
250

251
static int
252
loader_manager_parse_builtin_bmp_info40_mode(char const *text,
264✔
253
                                             size_t length,
254
                                             int *value_out,
255
                                             int allow_numeric)
256
{
257
    if (text == NULL || value_out == NULL || length == 0u) {
264!
258
        return 0;
259
    }
260
    if (loader_manager_span_equals_nocase(text, length, "auto")) {
264✔
261
        *value_out = SIXEL_LOADER_BUILTIN_BMP_INFO40_MODE_AUTO;
24✔
262
        return 1;
24✔
263
    }
264
    if (loader_manager_span_equals_nocase(text, length, "windows")) {
240✔
265
        *value_out = SIXEL_LOADER_BUILTIN_BMP_INFO40_MODE_WINDOWS;
120✔
266
        return 1;
120✔
267
    }
268
    if (loader_manager_span_equals_nocase(text, length, "os2")) {
120!
269
        *value_out = SIXEL_LOADER_BUILTIN_BMP_INFO40_MODE_OS2;
120✔
270
        return 1;
120✔
271
    }
272
    if (allow_numeric &&
×
273
        loader_manager_span_equals_nocase(text, length, "0")) {
×
274
        *value_out = SIXEL_LOADER_BUILTIN_BMP_INFO40_MODE_AUTO;
×
275
        return 1;
×
276
    }
277
    if (allow_numeric &&
×
278
        loader_manager_span_equals_nocase(text, length, "1")) {
×
279
        *value_out = SIXEL_LOADER_BUILTIN_BMP_INFO40_MODE_WINDOWS;
×
280
        return 1;
×
281
    }
282
    if (allow_numeric &&
×
283
        loader_manager_span_equals_nocase(text, length, "2")) {
×
284
        *value_out = SIXEL_LOADER_BUILTIN_BMP_INFO40_MODE_OS2;
×
285
        return 1;
×
286
    }
287

288
    return 0;
289
}
99✔
290

291
static int
292
loader_manager_read_env_orientation(char const *name, int fallback_value)
1,325,664✔
293
{
294
    char const *env_value;
680,406✔
295
    int parsed_value;
680,406✔
296

297
    env_value = NULL;
1,325,664✔
298
    parsed_value = fallback_value;
1,325,664✔
299
    if (name == NULL) {
1,325,664!
300
        return fallback_value;
301
    }
302

303
    env_value = sixel_compat_getenv(name);
1,325,664✔
304
    if (env_value == NULL || env_value[0] == '\0') {
1,325,664!
305
        return fallback_value;
966,464✔
306
    }
307
    if (!loader_manager_parse_orientation(env_value,
5,028!
308
                                          strlen(env_value),
1,914✔
309
                                          &parsed_value,
310
                                          1)) {
311
        return fallback_value;
12✔
312
    }
313

314
    return parsed_value;
3,102✔
315
}
563,058✔
316

317
static int
318
loader_manager_read_env_builtin_bmp_info40_mode(char const *name,
220,944✔
319
                                                int fallback_value)
320
{
321
    char const *env_value;
113,401✔
322
    int parsed_value;
113,401✔
323

324
    env_value = NULL;
220,944✔
325
    parsed_value = fallback_value;
220,944✔
326
    if (name == NULL) {
220,944!
327
        return fallback_value;
328
    }
329

330
    env_value = sixel_compat_getenv(name);
220,944✔
331
    if (env_value == NULL || env_value[0] == '\0') {
220,944!
332
        return fallback_value;
161,367✔
333
    }
334
    if (!loader_manager_parse_builtin_bmp_info40_mode(env_value,
264!
335
                                                       strlen(env_value),
72✔
336
                                                       &parsed_value,
337
                                                       1)) {
338
        return fallback_value;
339
    }
340

341
    return parsed_value;
192✔
342
}
93,843✔
343

344
#if HAVE_WIC
345
static int
346
loader_manager_read_env_positive_int(char const *name,
85,814✔
347
                                     int fallback_value)
348
{
349
    char const *env_value;
42,913✔
350
    char *endptr;
42,913✔
351
    long parsed;
42,913✔
352

353
    env_value = NULL;
85,814✔
354
    endptr = NULL;
85,814✔
355
    parsed = 0;
85,814✔
356
    if (name == NULL) {
85,814✔
357
        return fallback_value;
358
    }
359

360
    env_value = sixel_compat_getenv(name);
85,814✔
361
    if (env_value == NULL || env_value[0] == '\0') {
85,814✔
362
        return fallback_value;
42,881✔
363
    }
364

365
    errno = 0;
40✔
366
    parsed = strtol(env_value, &endptr, 10);
40✔
367
    if (errno != 0 || endptr == env_value || endptr == NULL ||
40✔
368
        endptr[0] != '\0' || parsed <= 0) {
40✔
369
        return fallback_value;
370
    }
371
    if (parsed > (long)INT_MAX) {
8✔
372
        parsed = (long)INT_MAX;
373
    }
374

375
    return (int)parsed;
24✔
376
}
8,589✔
377

378
static int
379
loader_manager_read_wic_ico_minsize_from_env(int fallback_value)
85,814✔
380
{
381
    char const *primary_name;
42,913✔
382
    char const *legacy_name;
42,913✔
383
    char const *primary_value;
42,913✔
384

385
    primary_name = "SIXEL_LOADER_WIC_ICO_MINSIZE";
85,814✔
386
    legacy_name = "SIXEL_LODER_WIC_ICO_MINSIZE";
85,814✔
387
    primary_value = sixel_compat_getenv(primary_name);
85,814✔
388

389
    if (primary_value != NULL && primary_value[0] != '\0') {
85,814✔
390
        return loader_manager_read_env_positive_int(primary_name,
22✔
391
                                                    fallback_value);
2✔
392
    }
393

394
    return loader_manager_read_env_positive_int(legacy_name, fallback_value);
85,794✔
395
}
8,589✔
396
#endif
397

398
static int
399
loader_manager_plan_contains(sixel_loader_entry_t const **plan,
208,779✔
400
                             size_t plan_length,
401
                             sixel_loader_entry_t const *entry)
402
{
403
    size_t index;
102,111✔
404

405
    index = 0u;
208,779✔
406
    for (index = 0u; index < plan_length; ++index) {
622,634!
407
        if (plan[index] == entry) {
375,398!
408
            return 1;
4,489✔
409
        }
410
    }
345,246✔
411

412
    return 0;
204,290✔
413
}
162,285✔
414

415
static int
416
loader_manager_token_matches(char const *token,
89,390✔
417
                             size_t token_length,
418
                             char const *name)
419
{
420
    size_t index;
54,498✔
421
    unsigned char left;
54,498✔
422
    unsigned char right;
54,498✔
423

424
    index = 0u;
89,390✔
425
    left = 0u;
89,390✔
426
    right = 0u;
89,390✔
427
    for (index = 0u; index < token_length && name[index] != '\0'; ++index) {
455,588!
428
        left = (unsigned char)token[index];
405,148✔
429
        right = (unsigned char)name[index];
405,148✔
430
        if (tolower(left) != tolower(right)) {
405,148✔
431
            return 0;
38,645✔
432
        }
433
    }
169,963✔
434

435
    if (index != token_length || name[index] != '\0') {
50,440!
436
        return 0;
437
    }
438

439
    return 1;
37,325✔
440
}
60,760✔
441

442
static sixel_loader_entry_t const *
443
loader_manager_lookup_token(char const *token,
50,440✔
444
                            size_t token_length,
445
                            sixel_loader_entry_t const *entries,
446
                            size_t entry_count)
447
{
448
    size_t index;
26,114✔
449

450
    index = 0u;
50,440✔
451
    for (index = 0u; index < entry_count; ++index) {
89,390!
452
        if (loader_manager_token_matches(token,
150,150!
453
                                         token_length,
60,760✔
454
                                         entries[index].name)) {
89,390✔
455
            return &entries[index];
37,325✔
456
        }
457
    }
38,385✔
458

459
    return NULL;
460
}
22,375✔
461

462
SIXELSTATUS
463
loader_manager_parse_loader_order(
49✔
464
    char const *order,
465
    sixel_option_argument_list_resolution_t *resolution)
466
{
467
    char diagnostic[128];
25✔
468

469
    diagnostic[0] = '\0';
49✔
470
    if (resolution == NULL) {
49!
471
        sixel_helper_set_additional_message(
×
472
            "loader_manager_parse_loader_order: resolution is null.");
473
        return SIXEL_BAD_ARGUMENT;
×
474
    }
475

476
    return sixel_loader_order_parse_and_validate(
49✔
477
        order,
18✔
478
        resolution,
18✔
479
        diagnostic,
18✔
480
        sizeof(diagnostic));
481
}
18✔
482

483
void
484
loader_manager_init_loader_suboptions(
220,943✔
485
    sixel_loader_suboptions_t *suboptions)
486
{
487
    int default_cms_engine;
113,400✔
488
    int default_orientation;
113,400✔
489

490
    if (suboptions == NULL) {
220,943✔
491
        return;
492
    }
493

494
    default_cms_engine = loader_manager_read_env_cms_engine(
220,943✔
495
        "SIXEL_LOADER_CMS_ENGINE",
496
        SIXEL_CMS_ENGINE_NONE);
497
    default_orientation = loader_manager_read_env_orientation(
220,943✔
498
        "SIXEL_LOADER_ORIENTATION",
499
        1);
500

501
    suboptions->libjpeg_enable_cms = 0;
220,943✔
502
    suboptions->libjpeg_cms_engine = loader_manager_read_env_cms_engine(
220,943✔
503
        "SIXEL_LOADER_LIBJPEG_CMS_ENGINE",
504
        default_cms_engine);
93,842✔
505
    suboptions->libjpeg_enable_orientation =
280,384✔
506
        loader_manager_read_env_orientation(
220,943✔
507
            "SIXEL_LOADER_LIBJPEG_ORIENTATION",
508
            default_orientation);
93,842✔
509
    suboptions->libpng_enable_cms = 0;
220,943✔
510
    suboptions->libpng_cms_engine = loader_manager_read_env_cms_engine(
220,943✔
511
        "SIXEL_LOADER_LIBPNG_CMS_ENGINE",
512
        default_cms_engine);
93,842✔
513
    suboptions->libpng_enable_orientation =
280,384✔
514
        loader_manager_read_env_orientation(
220,943✔
515
            "SIXEL_LOADER_LIBPNG_ORIENTATION",
516
            default_orientation);
93,842✔
517
    suboptions->libwebp_enable_cms = 0;
220,943✔
518
    suboptions->libwebp_cms_engine = loader_manager_read_env_cms_engine(
220,943✔
519
        "SIXEL_LOADER_LIBWEBP_CMS_ENGINE",
520
        default_cms_engine);
93,842✔
521
    suboptions->libwebp_enable_orientation =
280,384✔
522
        loader_manager_read_env_orientation(
220,943✔
523
            "SIXEL_LOADER_LIBWEBP_ORIENTATION",
524
            default_orientation);
93,842✔
525
    suboptions->coregraphics_enable_orientation =
280,384✔
526
        loader_manager_read_env_orientation(
220,943✔
527
            "SIXEL_LOADER_COREGRAPHICS_ORIENTATION",
528
            default_orientation);
93,842✔
529
    suboptions->libtiff_enable_cms = 0;
220,943✔
530
    suboptions->libtiff_cms_engine = loader_manager_read_env_cms_engine(
220,943✔
531
        "SIXEL_LOADER_LIBTIFF_CMS_ENGINE",
532
        default_cms_engine);
93,842✔
533
    suboptions->builtin_enable_cms = 0;
220,943✔
534
    suboptions->builtin_cms_engine = loader_manager_read_env_cms_engine(
220,943✔
535
        "SIXEL_LOADER_BUILTIN_CMS_ENGINE",
536
        default_cms_engine);
93,842✔
537
    suboptions->builtin_enable_orientation =
280,384✔
538
        loader_manager_read_env_orientation(
220,943✔
539
            "SIXEL_LOADER_BUILTIN_ORIENTATION",
540
            default_orientation);
93,842✔
541
    suboptions->builtin_bmp_info40_mode =
280,384✔
542
        loader_manager_read_env_builtin_bmp_info40_mode(
220,943✔
543
            "SIXEL_LOADER_BUILTIN_BMP_INFO40_MODE",
544
            SIXEL_LOADER_BUILTIN_BMP_INFO40_MODE_AUTO);
545
#if HAVE_WIC
546
    suboptions->wic_ico_minsize = loader_manager_read_wic_ico_minsize_from_env(
85,814✔
547
        0);
548
#else
549
    suboptions->wic_ico_minsize = 0;
135,129✔
550
#endif
551
}
93,842!
552

553
void
554
loader_manager_resolve_loader_suboptions(
110,407✔
555
    sixel_option_argument_list_resolution_t const *resolution,
556
    sixel_loader_suboptions_t *suboptions)
557
{
558
    size_t item_index;
56,666✔
559
    size_t assignment_index;
56,666✔
560
    sixel_option_argument_resolution_t const *item;
56,666✔
561
    char const *key_name;
56,666✔
562
    char const *value_text;
56,666✔
563
    size_t value_length;
56,666✔
564
    int parsed_value;
56,666✔
565

566
    item_index = 0u;
110,407✔
567
    assignment_index = 0u;
110,407✔
568
    item = NULL;
110,407✔
569
    key_name = NULL;
110,407✔
570
    value_text = NULL;
110,407✔
571
    value_length = 0u;
110,407✔
572
    parsed_value = 0;
110,407✔
573

574
    if (suboptions == NULL) {
110,407✔
575
        return;
35,457✔
576
    }
577

578
    loader_manager_init_loader_suboptions(suboptions);
110,407✔
579

580
    if (resolution == NULL) {
110,407✔
581
        return;
43,443✔
582
    }
583

584
    while (item_index < resolution->item_count) {
100,813!
585
        item = &resolution->items[item_index].resolution;
50,440✔
586
        if (item->base_def == NULL) {
50,440!
587
            ++item_index;
×
588
            continue;
×
589
        }
590
        assignment_index = 0u;
37,325✔
591
        while (assignment_index < item->assignment_count) {
66,081!
592
            key_name = item->assignments[assignment_index].resolved_key_name;
15,641✔
593
            value_text = item->assignments[assignment_index]
23,045✔
594
                .resolved_value_text;
7,404✔
595
            value_length = 0u;
15,641✔
596
            if (value_text != NULL) {
15,641!
597
                value_length = strlen(value_text);
15,641✔
598
            }
7,404✔
599
            if (key_name == NULL || value_text == NULL) {
15,641!
600
                ++assignment_index;
×
601
                continue;
×
602
            }
603
#if HAVE_WIC
604
            if (strcmp(item->base_def->name, "wic") == 0 &&
5,670✔
605
                strcmp(key_name, "ico_minsize") == 0 &&
38✔
606
                loader_manager_parse_positive_int(value_text,
22✔
607
                                                  value_length,
2✔
608
                                                  &parsed_value)) {
609
                suboptions->wic_ico_minsize = parsed_value;
20✔
610
                ++assignment_index;
20✔
611
                continue;
20✔
612
            }
613
#endif
614
            if (strcmp(item->base_def->name, "libpng") == 0 &&
16,221!
615
                       strcmp(key_name, "cms_engine") == 0 &&
645!
616
                       loader_manager_parse_cms_engine(value_text,
1,200✔
617
                                                       value_length,
600✔
618
                                                       &parsed_value)) {
619
                suboptions->libpng_cms_engine = parsed_value;
600✔
620
            } else if (strcmp(item->base_def->name, "libpng") == 0 &&
14,853!
621
                       strcmp(key_name, "orientation") == 0 &&
45!
622
                       loader_manager_parse_orientation(value_text,
90✔
623
                                                        value_length,
45✔
624
                                                        &parsed_value,
625
                                                        0)) {
626
                suboptions->libpng_enable_orientation = parsed_value;
45✔
627
            } else if (strcmp(item->base_def->name, "libjpeg") == 0 &&
14,260!
628
                       strcmp(key_name, "cms_engine") == 0 &&
48!
629
                       loader_manager_parse_cms_engine(value_text,
72✔
630
                                                       value_length,
36✔
631
                                                       &parsed_value)) {
632
                suboptions->libjpeg_cms_engine = parsed_value;
36✔
633
            } else if (strcmp(item->base_def->name, "libjpeg") == 0 &&
14,191!
634
                       strcmp(key_name, "orientation") == 0 &&
12!
635
                       loader_manager_parse_orientation(value_text,
24✔
636
                                                        value_length,
12✔
637
                                                        &parsed_value,
638
                                                        0)) {
639
                suboptions->libjpeg_enable_orientation = parsed_value;
12✔
640
            } else if (strcmp(item->base_def->name, "libwebp") == 0 &&
14,675!
641
                       strcmp(key_name, "cms_engine") == 0 &&
556!
642
                       loader_manager_parse_cms_engine(value_text,
1,064✔
643
                                                       value_length,
532✔
644
                                                       &parsed_value)) {
645
                suboptions->libwebp_cms_engine = parsed_value;
532✔
646
            } else if (strcmp(item->base_def->name, "libwebp") == 0 &&
14,155!
647
                       strcmp(key_name, "orientation") == 0 &&
24!
648
                       loader_manager_parse_orientation(value_text,
48✔
649
                                                        value_length,
24✔
650
                                                        &parsed_value,
651
                                                        0)) {
652
                suboptions->libwebp_enable_orientation = parsed_value;
24✔
653
            } else if (strcmp(item->base_def->name, "coregraphics") == 0 &&
13,671!
654
                       strcmp(key_name, "orientation") == 0 &&
64!
655
                       loader_manager_parse_orientation(value_text,
128✔
656
                                                        value_length,
64✔
657
                                                        &parsed_value,
658
                                                        0)) {
659
                suboptions->coregraphics_enable_orientation = parsed_value;
64✔
660
            } else if (strcmp(item->base_def->name, "libtiff") == 0 &&
14,135!
661
                       strcmp(key_name, "cms_engine") == 0 &&
414!
662
                       loader_manager_parse_cms_engine(value_text,
828✔
663
                                                       value_length,
414✔
664
                                                       &parsed_value)) {
665
                suboptions->libtiff_cms_engine = parsed_value;
414✔
666
            } else if (strcmp(item->base_def->name, "builtin") == 0 &&
19,394!
667
                       strcmp(key_name, "cms_engine") == 0 &&
22,068!
668
                       loader_manager_parse_cms_engine(value_text,
19,470✔
669
                                                       value_length,
5,648✔
670
                                                       &parsed_value)) {
671
                suboptions->builtin_cms_engine = parsed_value;
13,822✔
672
            } else if (strcmp(item->base_def->name, "builtin") == 0 &&
5,744!
673
                       strcmp(key_name, "bmp_info40_mode") == 0 &&
117!
674
                       loader_manager_parse_builtin_bmp_info40_mode(
72✔
675
                           value_text,
27✔
676
                           value_length,
27✔
677
                           &parsed_value,
678
                           0)) {
679
                suboptions->builtin_bmp_info40_mode = parsed_value;
72✔
680
            } else if (strcmp(item->base_def->name, "builtin") == 0 &&
27!
681
                       strcmp(key_name, "orientation") == 0 &&
×
682
                       loader_manager_parse_orientation(value_text,
×
683
                                                        value_length,
684
                                                        &parsed_value,
685
                                                        0)) {
686
                suboptions->builtin_enable_orientation = parsed_value;
×
687
            }
688
            ++assignment_index;
15,621✔
689
        }
690
        ++item_index;
50,440✔
691
    }
692
}
46,894!
693

694
size_t
695
loader_manager_build_plan_from_resolution(
110,407✔
696
    sixel_option_argument_list_resolution_t const *resolution,
697
    sixel_loader_entry_t const *entries,
698
    size_t entry_count,
699
    sixel_loader_entry_t const **plan,
700
    size_t plan_capacity)
701
{
702
    size_t plan_length;
56,666✔
703
    size_t index;
56,666✔
704
    sixel_loader_entry_t const *entry;
56,666✔
705
    size_t limit;
56,666✔
706
    int allow_fallback;
56,666✔
707
    sixel_option_argument_resolution_t const *item;
56,666✔
708

709
    plan_length = 0u;
110,407✔
710
    index = 0u;
110,407✔
711
    entry = NULL;
110,407✔
712
    limit = plan_capacity;
110,407✔
713
    allow_fallback = 1;
110,407✔
714
    item = NULL;
110,407✔
715

716
    if (resolution != NULL) {
110,407✔
717
        allow_fallback = !resolution->has_trailing_bang;
50,373✔
718
    }
22,317✔
719

720
    if (plan != NULL && plan_capacity > 0u && resolution != NULL) {
110,407!
721
        for (index = 0u; index < resolution->item_count; ++index) {
100,813!
722
            item = &resolution->items[index].resolution;
50,440✔
723
            if (item->base_def == NULL || item->base_def->name == NULL) {
50,440!
724
                continue;
×
725
            }
726
            entry = loader_manager_lookup_token(item->base_def->name,
72,815✔
727
                                                strlen(item->base_def->name),
37,325✔
728
                                                entries,
22,375✔
729
                                                entry_count);
22,375✔
730
            if (entry != NULL &&
85,863!
731
                !loader_manager_plan_contains(plan, plan_length, entry) &&
65,390!
732
                plan_length < limit) {
22,375✔
733
                plan[plan_length] = entry;
50,440✔
734
                ++plan_length;
50,440✔
735
            }
22,375✔
736
        }
22,375✔
737
    }
22,317✔
738

739
    if (allow_fallback && plan != NULL && limit > 0u) {
108,058!
740
        for (index = 0u; index < entry_count && plan_length < limit; ++index) {
311,932!
741
            entry = &entries[index];
245,586✔
742
            if (!entry->default_enabled) {
245,586✔
743
                continue;
42,486✔
744
            }
745
            if (!loader_manager_plan_contains(plan, plan_length, entry)) {
203,100✔
746
                plan[plan_length] = entry;
196,796✔
747
                ++plan_length;
196,796✔
748
            }
137,501✔
749
        }
139,910✔
750
    }
26,987✔
751

752
    return plan_length;
137,372✔
753
}
26,965✔
754

755
static size_t
756
loader_manager_find_entry_index(char const *name)
×
757
{
758
    size_t entry_count;
759
    size_t index;
760

761
    entry_count = sizeof(g_sixel_loader_entries)
×
762
        / sizeof(g_sixel_loader_entries[0]);
763
    index = 0u;
×
764
    if (name == NULL) {
×
765
        return entry_count;
766
    }
767
    for (index = 0u; index < entry_count; ++index) {
×
768
        if (g_sixel_loader_entries[index].name != NULL &&
×
769
            strcmp(g_sixel_loader_entries[index].name, name) == 0) {
×
770
            return index;
771
        }
772
    }
773

774
    return entry_count;
775
}
776

777
size_t
778
loader_manager_get_entries(sixel_loader_entry_t const **entries)
110,599✔
779
{
780
    if (entries != NULL) {
89,160!
781
        *entries = g_sixel_loader_entries;
89,160✔
782
    }
46,966✔
783
    return sizeof(g_sixel_loader_entries) / sizeof(g_sixel_loader_entries[0]);
110,599✔
784
}
785

786
int
787
loader_manager_entry_available(char const *name)
×
788
{
789
    size_t index;
790
    size_t entry_count;
791

792
    index = 0u;
×
793
    entry_count = loader_manager_get_entries(NULL);
×
794
    index = loader_manager_find_entry_index(name);
×
795
    return index < entry_count ? 1 : 0;
×
796
}
797

798
static void
799
sixel_loader_manager_clear_chain(sixel_loader_manager_t *manager)
220,944✔
800
{
801
    struct sixel_loader_manager *object;
113,401✔
802
    size_t index;
113,401✔
803

804
    object = NULL;
220,944✔
805
    index = 0u;
220,944✔
806
    if (manager == NULL) {
220,944✔
807
        return;
808
    }
809
    object = (struct sixel_loader_manager *)manager;
263,890✔
810
    for (index = 0u; index < object->chain_count; ++index) {
468,180!
811
        sixel_loader_component_unref(object->chain[index]);
247,236✔
812
    }
159,876✔
813
    if (object->chain != NULL) {
220,944✔
814
        sixel_allocator_free(object->allocator, object->chain);
110,407✔
815
    }
46,894✔
816
    object->chain = NULL;
220,944✔
817
    object->chain_count = 0u;
220,944✔
818
    object->chain_capacity = 0u;
220,944✔
819
}
93,843!
820

821
static SIXELSTATUS
822
sixel_loader_manager_append_chain(sixel_loader_manager_t *manager,
247,236✔
823
                                  sixel_loader_component_interface_t *loader)
824
{
825
    struct sixel_loader_manager *object;
143,714✔
826
    sixel_loader_component_interface_t **new_chain;
143,714✔
827
    size_t new_capacity;
143,714✔
828
    size_t index;
143,714✔
829

830
    object = NULL;
247,236✔
831
    new_chain = NULL;
247,236✔
832
    new_capacity = 0u;
247,236✔
833
    index = 0u;
247,236✔
834
    if (manager == NULL || loader == NULL) {
247,236!
835
        return SIXEL_BAD_ARGUMENT;
836
    }
837
    object = (struct sixel_loader_manager *)manager;
247,236✔
838
    if (object->chain_count == object->chain_capacity) {
247,236!
839
        new_capacity = object->chain_capacity == 0u
109,698✔
840
            ? 8u : object->chain_capacity * 2u;
63,513!
841
        new_chain = (sixel_loader_component_interface_t **)
76,595✔
842
            sixel_allocator_malloc(
131,846✔
843
                object->allocator,
46,894✔
844
                new_capacity * sizeof(*new_chain));
89,677✔
845
        if (new_chain == NULL) {
110,407!
846
            return SIXEL_BAD_ALLOCATION;
847
        }
848
        for (index = 0u; index < object->chain_count; ++index) {
110,407!
849
            new_chain[index] = object->chain[index];
×
850
        }
851
        if (object->chain != NULL) {
110,407!
852
            sixel_allocator_free(object->allocator, object->chain);
×
853
        }
854
        object->chain = new_chain;
110,407✔
855
        object->chain_capacity = new_capacity;
110,407✔
856
    }
46,894✔
857
    object->chain[object->chain_count] = loader;
247,236✔
858
    ++object->chain_count;
247,236✔
859
    sixel_loader_component_ref(loader);
247,236✔
860
    return SIXEL_OK;
247,236✔
861
}
159,876✔
862

863
static SIXELSTATUS
864
sixel_loader_manager_apply_component_options(
247,236✔
865
    sixel_loader_component_interface_t *component,
866
    sixel_loader_manager_build_request_t const *request)
867
{
868
    typedef struct loader_component_option_entry {
42,946✔
869
        int option;
870
        char const *name;
871
    } loader_component_option_entry_t;
872

873
    loader_component_option_entry_t const options[] = {
247,236✔
874
        { SIXEL_LOADER_OPTION_REQUIRE_STATIC, "require-static" },
875
        { SIXEL_LOADER_OPTION_USE_PALETTE, "use-palette" },
876
        { SIXEL_LOADER_OPTION_REQCOLORS, "reqcolors" },
877
        { SIXEL_LOADER_OPTION_BGCOLOR, "bgcolor" },
878
        { SIXEL_LOADER_OPTION_LOOP_CONTROL, "loop-control" },
879
        { SIXEL_LOADER_OPTION_START_FRAME_NO, "start-frame-no" }
880
    };
881
    void const *value;
143,714✔
882
    char message[128];
143,714✔
883
    size_t index;
143,714✔
884
    SIXELSTATUS status;
143,714✔
885
    int suboption_value;
143,714✔
886
    char const *component_name;
143,714✔
887

888
    value = NULL;
247,236✔
889
    message[0] = '\0';
247,236✔
890
    index = 0u;
247,236✔
891
    status = SIXEL_FALSE;
247,236✔
892
    suboption_value = 0;
247,236✔
893
    component_name = NULL;
247,236✔
894
    if (component == NULL || request == NULL) {
247,236!
895
        return SIXEL_BAD_ARGUMENT;
896
    }
897
    component_name = sixel_loader_component_get_name(component);
247,236✔
898

899
    for (index = 0u; index < sizeof(options) / sizeof(options[0]); ++index) {
1,773,598!
900
        switch (options[index].option) {
1,483,416!
901
        case SIXEL_LOADER_OPTION_REQUIRE_STATIC:
87,360✔
902
            value = &request->require_static;
247,236✔
903
            break;
247,236✔
904
        case SIXEL_LOADER_OPTION_USE_PALETTE:
87,360✔
905
            value = &request->use_palette;
247,236✔
906
            break;
247,236✔
907
        case SIXEL_LOADER_OPTION_REQCOLORS:
87,360✔
908
            value = &request->reqcolors;
247,236✔
909
            break;
247,236✔
910
        case SIXEL_LOADER_OPTION_BGCOLOR:
87,360✔
911
            value = request->has_bgcolor ? request->bgcolor : NULL;
247,236✔
912
            break;
204,290✔
913
        case SIXEL_LOADER_OPTION_LOOP_CONTROL:
87,360✔
914
            value = &request->loop_control;
247,236✔
915
            break;
247,236✔
916
        case SIXEL_LOADER_OPTION_START_FRAME_NO:
87,360✔
917
            value = request->has_start_frame_no
226,030✔
918
                ? &request->start_frame_no : NULL;
92,454✔
919
            break;
204,290✔
920
        default:
921
            value = NULL;
922
            break;
923
        }
924

925
        status = sixel_loader_component_setopt(component,
2,442,672✔
926
                                               options[index].option,
1,225,740✔
927
                                               value);
959,256✔
928
        if (SIXEL_FAILED(status)) {
1,483,416!
929
            (void)sixel_compat_snprintf(message,
×
930
                                        sizeof(message),
931
                                        "sixel_loader_manager_build_chain: "
932
                                        "failed to apply loader option "
933
                                        "'%s'.",
934
                                        options[index].name);
×
935
            sixel_helper_set_additional_message(message);
×
936
            return status;
×
937
        }
938
    }
959,256✔
939

940
    status = sixel_loader_component_setopt(
290,182✔
941
        component,
159,876✔
942
        SIXEL_LOADER_COMPONENT_OPTION_BGCOLOR_SOURCE,
943
        &request->bgcolor_source);
247,236✔
944
    if (SIXEL_FAILED(status)) {
247,236!
945
        sixel_helper_set_additional_message(
×
946
            "sixel_loader_manager_build_chain: failed to apply loader option "
947
            "'bgcolor-source'.");
948
        return status;
×
949
    }
950

951
    if (request->suboptions == NULL) {
247,236!
952
        return SIXEL_OK;
953
    }
954

955
#if HAVE_WIC
956
    suboption_value = request->suboptions->wic_ico_minsize;
69,375✔
957
    status = sixel_loader_component_setopt(
69,375✔
958
        component,
6,947✔
959
        SIXEL_LOADER_COMPONENT_OPTION_WIC_ICO_MINSIZE,
960
        &suboption_value);
961
    if (SIXEL_FAILED(status)) {
69,375✔
962
        sixel_helper_set_additional_message(
963
            "sixel_loader_manager_build_chain: failed to apply loader option "
964
            "'wic-ico-minsize'.");
965
        return status;
966
    }
967
#endif
968

969
    suboption_value = request->suboptions->libpng_enable_cms;
247,236✔
970
    status = sixel_loader_component_setopt(
247,236✔
971
        component,
159,876✔
972
        SIXEL_LOADER_COMPONENT_OPTION_LIBPNG_ENABLE_CMS,
973
        &suboption_value);
974
    if (SIXEL_FAILED(status)) {
247,236!
975
        sixel_helper_set_additional_message(
×
976
            "sixel_loader_manager_build_chain: failed to apply loader option "
977
            "'libpng-enable-cms'.");
978
        return status;
×
979
    }
980

981
    suboption_value = request->suboptions->libjpeg_enable_cms;
247,236✔
982
    status = sixel_loader_component_setopt(
247,236✔
983
        component,
159,876✔
984
        SIXEL_LOADER_COMPONENT_OPTION_LIBJPEG_ENABLE_CMS,
985
        &suboption_value);
986
    if (SIXEL_FAILED(status)) {
247,236!
987
        sixel_helper_set_additional_message(
×
988
            "sixel_loader_manager_build_chain: failed to apply loader option "
989
            "'libjpeg-enable-cms'.");
990
        return status;
×
991
    }
992

993
    suboption_value = request->suboptions->libwebp_enable_cms;
247,236✔
994
    status = sixel_loader_component_setopt(
247,236✔
995
        component,
159,876✔
996
        SIXEL_LOADER_COMPONENT_OPTION_LIBWEBP_ENABLE_CMS,
997
        &suboption_value);
998
    if (SIXEL_FAILED(status)) {
247,236!
999
        sixel_helper_set_additional_message(
×
1000
            "sixel_loader_manager_build_chain: failed to apply loader option "
1001
            "'libwebp-enable-cms'.");
1002
        return status;
×
1003
    }
1004

1005
    suboption_value = request->suboptions->libtiff_enable_cms;
247,236✔
1006
    status = sixel_loader_component_setopt(
247,236✔
1007
        component,
159,876✔
1008
        SIXEL_LOADER_COMPONENT_OPTION_LIBTIFF_ENABLE_CMS,
1009
        &suboption_value);
1010
    if (SIXEL_FAILED(status)) {
247,236!
1011
        sixel_helper_set_additional_message(
×
1012
            "sixel_loader_manager_build_chain: failed to apply loader option "
1013
            "'libtiff-enable-cms'.");
1014
        return status;
×
1015
    }
1016

1017
    suboption_value = request->suboptions->builtin_enable_cms;
247,236✔
1018
    status = sixel_loader_component_setopt(
247,236✔
1019
        component,
159,876✔
1020
        SIXEL_LOADER_COMPONENT_OPTION_BUILTIN_ENABLE_CMS,
1021
        &suboption_value);
1022
    if (SIXEL_FAILED(status)) {
247,236!
1023
        sixel_helper_set_additional_message(
×
1024
            "sixel_loader_manager_build_chain: failed to apply loader option "
1025
            "'builtin-enable-cms'.");
1026
        return status;
×
1027
    }
1028

1029
    suboption_value = request->suboptions->builtin_bmp_info40_mode;
247,236✔
1030
    status = sixel_loader_component_setopt(
247,236✔
1031
        component,
159,876✔
1032
        SIXEL_LOADER_COMPONENT_OPTION_BUILTIN_BMP_INFO40_MODE,
1033
        &suboption_value);
1034
    if (SIXEL_FAILED(status)) {
247,236!
1035
        sixel_helper_set_additional_message(
×
1036
            "sixel_loader_manager_build_chain: failed to apply loader option "
1037
            "'builtin-bmp-info40-mode'.");
1038
        return status;
×
1039
    }
1040

1041
    suboption_value = request->suboptions->builtin_enable_orientation;
247,236✔
1042
    status = sixel_loader_component_setopt(
247,236✔
1043
        component,
159,876✔
1044
        SIXEL_LOADER_COMPONENT_OPTION_BUILTIN_ENABLE_ORIENTATION,
1045
        &suboption_value);
1046
    if (SIXEL_FAILED(status)) {
247,236!
1047
        sixel_helper_set_additional_message(
×
1048
            "sixel_loader_manager_build_chain: failed to apply loader option "
1049
            "'builtin-enable-orientation'.");
1050
        return status;
×
1051
    }
1052

1053
    suboption_value = request->suboptions->libjpeg_enable_orientation;
247,236✔
1054
    status = sixel_loader_component_setopt(
247,236✔
1055
        component,
159,876✔
1056
        SIXEL_LOADER_COMPONENT_OPTION_LIBJPEG_ENABLE_ORIENTATION,
1057
        &suboption_value);
1058
    if (SIXEL_FAILED(status)) {
247,236!
1059
        sixel_helper_set_additional_message(
×
1060
            "sixel_loader_manager_build_chain: failed to apply loader option "
1061
            "'libjpeg-enable-orientation'.");
1062
        return status;
×
1063
    }
1064

1065
    suboption_value = request->suboptions->libpng_enable_orientation;
247,236✔
1066
    status = sixel_loader_component_setopt(
247,236✔
1067
        component,
159,876✔
1068
        SIXEL_LOADER_COMPONENT_OPTION_LIBPNG_ENABLE_ORIENTATION,
1069
        &suboption_value);
1070
    if (SIXEL_FAILED(status)) {
247,236!
1071
        sixel_helper_set_additional_message(
×
1072
            "sixel_loader_manager_build_chain: failed to apply loader option "
1073
            "'libpng-enable-orientation'.");
1074
        return status;
×
1075
    }
1076

1077
    suboption_value = request->suboptions->libwebp_enable_orientation;
247,236✔
1078
    status = sixel_loader_component_setopt(
247,236✔
1079
        component,
159,876✔
1080
        SIXEL_LOADER_COMPONENT_OPTION_LIBWEBP_ENABLE_ORIENTATION,
1081
        &suboption_value);
1082
    if (SIXEL_FAILED(status)) {
247,236!
1083
        sixel_helper_set_additional_message(
×
1084
            "sixel_loader_manager_build_chain: failed to apply loader option "
1085
            "'libwebp-enable-orientation'.");
1086
        return status;
×
1087
    }
1088

1089
    suboption_value = request->suboptions->coregraphics_enable_orientation;
247,236✔
1090
    status = sixel_loader_component_setopt(
247,236✔
1091
        component,
159,876✔
1092
        SIXEL_LOADER_COMPONENT_OPTION_COREGRAPHICS_ENABLE_ORIENTATION,
1093
        &suboption_value);
1094
    if (SIXEL_FAILED(status)) {
247,236!
1095
        sixel_helper_set_additional_message(
×
1096
            "sixel_loader_manager_build_chain: failed to apply loader option "
1097
            "'coregraphics-enable-orientation'.");
1098
        return status;
×
1099
    }
1100

1101
    if (component_name != NULL && strcmp(component_name, "libpng") == 0) {
247,236!
1102
        suboption_value = request->suboptions->libpng_cms_engine;
11,023✔
1103
    } else if (component_name != NULL &&
247,236!
1104
               strcmp(component_name, "libjpeg") == 0) {
236,213!
1105
        suboption_value = request->suboptions->libjpeg_cms_engine;
10,315✔
1106
    } else if (component_name != NULL &&
236,213!
1107
               strcmp(component_name, "libwebp") == 0) {
225,898!
1108
        suboption_value = request->suboptions->libwebp_cms_engine;
14,170✔
1109
    } else if (component_name != NULL &&
225,898!
1110
               strcmp(component_name, "libtiff") == 0) {
211,728!
1111
        suboption_value = request->suboptions->libtiff_cms_engine;
10,645✔
1112
    } else if (component_name != NULL &&
282,894!
1113
               (strcmp(component_name, "builtin") == 0 ||
201,083✔
1114
                strcmp(component_name, "gnome-thumbnailer") == 0)) {
95,549!
1115
        suboption_value = request->suboptions->builtin_cms_engine;
105,834✔
1116
    } else {
42,717✔
1117
        suboption_value = 0;
95,249✔
1118
    }
1119
    status = sixel_loader_component_setopt(
247,236✔
1120
        component,
159,876✔
1121
        SIXEL_LOADER_COMPONENT_OPTION_CMS_ENGINE,
1122
        &suboption_value);
1123
    if (SIXEL_FAILED(status)) {
247,236!
1124
        sixel_helper_set_additional_message(
×
1125
            "sixel_loader_manager_build_chain: failed to apply loader option "
1126
            "'cms-engine'.");
1127
        return status;
×
1128
    }
1129

1130
    return SIXEL_OK;
204,290✔
1131
}
159,876✔
1132

1133
static void
1134
sixel_loader_manager_format_worker_name(char *worker,
112,293✔
1135
                                        size_t worker_size,
1136
                                        char const *name)
1137
{
1138
    char const *backend_name;
57,750✔
1139

1140
    backend_name = NULL;
112,293✔
1141
    if (worker == NULL || worker_size == 0u) {
112,293!
1142
        return;
1143
    }
1144

1145
    backend_name = name != NULL && name[0] != '\0' ? name : "unknown";
112,293!
1146
    (void)sixel_compat_snprintf(worker,
160,542✔
1147
                                worker_size,
48,249✔
1148
                                "loader/%s",
1149
                                backend_name);
48,249✔
1150
}
48,249!
1151

1152
static int
1153
sixel_loader_manager_status_allows_fallback(SIXELSTATUS status)
10,031✔
1154
{
1155
    switch (status) {
10,031!
1156
    case SIXEL_FALSE:
2,648✔
1157
    case SIXEL_BAD_INPUT:
1158
    case SIXEL_JPEG_ERROR:
1159
    case SIXEL_PNG_ERROR:
1160
    case SIXEL_WEBP_ERROR:
1161
    case SIXEL_TIFF_ERROR:
1162
    case SIXEL_GDK_ERROR:
1163
    case SIXEL_GD_ERROR:
1164
    case SIXEL_STBI_ERROR:
1165
    case SIXEL_STBIW_ERROR:
1166
    case SIXEL_COM_ERROR:
1167
    case SIXEL_WIC_ERROR:
1168
        return 1;
7,053✔
1169
    default:
168✔
1170
        break;
568✔
1171
    }
1172

1173
    return 0;
568✔
1174
}
4,658✔
1175

1176
static void
1177
sixel_loader_manager_ref_impl(sixel_loader_manager_t *manager)
×
1178
{
1179
    struct sixel_loader_manager *object;
1180

1181
    object = NULL;
×
1182
    if (manager == NULL) {
×
1183
        return;
1184
    }
1185
    object = (struct sixel_loader_manager *)manager;
×
1186
    (void)sixel_atomic_fetch_add_u32(&object->ref, 1u);
×
1187
}
×
1188

1189
static void
1190
sixel_loader_manager_unref_impl(sixel_loader_manager_t *manager)
110,537✔
1191
{
1192
    struct sixel_loader_manager *object;
56,735✔
1193
    unsigned int previous;
56,735✔
1194

1195
    object = NULL;
110,537✔
1196
    previous = 0u;
110,537✔
1197
    if (manager == NULL) {
110,537✔
1198
        return;
1199
    }
1200
    object = (struct sixel_loader_manager *)manager;
110,537✔
1201
    previous = sixel_atomic_fetch_sub_u32(&object->ref, 1u);
110,537!
1202
    if (previous != 1u) {
110,537!
1203
        if (previous == 0u) {
×
1204
            (void)sixel_atomic_fetch_add_u32(&object->ref, 1u);
×
1205
        }
1206
        return;
×
1207
    }
1208
    sixel_loader_manager_clear_chain(manager);
110,537✔
1209
    if (object->factory != NULL && object->factory->vtbl != NULL &&
110,537!
1210
        object->factory->vtbl->unref != NULL) {
110,537!
1211
        object->factory->vtbl->unref(object->factory);
110,537✔
1212
    }
46,949✔
1213
    sixel_allocator_unref(object->allocator);
110,537✔
1214
    sixel_allocator_free(object->allocator, object);
110,537✔
1215
}
46,949!
1216

1217
static SIXELSTATUS
1218
sixel_loader_manager_build_chain_impl(
110,407✔
1219
    sixel_loader_manager_t *manager,
1220
    sixel_loader_manager_build_request_t const *request)
1221
{
1222
    struct sixel_loader_manager *object;
56,666✔
1223
    sixel_loader_entry_t const *entries;
56,666✔
1224
    sixel_loader_entry_t const **plan;
56,666✔
1225
    sixel_loader_entry_t const *entry;
56,666✔
1226
    sixel_loader_component_interface_t *loader;
56,666✔
1227
    SIXELSTATUS status;
56,666✔
1228
    size_t entry_count;
56,666✔
1229
    size_t plan_length;
56,666✔
1230
    size_t index;
56,666✔
1231

1232
    object = NULL;
110,407✔
1233
    entries = NULL;
110,407✔
1234
    plan = NULL;
110,407✔
1235
    entry = NULL;
110,407✔
1236
    loader = NULL;
110,407✔
1237
    status = SIXEL_FALSE;
110,407✔
1238
    entry_count = 0u;
110,407✔
1239
    plan_length = 0u;
110,407✔
1240
    index = 0u;
110,407✔
1241
    if (manager == NULL) {
110,407!
1242
        return SIXEL_BAD_ARGUMENT;
1243
    }
1244
    object = (struct sixel_loader_manager *)manager;
110,407✔
1245
    object->skip_predicate_gate = 0;
110,407✔
1246
    object->timeline_logger = request != NULL
140,108!
1247
        ? request->timeline_logger : NULL;
110,407!
1248
    object->timeline_job_seq = request != NULL
110,407!
1249
        ? request->timeline_job_seq : NULL;
110,407!
1250
    object->timeline_loader = request != NULL
80,706!
1251
        ? request->timeline_loader : NULL;
110,407!
1252

1253
    entry_count = loader_manager_get_entries(&entries);
110,407✔
1254
    if (entry_count == 0u || entries == NULL) {
110,407!
1255
        sixel_helper_set_additional_message(
1256
            "sixel_loader_manager_build_chain: no loader entry.");
1257
        return SIXEL_LOGIC_ERROR;
1258
    }
1259

1260
    plan = (sixel_loader_entry_t const **)sixel_allocator_malloc(
110,407✔
1261
        object->allocator,
46,894✔
1262
        entry_count * sizeof(*plan));
68,238✔
1263
    if (plan == NULL) {
110,407!
1264
        return SIXEL_BAD_ALLOCATION;
1265
    }
1266
    plan_length = loader_manager_build_plan_from_resolution(
110,407!
1267
        request != NULL ? request->resolution : NULL,
46,894!
1268
        entries,
46,894✔
1269
        entry_count,
46,894✔
1270
        plan,
46,894✔
1271
        entry_count);
46,894✔
1272
    if (plan_length == 0u) {
110,407!
1273
        sixel_allocator_free(object->allocator, plan);
×
1274
        sixel_helper_set_additional_message(
×
1275
            "sixel_loader_manager_build_chain: no selectable loader.");
1276
        return SIXEL_BAD_ARGUMENT;
×
1277
    }
1278

1279
    sixel_loader_manager_clear_chain(manager);
110,407✔
1280
    for (index = 0u; index < plan_length; ++index) {
387,344!
1281
        entry = plan[index];
247,236✔
1282
        if (entry == NULL || entry->classid == NULL) {
247,236!
1283
            continue;
×
1284
        }
1285
        status = object->factory->vtbl->create(object->factory,
407,112✔
1286
                                               entry->classid,
204,290✔
1287
                                               object->allocator,
159,876✔
1288
                                               (void **)&loader);
1289
        if (SIXEL_FAILED(status)) {
247,236!
1290
            sixel_loader_manager_clear_chain(manager);
×
1291
            sixel_allocator_free(object->allocator, plan);
×
1292
            return status;
×
1293
        }
1294
        if (request != NULL) {
247,236!
1295
            status = sixel_loader_manager_apply_component_options(loader,
407,112✔
1296
                                                                  request);
159,876✔
1297
            if (SIXEL_FAILED(status)) {
247,236!
1298
                sixel_loader_component_unref(loader);
×
1299
                loader = NULL;
×
1300
                sixel_loader_manager_clear_chain(manager);
×
1301
                sixel_allocator_free(object->allocator, plan);
×
1302
                return status;
×
1303
            }
1304
        }
159,876✔
1305
        status = sixel_loader_manager_append_chain(manager, loader);
247,236✔
1306
        sixel_loader_component_unref(loader);
247,236✔
1307
        loader = NULL;
247,236✔
1308
        if (SIXEL_FAILED(status)) {
247,236!
1309
            sixel_loader_manager_clear_chain(manager);
×
1310
            sixel_allocator_free(object->allocator, plan);
×
1311
            return status;
×
1312
        }
1313
    }
159,876✔
1314
    sixel_allocator_free(object->allocator, plan);
110,407✔
1315
    if (object->chain_count == 0u) {
110,407!
1316
        sixel_helper_set_additional_message(
×
1317
            "sixel_loader_manager_build_chain: empty chain.");
1318
        return SIXEL_BAD_ARGUMENT;
×
1319
    }
1320
    if (request != NULL && request->skip_predicate_gate != 0) {
110,407!
1321
        object->skip_predicate_gate = 1;
44,004✔
1322
    }
19,850✔
1323
    return SIXEL_OK;
80,706✔
1324
}
46,894✔
1325

1326
static SIXELSTATUS
1327
sixel_loader_manager_load_impl(
110,407✔
1328
    sixel_loader_manager_t *manager,
1329
    sixel_chunk_t const *chunk,
1330
    sixel_loader_component_interface_t **selected_loader,
1331
    sixel_load_image_function fn_load,
1332
    void *load_context)
1333
{
1334
    struct sixel_loader_manager *object;
56,666✔
1335
    SIXELSTATUS status;
56,666✔
1336
    sixel_loader_component_interface_t *loader;
56,666✔
1337
    char const *name;
56,666✔
1338
    char worker[96];
56,666✔
1339
    size_t index;
56,666✔
1340
    int enforce_predicate;
56,666✔
1341

1342
    object = NULL;
110,407✔
1343
    status = SIXEL_FALSE;
110,407✔
1344
    loader = NULL;
110,407✔
1345
    name = NULL;
110,407✔
1346
    worker[0] = '\0';
110,407✔
1347
    index = 0u;
110,407✔
1348
    enforce_predicate = 0;
110,407✔
1349
    if (selected_loader != NULL) {
110,407!
1350
        *selected_loader = NULL;
×
1351
    }
1352
    if (manager == NULL || chunk == NULL || fn_load == NULL) {
110,407!
1353
        sixel_helper_set_additional_message(
×
1354
            "sixel_loader_manager_load: invalid argument.");
1355
        return SIXEL_BAD_ARGUMENT;
×
1356
    }
1357
    object = (struct sixel_loader_manager *)manager;
110,407✔
1358
    if (object->chain == NULL || object->chain_count == 0u) {
110,407!
1359
        sixel_helper_set_additional_message(
×
1360
            "sixel_loader_manager_load: chain is empty.");
1361
        return SIXEL_BAD_ARGUMENT;
×
1362
    }
1363
    enforce_predicate = object->skip_predicate_gate ? 0 : 1;
110,407✔
1364
    for (index = 0u; index < object->chain_count; ++index) {
159,490!
1365
        loader = object->chain[index];
151,913✔
1366
        if (loader == NULL) {
151,913!
1367
            continue;
×
1368
        }
1369
        if (enforce_predicate &&
191,803!
1370
            !sixel_loader_component_predicate(loader, chunk)) {
107,909✔
1371
            continue;
39,620✔
1372
        }
1373
        name = sixel_loader_component_get_name(loader);
112,293✔
1374
        sixel_loader_manager_format_worker_name(worker,
160,542✔
1375
                                                sizeof(worker),
1376
                                                name);
48,249✔
1377
        loader_trace_try(name);
112,293✔
1378
        sixel_loader_timeline_candidate_select_start(
112,293✔
1379
            object->timeline_loader,
48,249✔
1380
            worker);
48,249✔
1381
        loader_timeline_scope_begin(object->timeline_logger,
160,542✔
1382
                                    worker,
48,249✔
1383
                                    object->timeline_job_seq);
48,249✔
1384
        status = sixel_loader_component_load(loader,
160,542✔
1385
                                             chunk,
48,249✔
1386
                                             fn_load,
48,249✔
1387
                                             load_context);
48,249✔
1388
        loader_timeline_scope_end();
112,293✔
1389
        sixel_loader_timeline_candidate_select_finish(
112,293✔
1390
            object->timeline_loader,
48,249✔
1391
            status);
48,249✔
1392
        loader_trace_result(name, status);
112,293✔
1393
        if (status == SIXEL_OK) {
112,293✔
1394
            if (selected_loader != NULL) {
102,228!
1395
                *selected_loader = loader;
×
1396
            }
1397
            return SIXEL_OK;
102,228✔
1398
        }
1399
        if (SIXEL_SUCCEEDED(status) ||
12,881!
1400
                !sixel_loader_manager_status_allows_fallback(status)) {
10,031✔
1401
            return status;
451✔
1402
        }
1403
        /*
1404
         * A failed decode rejects only the current candidate.  The next
1405
         * backend may still be able to parse the same byte stream, especially
1406
         * when a forced order probes an optional external decoder first.
1407
         */
1408
    }
4,405✔
1409

1410
    return status;
5,462✔
1411
}
46,894✔
1412

1413
static sixel_loader_manager_vtbl_t const g_sixel_loader_manager_vtbl = {
1414
    sixel_loader_manager_ref_impl,
1415
    sixel_loader_manager_unref_impl,
1416
    sixel_loader_manager_build_chain_impl,
1417
    sixel_loader_manager_load_impl
1418
};
1419

1420
SIXELSTATUS
1421
sixel_loader_manager_new(sixel_allocator_t *allocator,
110,537✔
1422
                         void **manager)
1423
{
1424
    struct sixel_loader_manager *object;
56,735✔
1425
    SIXELSTATUS status;
56,735✔
1426

1427
    object = NULL;
110,537✔
1428
    status = SIXEL_FALSE;
110,537✔
1429
    if (allocator == NULL || manager == NULL) {
110,537!
1430
        return SIXEL_BAD_ARGUMENT;
1431
    }
1432
    *manager = NULL;
110,537✔
1433

1434
    object = (struct sixel_loader_manager *)sixel_allocator_malloc(
110,537✔
1435
        allocator,
46,949✔
1436
        sizeof(*object));
1437
    if (object == NULL) {
110,537!
1438
        return SIXEL_BAD_ALLOCATION;
1439
    }
1440
    object->base.vtbl = &g_sixel_loader_manager_vtbl;
110,537✔
1441
    object->ref = 1u;
110,537✔
1442
    object->allocator = allocator;
110,537✔
1443
    object->factory = NULL;
110,537✔
1444
    object->chain = NULL;
110,537✔
1445
    object->chain_count = 0u;
110,537✔
1446
    object->chain_capacity = 0u;
110,537✔
1447
    object->skip_predicate_gate = 0;
110,537✔
1448
    object->timeline_logger = NULL;
110,537✔
1449
    object->timeline_job_seq = NULL;
110,537✔
1450
    object->timeline_loader = NULL;
110,537✔
1451
    sixel_allocator_ref(allocator);
110,537✔
1452

1453
    status = sixel_factory_get_default((void **)&object->factory);
110,537✔
1454
    if (SIXEL_FAILED(status)) {
110,537!
1455
        sixel_allocator_unref(object->allocator);
×
1456
        sixel_allocator_free(allocator, object);
×
1457
        return status;
×
1458
    }
1459

1460
    *manager = &object->base;
110,537✔
1461
    return SIXEL_OK;
110,537✔
1462
}
46,949✔
1463

1464
/* emacs Local Variables:      */
1465
/* emacs mode: c               */
1466
/* emacs tab-width: 4          */
1467
/* emacs indent-tabs-mode: nil */
1468
/* emacs c-basic-offset: 4     */
1469
/* emacs End:                  */
1470
/* vim: set expandtab ts=4 sts=4 sw=4 : */
1471
/* EOF */
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