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

saitoha / libsixel / 25410478234

06 May 2026 12:39AM UTC coverage: 85.548% (-0.02%) from 85.564%
25410478234

push

github

saitoha
fix: avoid tty fd unused in no-isatty builds

125661 of 267416 branches covered (46.99%)

151048 of 176566 relevant lines covered (85.55%)

8930818.64 hits per line

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

88.02
/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,153✔
143
                                size_t length,
144
                                int *value_out)
145
{
146
    sixel_cms_engine_t engine;
9,148✔
147

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

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

162
static int
163
loader_manager_read_env_cms_engine(char const *name,
1,285,452✔
164
                                   int fallback_value)
165
{
166
    char const *env_value;
660,078✔
167
    int parsed_value;
660,078✔
168

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

175
    env_value = sixel_compat_getenv(name);
1,285,452✔
176
    if (env_value == NULL || env_value[0] == '\0') {
1,285,452!
177
        return fallback_value;
938,448✔
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
}
547,578✔
188

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

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

205
    while (index < length && token[index] != '\0') {
16,245!
206
        left = (unsigned char)text[index];
12,974✔
207
        right = (unsigned char)token[index];
12,974✔
208
        if (tolower(left) != tolower(right)) {
12,974✔
209
            return 0;
1,945✔
210
        }
211
        ++index;
10,742✔
212
    }
213
    if (index != length || token[index] != '\0') {
3,271!
214
        return 0;
215
    }
216

217
    return 1;
2,704✔
218
}
3,673✔
219

220
static int
221
loader_manager_parse_orientation(char const *text,
3,019✔
222
                                 size_t length,
223
                                 int *value_out,
224
                                 int allow_numeric)
225
{
226
    if (text == NULL || value_out == NULL || length == 0u) {
3,019!
227
        return 0;
228
    }
229
    if (loader_manager_span_equals_nocase(text, length, "on")) {
3,019✔
230
        *value_out = 1;
1,237✔
231
        return 1;
1,237✔
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
}
1,969✔
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,285,452✔
293
{
294
    char const *env_value;
660,078✔
295
    int parsed_value;
660,078✔
296

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

303
    env_value = sixel_compat_getenv(name);
1,285,452✔
304
    if (env_value == NULL || env_value[0] == '\0') {
1,285,452!
305
        return fallback_value;
938,026✔
306
    }
307
    if (!loader_manager_parse_orientation(env_value,
4,698!
308
                                          strlen(env_value),
1,824✔
309
                                          &parsed_value,
310
                                          1)) {
311
        return fallback_value;
12✔
312
    }
313

314
    return parsed_value;
2,862✔
315
}
547,578✔
316

317
static int
318
loader_manager_read_env_builtin_bmp_info40_mode(char const *name,
214,242✔
319
                                                int fallback_value)
320
{
321
    char const *env_value;
110,013✔
322
    int parsed_value;
110,013✔
323

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

330
    env_value = sixel_compat_getenv(name);
214,242✔
331
    if (env_value == NULL || env_value[0] == '\0') {
214,242!
332
        return fallback_value;
156,599✔
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
}
91,263✔
343

344
#if HAVE_WIC
345
static int
346
loader_manager_read_env_positive_int(char const *name,
83,058✔
347
                                     int fallback_value)
348
{
349
    char const *env_value;
41,527✔
350
    char *endptr;
41,527✔
351
    long parsed;
41,527✔
352

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

360
    env_value = sixel_compat_getenv(name);
83,058✔
361
    if (env_value == NULL || env_value[0] == '\0') {
83,058✔
362
        return fallback_value;
41,511✔
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,315✔
377

378
static int
379
loader_manager_read_wic_ico_minsize_from_env(int fallback_value)
83,058✔
380
{
381
    char const *primary_name;
41,527✔
382
    char const *legacy_name;
41,527✔
383
    char const *primary_value;
41,527✔
384

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

389
    if (primary_value != NULL && primary_value[0] != '\0') {
83,058✔
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);
83,038✔
395
}
8,315✔
396
#endif
397

398
static int
399
loader_manager_plan_contains(sixel_loader_entry_t const **plan,
201,802✔
400
                             size_t plan_length,
401
                             sixel_loader_entry_t const *entry)
402
{
403
    size_t index;
98,895✔
404

405
    index = 0u;
201,802✔
406
    for (index = 0u; index < plan_length; ++index) {
602,327!
407
        if (plan[index] == entry) {
362,913!
408
            return 1;
3,877✔
409
        }
410
    }
334,502✔
411

412
    return 0;
197,925✔
413
}
157,078✔
414

415
static int
416
loader_manager_token_matches(char const *token,
86,272✔
417
                             size_t token_length,
418
                             char const *name)
419
{
420
    size_t index;
52,615✔
421
    unsigned char left;
52,615✔
422
    unsigned char right;
52,615✔
423

424
    index = 0u;
86,272✔
425
    left = 0u;
86,272✔
426
    right = 0u;
86,272✔
427
    for (index = 0u; index < token_length && name[index] != '\0'; ++index) {
439,396!
428
        left = (unsigned char)token[index];
390,822✔
429
        right = (unsigned char)name[index];
390,822✔
430
        if (tolower(left) != tolower(right)) {
390,822✔
431
            return 0;
37,393✔
432
        }
433
    }
164,883✔
434

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

439
    return 1;
35,993✔
440
}
58,784✔
441

442
static sixel_loader_entry_t const *
443
loader_manager_lookup_token(char const *token,
48,574✔
444
                            size_t token_length,
445
                            sixel_loader_entry_t const *entries,
446
                            size_t entry_count)
447
{
448
    size_t index;
25,170✔
449

450
    index = 0u;
48,574✔
451
    for (index = 0u; index < entry_count; ++index) {
86,272!
452
        if (loader_manager_token_matches(token,
145,056!
453
                                         token_length,
58,784✔
454
                                         entries[index].name)) {
86,272✔
455
            return &entries[index];
35,993✔
456
        }
457
    }
37,133✔
458

459
    return NULL;
460
}
21,651✔
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(
214,242✔
485
    sixel_loader_suboptions_t *suboptions)
486
{
487
    int default_cms_engine;
110,013✔
488
    int default_orientation;
110,013✔
489

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

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

501
    suboptions->libjpeg_enable_cms = 0;
214,242✔
502
    suboptions->libjpeg_cms_engine = loader_manager_read_env_cms_engine(
214,242✔
503
        "SIXEL_LOADER_LIBJPEG_CMS_ENGINE",
504
        default_cms_engine);
91,263✔
505
    suboptions->libjpeg_enable_orientation =
271,749✔
506
        loader_manager_read_env_orientation(
214,242✔
507
            "SIXEL_LOADER_LIBJPEG_ORIENTATION",
508
            default_orientation);
91,263✔
509
    suboptions->libpng_enable_cms = 0;
214,242✔
510
    suboptions->libpng_cms_engine = loader_manager_read_env_cms_engine(
214,242✔
511
        "SIXEL_LOADER_LIBPNG_CMS_ENGINE",
512
        default_cms_engine);
91,263✔
513
    suboptions->libpng_enable_orientation =
271,749✔
514
        loader_manager_read_env_orientation(
214,242✔
515
            "SIXEL_LOADER_LIBPNG_ORIENTATION",
516
            default_orientation);
91,263✔
517
    suboptions->libwebp_enable_cms = 0;
214,242✔
518
    suboptions->libwebp_cms_engine = loader_manager_read_env_cms_engine(
214,242✔
519
        "SIXEL_LOADER_LIBWEBP_CMS_ENGINE",
520
        default_cms_engine);
91,263✔
521
    suboptions->libwebp_enable_orientation =
271,749✔
522
        loader_manager_read_env_orientation(
214,242✔
523
            "SIXEL_LOADER_LIBWEBP_ORIENTATION",
524
            default_orientation);
91,263✔
525
    suboptions->coregraphics_enable_orientation =
271,749✔
526
        loader_manager_read_env_orientation(
214,242✔
527
            "SIXEL_LOADER_COREGRAPHICS_ORIENTATION",
528
            default_orientation);
91,263✔
529
    suboptions->libtiff_enable_cms = 0;
214,242✔
530
    suboptions->libtiff_cms_engine = loader_manager_read_env_cms_engine(
214,242✔
531
        "SIXEL_LOADER_LIBTIFF_CMS_ENGINE",
532
        default_cms_engine);
91,263✔
533
    suboptions->builtin_enable_cms = 0;
214,242✔
534
    suboptions->builtin_cms_engine = loader_manager_read_env_cms_engine(
214,242✔
535
        "SIXEL_LOADER_BUILTIN_CMS_ENGINE",
536
        default_cms_engine);
91,263✔
537
    suboptions->builtin_enable_orientation =
271,749✔
538
        loader_manager_read_env_orientation(
214,242✔
539
            "SIXEL_LOADER_BUILTIN_ORIENTATION",
540
            default_orientation);
91,263✔
541
    suboptions->builtin_bmp_info40_mode =
271,749✔
542
        loader_manager_read_env_builtin_bmp_info40_mode(
214,242✔
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(
83,058✔
547
        0);
548
#else
549
    suboptions->wic_ico_minsize = 0;
131,184✔
550
#endif
551
}
91,263!
552

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

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

574
    if (suboptions == NULL) {
107,056✔
575
        return;
34,538✔
576
    }
577

578
    loader_manager_init_loader_suboptions(suboptions);
107,056✔
579

580
    if (resolution == NULL) {
107,056✔
581
        return;
42,391✔
582
    }
583

584
    while (item_index < resolution->item_count) {
97,081!
585
        item = &resolution->items[item_index].resolution;
48,574✔
586
        if (item->base_def == NULL) {
48,574!
587
            ++item_index;
×
588
            continue;
×
589
        }
590
        assignment_index = 0u;
35,993✔
591
        while (assignment_index < item->assignment_count) {
63,806!
592
            key_name = item->assignments[assignment_index].resolved_key_name;
15,232✔
593
            value_text = item->assignments[assignment_index]
22,483✔
594
                .resolved_value_text;
7,251✔
595
            value_length = 0u;
15,232✔
596
            if (value_text != NULL) {
15,232!
597
                value_length = strlen(value_text);
15,232✔
598
            }
7,251✔
599
            if (key_name == NULL || value_text == NULL) {
15,232!
600
                ++assignment_index;
×
601
                continue;
×
602
            }
603
#if HAVE_WIC
604
            if (strcmp(item->base_def->name, "wic") == 0 &&
5,499✔
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 &&
15,812!
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,461!
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 &&
13,868!
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 &&
13,799!
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,283!
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 &&
13,763!
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,279!
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 &&
13,743!
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 &&
18,849!
667
                       strcmp(key_name, "cms_engine") == 0 &&
21,403!
668
                       loader_manager_parse_cms_engine(value_text,
18,908!
669
                                                       value_length,
5,495✔
670
                                                       &parsed_value)) {
671
                suboptions->builtin_cms_engine = parsed_value;
13,413✔
672
            } else if (strcmp(item->base_def->name, "builtin") == 0 &&
5,591!
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,212✔
689
        }
690
        ++item_index;
48,574✔
691
    }
692
}
45,604!
693

694
size_t
695
loader_manager_build_plan_from_resolution(
107,056✔
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;
54,972✔
703
    size_t index;
54,972✔
704
    sixel_loader_entry_t const *entry;
54,972✔
705
    size_t limit;
54,972✔
706
    int allow_fallback;
54,972✔
707
    sixel_option_argument_resolution_t const *item;
54,972✔
708

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

716
    if (resolution != NULL) {
107,056✔
717
        allow_fallback = !resolution->has_trailing_bang;
48,507✔
718
    }
21,593✔
719

720
    if (plan != NULL && plan_capacity > 0u && resolution != NULL) {
107,056!
721
        for (index = 0u; index < resolution->item_count; ++index) {
97,081!
722
            item = &resolution->items[index].resolution;
48,574✔
723
            if (item->base_def == NULL || item->base_def->name == NULL) {
48,574!
724
                continue;
×
725
            }
726
            entry = loader_manager_lookup_token(item->base_def->name,
70,225✔
727
                                                strlen(item->base_def->name),
35,993✔
728
                                                entries,
21,651✔
729
                                                entry_count);
21,651✔
730
            if (entry != NULL &&
82,741!
731
                !loader_manager_plan_contains(plan, plan_length, entry) &&
62,916!
732
                plan_length < limit) {
21,651!
733
                plan[plan_length] = entry;
48,574✔
734
                ++plan_length;
48,574✔
735
            }
21,651✔
736
        }
21,651✔
737
    }
21,593✔
738

739
    if (allow_fallback && plan != NULL && limit > 0u) {
104,747!
740
        for (index = 0u; index < entry_count && plan_length < limit; ++index) {
301,290!
741
            entry = &entries[index];
237,292✔
742
            if (!entry->default_enabled) {
237,292✔
743
                continue;
41,011✔
744
            }
745
            if (!loader_manager_plan_contains(plan, plan_length, entry)) {
196,281✔
746
                plan[plan_length] = entry;
190,840✔
747
                ++plan_length;
190,840✔
748
            }
133,342✔
749
        }
135,427✔
750
    }
26,097✔
751

752
    return plan_length;
133,294✔
753
}
26,238✔
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)
107,248✔
779
{
780
    if (entries != NULL) {
86,502!
781
        *entries = g_sixel_loader_entries;
86,502✔
782
    }
45,676✔
783
    return sizeof(g_sixel_loader_entries) / sizeof(g_sixel_loader_entries[0]);
107,248✔
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)
214,242✔
800
{
801
    struct sixel_loader_manager *object;
110,013✔
802
    size_t index;
110,013✔
803

804
    object = NULL;
214,242✔
805
    index = 0u;
214,242✔
806
    if (manager == NULL) {
214,242✔
807
        return;
808
    }
809
    object = (struct sixel_loader_manager *)manager;
255,731✔
810
    for (index = 0u; index < object->chain_count; ++index) {
453,656!
811
        sixel_loader_component_unref(object->chain[index]);
239,414✔
812
    }
154,993✔
813
    if (object->chain != NULL) {
214,242✔
814
        sixel_allocator_free(object->allocator, object->chain);
107,056✔
815
    }
45,604✔
816
    object->chain = NULL;
214,242✔
817
    object->chain_count = 0u;
214,242✔
818
    object->chain_capacity = 0u;
214,242✔
819
}
91,263!
820

821
static SIXELSTATUS
822
sixel_loader_manager_append_chain(sixel_loader_manager_t *manager,
239,414✔
823
                                  sixel_loader_component_interface_t *loader)
824
{
825
    struct sixel_loader_manager *object;
139,221✔
826
    sixel_loader_component_interface_t **new_chain;
139,221✔
827
    size_t new_capacity;
139,221✔
828
    size_t index;
139,221✔
829

830
    object = NULL;
239,414✔
831
    new_chain = NULL;
239,414✔
832
    new_capacity = 0u;
239,414✔
833
    index = 0u;
239,414✔
834
    if (manager == NULL || loader == NULL) {
239,414!
835
        return SIXEL_BAD_ARGUMENT;
836
    }
837
    object = (struct sixel_loader_manager *)manager;
239,414✔
838
    if (object->chain_count == object->chain_capacity) {
239,414!
839
        new_capacity = object->chain_capacity == 0u
106,355✔
840
            ? 8u : object->chain_capacity * 2u;
61,452!
841
        new_chain = (sixel_loader_component_interface_t **)
74,338✔
842
            sixel_allocator_malloc(
127,802✔
843
                object->allocator,
45,604✔
844
                new_capacity * sizeof(*new_chain));
87,011✔
845
        if (new_chain == NULL) {
107,056!
846
            return SIXEL_BAD_ALLOCATION;
847
        }
848
        for (index = 0u; index < object->chain_count; ++index) {
107,056!
849
            new_chain[index] = object->chain[index];
×
850
        }
851
        if (object->chain != NULL) {
107,056!
852
            sixel_allocator_free(object->allocator, object->chain);
×
853
        }
854
        object->chain = new_chain;
107,056✔
855
        object->chain_capacity = new_capacity;
107,056✔
856
    }
45,604✔
857
    object->chain[object->chain_count] = loader;
239,414✔
858
    ++object->chain_count;
239,414✔
859
    sixel_loader_component_ref(loader);
239,414✔
860
    return SIXEL_OK;
239,414✔
861
}
154,993✔
862

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

873
    loader_component_option_entry_t const options[] = {
239,414✔
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;
139,221✔
882
    char message[128];
139,221✔
883
    size_t index;
139,221✔
884
    SIXELSTATUS status;
139,221✔
885
    int suboption_value;
139,221✔
886
    char const *component_name;
139,221✔
887

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

899
    for (index = 0u; index < sizeof(options) / sizeof(options[0]); ++index) {
1,717,387!
900
        switch (options[index].option) {
1,436,484!
901
        case SIXEL_LOADER_OPTION_REQUIRE_STATIC:
84,421!
902
            value = &request->require_static;
239,414✔
903
            break;
239,414✔
904
        case SIXEL_LOADER_OPTION_USE_PALETTE:
84,421!
905
            value = &request->use_palette;
239,414✔
906
            break;
239,414✔
907
        case SIXEL_LOADER_OPTION_REQCOLORS:
84,421!
908
            value = &request->reqcolors;
239,414✔
909
            break;
239,414✔
910
        case SIXEL_LOADER_OPTION_BGCOLOR:
84,421!
911
            value = request->has_bgcolor ? request->bgcolor : NULL;
239,414✔
912
            break;
197,925✔
913
        case SIXEL_LOADER_OPTION_LOOP_CONTROL:
84,421!
914
            value = &request->loop_control;
239,414✔
915
            break;
239,414✔
916
        case SIXEL_LOADER_OPTION_START_FRAME_NO:
84,421!
917
            value = request->has_start_frame_no
218,980✔
918
                ? &request->start_frame_no : NULL;
89,507✔
919
            break;
197,925✔
920
        default:
×
921
            value = NULL;
922
            break;
923
        }
924

925
        status = sixel_loader_component_setopt(component,
2,366,442✔
926
                                               options[index].option,
1,187,550✔
927
                                               value);
929,958✔
928
        if (SIXEL_FAILED(status)) {
1,436,484!
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
    }
929,958✔
939

940
    status = sixel_loader_component_setopt(
280,903✔
941
        component,
154,993✔
942
        SIXEL_LOADER_COMPONENT_OPTION_BGCOLOR_SOURCE,
943
        &request->bgcolor_source);
239,414✔
944
    if (SIXEL_FAILED(status)) {
239,414!
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) {
239,414!
952
        return SIXEL_OK;
953
    }
954

955
#if HAVE_WIC
956
    suboption_value = request->suboptions->wic_ico_minsize;
67,022✔
957
    status = sixel_loader_component_setopt(
67,022✔
958
        component,
6,713✔
959
        SIXEL_LOADER_COMPONENT_OPTION_WIC_ICO_MINSIZE,
960
        &suboption_value);
961
    if (SIXEL_FAILED(status)) {
67,022✔
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;
239,414✔
970
    status = sixel_loader_component_setopt(
239,414✔
971
        component,
154,993✔
972
        SIXEL_LOADER_COMPONENT_OPTION_LIBPNG_ENABLE_CMS,
973
        &suboption_value);
974
    if (SIXEL_FAILED(status)) {
239,414!
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;
239,414✔
982
    status = sixel_loader_component_setopt(
239,414✔
983
        component,
154,993✔
984
        SIXEL_LOADER_COMPONENT_OPTION_LIBJPEG_ENABLE_CMS,
985
        &suboption_value);
986
    if (SIXEL_FAILED(status)) {
239,414!
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;
239,414✔
994
    status = sixel_loader_component_setopt(
239,414✔
995
        component,
154,993✔
996
        SIXEL_LOADER_COMPONENT_OPTION_LIBWEBP_ENABLE_CMS,
997
        &suboption_value);
998
    if (SIXEL_FAILED(status)) {
239,414!
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;
239,414✔
1006
    status = sixel_loader_component_setopt(
239,414✔
1007
        component,
154,993✔
1008
        SIXEL_LOADER_COMPONENT_OPTION_LIBTIFF_ENABLE_CMS,
1009
        &suboption_value);
1010
    if (SIXEL_FAILED(status)) {
239,414!
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;
239,414✔
1018
    status = sixel_loader_component_setopt(
239,414✔
1019
        component,
154,993✔
1020
        SIXEL_LOADER_COMPONENT_OPTION_BUILTIN_ENABLE_CMS,
1021
        &suboption_value);
1022
    if (SIXEL_FAILED(status)) {
239,414!
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;
239,414✔
1030
    status = sixel_loader_component_setopt(
239,414✔
1031
        component,
154,993✔
1032
        SIXEL_LOADER_COMPONENT_OPTION_BUILTIN_BMP_INFO40_MODE,
1033
        &suboption_value);
1034
    if (SIXEL_FAILED(status)) {
239,414!
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;
239,414✔
1042
    status = sixel_loader_component_setopt(
239,414✔
1043
        component,
154,993✔
1044
        SIXEL_LOADER_COMPONENT_OPTION_BUILTIN_ENABLE_ORIENTATION,
1045
        &suboption_value);
1046
    if (SIXEL_FAILED(status)) {
239,414!
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;
239,414✔
1054
    status = sixel_loader_component_setopt(
239,414✔
1055
        component,
154,993✔
1056
        SIXEL_LOADER_COMPONENT_OPTION_LIBJPEG_ENABLE_ORIENTATION,
1057
        &suboption_value);
1058
    if (SIXEL_FAILED(status)) {
239,414!
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;
239,414✔
1066
    status = sixel_loader_component_setopt(
239,414✔
1067
        component,
154,993✔
1068
        SIXEL_LOADER_COMPONENT_OPTION_LIBPNG_ENABLE_ORIENTATION,
1069
        &suboption_value);
1070
    if (SIXEL_FAILED(status)) {
239,414!
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;
239,414✔
1078
    status = sixel_loader_component_setopt(
239,414✔
1079
        component,
154,993✔
1080
        SIXEL_LOADER_COMPONENT_OPTION_LIBWEBP_ENABLE_ORIENTATION,
1081
        &suboption_value);
1082
    if (SIXEL_FAILED(status)) {
239,414!
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;
239,414✔
1090
    status = sixel_loader_component_setopt(
239,414✔
1091
        component,
154,993✔
1092
        SIXEL_LOADER_COMPONENT_OPTION_COREGRAPHICS_ENABLE_ORIENTATION,
1093
        &suboption_value);
1094
    if (SIXEL_FAILED(status)) {
239,414!
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) {
239,414!
1102
        suboption_value = request->suboptions->libpng_cms_engine;
10,719✔
1103
    } else if (component_name != NULL &&
239,414!
1104
               strcmp(component_name, "libjpeg") == 0) {
228,695!
1105
        suboption_value = request->suboptions->libjpeg_cms_engine;
10,011✔
1106
    } else if (component_name != NULL &&
228,695!
1107
               strcmp(component_name, "libwebp") == 0) {
218,684!
1108
        suboption_value = request->suboptions->libwebp_cms_engine;
13,764✔
1109
    } else if (component_name != NULL &&
218,684!
1110
               strcmp(component_name, "libtiff") == 0) {
204,920!
1111
        suboption_value = request->suboptions->libtiff_cms_engine;
10,341✔
1112
    } else if (component_name != NULL &&
273,807!
1113
               (strcmp(component_name, "builtin") == 0 ||
194,579✔
1114
                strcmp(component_name, "gnome-thumbnailer") == 0)) {
92,392!
1115
        suboption_value = request->suboptions->builtin_cms_engine;
102,487✔
1116
    } else {
41,431✔
1117
        suboption_value = 0;
92,092✔
1118
    }
1119
    status = sixel_loader_component_setopt(
239,414✔
1120
        component,
154,993✔
1121
        SIXEL_LOADER_COMPONENT_OPTION_CMS_ENGINE,
1122
        &suboption_value);
1123
    if (SIXEL_FAILED(status)) {
239,414!
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;
197,925✔
1131
}
154,993✔
1132

1133
static void
1134
sixel_loader_manager_format_worker_name(char *worker,
108,183✔
1135
                                        size_t worker_size,
1136
                                        char const *name)
1137
{
1138
    char const *backend_name;
55,619✔
1139

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

1145
    backend_name = name != NULL && name[0] != '\0' ? name : "unknown";
108,183!
1146
    (void)sixel_compat_snprintf(worker,
154,590✔
1147
                                worker_size,
46,407✔
1148
                                "loader/%s",
1149
                                backend_name);
46,407✔
1150
}
46,407!
1151

1152
static int
1153
sixel_loader_manager_status_allows_fallback(SIXELSTATUS status)
8,719✔
1154
{
1155
    switch (status) {
8,719!
1156
    case SIXEL_FALSE:
2,372!
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;
6,018✔
1169
    default:
168!
1170
        break;
568✔
1171
    }
1172

1173
    return 0;
568✔
1174
}
3,899✔
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)
107,186✔
1191
{
1192
    struct sixel_loader_manager *object;
55,041✔
1193
    unsigned int previous;
55,041✔
1194

1195
    object = NULL;
107,186✔
1196
    previous = 0u;
107,186✔
1197
    if (manager == NULL) {
107,186✔
1198
        return;
1199
    }
1200
    object = (struct sixel_loader_manager *)manager;
107,186✔
1201
    previous = sixel_atomic_fetch_sub_u32(&object->ref, 1u);
107,186!
1202
    if (previous != 1u) {
107,186!
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);
107,186✔
1209
    if (object->factory != NULL && object->factory->vtbl != NULL &&
107,186!
1210
        object->factory->vtbl->unref != NULL) {
107,186!
1211
        object->factory->vtbl->unref(object->factory);
107,186✔
1212
    }
45,659✔
1213
    sixel_allocator_unref(object->allocator);
107,186✔
1214
    sixel_allocator_free(object->allocator, object);
107,186✔
1215
}
45,659!
1216

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

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

1253
    entry_count = loader_manager_get_entries(&entries);
107,056✔
1254
    if (entry_count == 0u || entries == NULL) {
107,056!
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(
107,056✔
1261
        object->allocator,
45,604✔
1262
        entry_count * sizeof(*plan));
66,265✔
1263
    if (plan == NULL) {
107,056!
1264
        return SIXEL_BAD_ALLOCATION;
1265
    }
1266
    plan_length = loader_manager_build_plan_from_resolution(
107,056!
1267
        request != NULL ? request->resolution : NULL,
45,604!
1268
        entries,
45,604✔
1269
        entry_count,
45,604✔
1270
        plan,
45,604✔
1271
        entry_count);
45,604✔
1272
    if (plan_length == 0u) {
107,056!
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);
107,056✔
1280
    for (index = 0u; index < plan_length; ++index) {
375,204!
1281
        entry = plan[index];
239,414✔
1282
        if (entry == NULL || entry->classid == NULL) {
239,414!
1283
            continue;
×
1284
        }
1285
        status = object->factory->vtbl->create(object->factory,
394,407✔
1286
                                               entry->classid,
197,925✔
1287
                                               object->allocator,
154,993✔
1288
                                               (void **)&loader);
1289
        if (SIXEL_FAILED(status)) {
239,414!
1290
            sixel_loader_manager_clear_chain(manager);
×
1291
            sixel_allocator_free(object->allocator, plan);
×
1292
            return status;
×
1293
        }
1294
        if (request != NULL) {
239,414!
1295
            status = sixel_loader_manager_apply_component_options(loader,
394,407✔
1296
                                                                  request);
154,993✔
1297
            if (SIXEL_FAILED(status)) {
239,414!
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
        }
154,993✔
1305
        status = sixel_loader_manager_append_chain(manager, loader);
239,414✔
1306
        sixel_loader_component_unref(loader);
239,414✔
1307
        loader = NULL;
239,414✔
1308
        if (SIXEL_FAILED(status)) {
239,414!
1309
            sixel_loader_manager_clear_chain(manager);
×
1310
            sixel_allocator_free(object->allocator, plan);
×
1311
            return status;
×
1312
        }
1313
    }
154,993✔
1314
    sixel_allocator_free(object->allocator, plan);
107,056✔
1315
    if (object->chain_count == 0u) {
107,056!
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) {
107,056!
1321
        object->skip_predicate_gate = 1;
43,001✔
1322
    }
19,450✔
1323
    return SIXEL_OK;
78,322✔
1324
}
45,604✔
1325

1326
static SIXELSTATUS
1327
sixel_loader_manager_load_impl(
107,056✔
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;
54,972✔
1335
    SIXELSTATUS status;
54,972✔
1336
    sixel_loader_component_interface_t *loader;
54,972✔
1337
    char const *name;
54,972✔
1338
    char worker[96];
54,972✔
1339
    size_t index;
54,972✔
1340
    int enforce_predicate;
54,972✔
1341

1342
    object = NULL;
107,056✔
1343
    status = SIXEL_FALSE;
107,056✔
1344
    loader = NULL;
107,056✔
1345
    name = NULL;
107,056✔
1346
    worker[0] = '\0';
107,056✔
1347
    index = 0u;
107,056✔
1348
    enforce_predicate = 0;
107,056✔
1349
    if (selected_loader != NULL) {
107,056!
1350
        *selected_loader = NULL;
×
1351
    }
1352
    if (manager == NULL || chunk == NULL || fn_load == NULL) {
107,056!
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;
107,056✔
1358
    if (object->chain == NULL || object->chain_count == 0u) {
107,056!
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;
107,056!
1364
    for (index = 0u; index < object->chain_count; ++index) {
154,334!
1365
        loader = object->chain[index];
147,310✔
1366
        if (loader == NULL) {
147,310!
1367
            continue;
×
1368
        }
1369
        if (enforce_predicate &&
185,535!
1370
            !sixel_loader_component_predicate(loader, chunk)) {
104,309!
1371
            continue;
39,127✔
1372
        }
1373
        name = sixel_loader_component_get_name(loader);
108,183✔
1374
        sixel_loader_manager_format_worker_name(worker,
154,590✔
1375
                                                sizeof(worker),
1376
                                                name);
46,407✔
1377
        loader_trace_try(name);
108,183✔
1378
        sixel_loader_timeline_candidate_select_start(
108,183✔
1379
            object->timeline_loader,
46,407✔
1380
            worker);
46,407✔
1381
        loader_timeline_scope_begin(object->timeline_logger,
154,590✔
1382
                                    worker,
46,407✔
1383
                                    object->timeline_job_seq);
46,407✔
1384
        status = sixel_loader_component_load(loader,
154,590✔
1385
                                             chunk,
46,407✔
1386
                                             fn_load,
46,407✔
1387
                                             load_context);
46,407✔
1388
        loader_timeline_scope_end();
108,183✔
1389
        sixel_loader_timeline_candidate_select_finish(
108,183✔
1390
            object->timeline_loader,
46,407✔
1391
            status);
46,407✔
1392
        loader_trace_result(name, status);
108,183✔
1393
        if (status == SIXEL_OK) {
108,183✔
1394
            if (selected_loader != NULL) {
99,430!
1395
                *selected_loader = loader;
×
1396
            }
1397
            return SIXEL_OK;
99,430✔
1398
        }
1399
        if (SIXEL_SUCCEEDED(status) ||
11,293!
1400
                !sixel_loader_manager_status_allows_fallback(status)) {
8,719!
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
    }
3,646✔
1409

1410
    return status;
5,071✔
1411
}
45,604✔
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,
107,186✔
1422
                         void **manager)
1423
{
1424
    struct sixel_loader_manager *object;
55,041✔
1425
    SIXELSTATUS status;
55,041✔
1426

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

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

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

1460
    *manager = &object->base;
107,186✔
1461
    return SIXEL_OK;
107,186✔
1462
}
45,659✔
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