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

saitoha / libsixel / 22262058122

21 Feb 2026 06:31PM UTC coverage: 83.681% (+0.3%) from 83.377%
22262058122

push

github

saitoha
ci: normalize cygwin package list for installer

26161 of 50394 branches covered (51.91%)

46444 of 55501 relevant lines covered (83.68%)

4070189.91 hits per line

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

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

26
#if defined(HAVE_CONFIG_H)
27
#include "config.h"
28
#endif
29
#if !defined(_POSIX_C_SOURCE)
30
# define _POSIX_C_SOURCE 200809L
31
#endif
32

33
/* STDC_HEADERS */
34
#include <stdio.h>
35
#include <stdlib.h>
36

37
#if HAVE_STRING_H
38
# include <string.h>
39
#endif
40
#if HAVE_CTYPE_H
41
# include <ctype.h>
42
#endif
43
#if HAVE_STDARG_H
44
# include <stdarg.h>
45
#endif
46
#if HAVE_SYS_TYPES_H
47
# include <sys/types.h>
48
#endif
49
#if HAVE_UNISTD_H
50
# include <unistd.h>
51
#endif
52
#if !defined(PATH_MAX)
53
#define PATH_MAX 4096
54
#endif
55
#if HAVE_FCNTL_H
56
# include <fcntl.h>
57
#endif
58
#if HAVE_SYS_TIME_H
59
# include <sys/time.h>
60
#elif HAVE_TIME_H
61
# include <time.h>
62
#endif  /* HAVE_SYS_TIME_H HAVE_TIME_H */
63
#if defined(_WIN32)
64
# if !defined(UNICODE)
65
#  define UNICODE
66
# endif
67
# if !defined(_UNICODE)
68
#  define _UNICODE
69
# endif
70
# if !defined(WIN32_LEAN_AND_MEAN)
71
#  define WIN32_LEAN_AND_MEAN
72
# endif
73
# include <windows.h>
74
#endif
75
#if HAVE_ERRNO_H
76
# include <errno.h>
77
#endif
78
#if HAVE_COREGRAPHICS && HAVE_QUICKLOOK
79
# include <CoreServices/CoreServices.h>
80
#endif
81
#if HAVE_SYS_WAIT_H
82
# include <sys/wait.h>
83
#endif
84
#if HAVE_SPAWN_H
85
# include <spawn.h>
86
#endif
87
#if HAVE_LIMITS_H
88
# include <limits.h>
89
#endif
90
#if HAVE_DIRENT_H
91
# include <dirent.h>
92
#endif
93

94
#include <sixel.h>
95
#include "loader.h"
96
#include "loader-builtin.h"
97
#include "loader-common.h"
98
#include "loader-coregraphics.h"
99
#include "loader-gd.h"
100
#include "loader-gdk-pixbuf2.h"
101
#include "loader-gnome-thumbnailer.h"
102
#include "loader-libjpeg.h"
103
#include "loader-libpng.h"
104
#include "loader-librsvg.h"
105
#include "loader-quicklook.h"
106
#include "loader-registry.h"
107
#include "loader-component.h"
108
#include "loader-component-legacy.h"
109
#include "loader-wic.h"
110
#include "compat_stub.h"
111
#include "frame.h"
112
#include "chunk.h"
113
#include "allocator.h"
114
#include "encoder.h"
115
#include "logger.h"
116
#include "options.h"
117
#include "sixel_atomic.h"
118

119
/*
120
 * Internal loader state carried across backends.  The fields mirror the
121
 * original `loader.c` layout to keep statistics, logging, and allocator
122
 * ownership centralized while implementations move into per-backend files.
123
 */
124
struct sixel_loader {
125
    sixel_atomic_u32_t ref;
126
    int fstatic;
127
    int fuse_palette;
128
    int reqcolors;
129
    unsigned char bgcolor[3];
130
    int has_bgcolor;
131
    int loop_control;
132
    int finsecure;
133
    int has_start_frame_no;
134
    int start_frame_no;
135
    int const *cancel_flag;
136
    void *context;
137
    sixel_logger_t logger;
138
    char *loader_order;
139
    sixel_allocator_t *allocator;
140
    char last_loader_name[64];
141
    char last_source_path[PATH_MAX];
142
    size_t last_input_bytes;
143
    int callback_failed;
144
    int log_loader_finished;
145
    char log_path[PATH_MAX];
146
    char log_loader_name[64];
147
    size_t log_input_bytes;
148
};
149

150
typedef struct sixel_loader_callback_state {
151
    sixel_loader_t *loader;
152
    sixel_load_image_function fn;
153
    void *context;
154
} sixel_loader_callback_state_t;
155

156
int
157
sixel_loader_callback_is_canceled(void *data)
38,083✔
158
{
159
    sixel_loader_callback_state_t *state;
22,488✔
160

161
    state = (sixel_loader_callback_state_t *)data;
38,083✔
162
    if (state == NULL || state->loader == NULL ||
38,083!
163
        state->loader->cancel_flag == NULL) {
37,988✔
164
        return 0;
9,008✔
165
    }
166

167
    return *state->loader->cancel_flag != 0;
23,911✔
168
}
18,828✔
169

170

171
#if HAVE_POSIX_SPAWNP
172
extern char **environ;
173
#endif
174

175
static char *
176
loader_strdup(char const *text, sixel_allocator_t *allocator)
1,842✔
177
{
178
    char *copy;
1,096✔
179
    size_t length;
1,096✔
180

181
    if (text == NULL) {
1,842!
182
        return NULL;
183
    }
184

185
    length = strlen(text) + 1;
1,842✔
186
    copy = (char *)sixel_allocator_malloc(allocator, length);
1,842✔
187
    if (copy == NULL) {
1,842!
188
        return NULL;
189
    }
190

191
    /* Copy the terminating NUL byte as part of length. */
192
    memcpy(copy, text, length);
1,842✔
193

194
    return copy;
1,842✔
195
}
1,053✔
196

197

198

199
/*
200
 * Emit loader stage markers.
201
 *
202
 * Loader callbacks run the downstream pipeline synchronously, so the finish
203
 * marker must be issued before invoking fn_load() to avoid inflating the
204
 * loader span. The helper keeps the formatting consistent with
205
 * sixel_encoder_log_stage() without depending on encoder internals.
206
 */
207
static void
208
loader_log_stage(sixel_loader_t *loader,
28,363✔
209
                 char const *event,
210
                 char const *fmt,
211
                 ...)
212
{
213
    sixel_logger_t *logger;
16,693✔
214
    char message[256];
16,693✔
215
    va_list args;
16,693✔
216

217
    logger = NULL;
28,363✔
218
    if (loader != NULL) {
28,363!
219
        logger = &loader->logger;
28,363✔
220
    }
13,899✔
221
    if (logger == NULL || logger->file == NULL || !logger->active) {
28,363!
222
        return;
28,363✔
223
    }
224

225
    message[0] = '\0';
×
226
#if HAVE_DIAGNOSTIC_FORMAT_NONLITERAL
227
# if defined(__clang__)
228
#  pragma clang diagnostic push
229
#  pragma clang diagnostic ignored "-Wformat-nonliteral"
230
# elif defined(__GNUC__) && !defined(__PCC__)
231
#  pragma GCC diagnostic push
232
#  pragma GCC diagnostic ignored "-Wformat-nonliteral"
233
# endif
234
#endif
235
    va_start(args, fmt);
×
236
    if (fmt != NULL) {
×
237
#if HAVE_DIAGNOSTIC_FORMAT_NONLITERAL
238
# if defined(__clang__)
239
#  pragma clang diagnostic push
240
#  pragma clang diagnostic ignored "-Wformat-nonliteral"
241
# elif defined(__GNUC__) && !defined(__PCC__)
242
#  pragma GCC diagnostic push
243
#  pragma GCC diagnostic ignored "-Wformat-nonliteral"
244
# endif
245
#endif
246
        (void)sixel_compat_vsnprintf(message, sizeof(message), fmt, args);
×
247
#if HAVE_DIAGNOSTIC_FORMAT_NONLITERAL
248
# if defined(__clang__)
249
#  pragma clang diagnostic pop
250
# elif defined(__GNUC__) && !defined(__PCC__)
251
#  pragma GCC diagnostic pop
252
# endif
253
#endif
254
    }
255
    va_end(args);
×
256
#if HAVE_DIAGNOSTIC_FORMAT_NONLITERAL
257
# if defined(__clang__)
258
#  pragma clang diagnostic pop
259
# elif defined(__GNUC__) && !defined(__PCC__)
260
#  pragma GCC diagnostic pop
261
# endif
262
#endif
263

264
    sixel_logger_logf(logger,
×
265
                      "worker",
266
                      "loader",
267
                      event,
268
                      -1,
269
                      -1,
270
                      0,
271
                      0,
272
                      0,
273
                      0,
274
                      "%s",
275
                      message);
276
}
13,899!
277

278
static SIXELSTATUS
279
loader_callback_trampoline(sixel_frame_t *frame, void *data)
15,263✔
280
{
281
    sixel_loader_callback_state_t *state;
8,987✔
282
    SIXELSTATUS status;
8,987✔
283
    sixel_loader_t *loader;
8,987✔
284

285
    state = (sixel_loader_callback_state_t *)data;
15,263✔
286
    loader = NULL;
15,263✔
287
    if (state == NULL || state->fn == NULL) {
15,263!
288
        return SIXEL_BAD_ARGUMENT;
289
    }
290

291
    loader = state->loader;
15,263✔
292
    if (loader != NULL && loader->log_loader_finished == 0) {
15,263!
293
        loader_log_stage(loader,
20,908✔
294
                         "finish",
295
                         "path=%s loader=%s bytes=%zu",
296
                         loader->log_path,
14,038✔
297
                         loader->log_loader_name,
14,038✔
298
                         loader->log_input_bytes);
6,870✔
299
        loader->log_loader_finished = 1;
14,038✔
300
    }
6,870✔
301

302
    status = state->fn(frame, state->context);
16,171✔
303
    if (SIXEL_FAILED(status) && state->loader != NULL) {
16,171!
304
        state->loader->callback_failed = 1;
×
305
    }
306

307
    return status;
10,163✔
308
}
7,541✔
309

310

311
static SIXELSTATUS
312
loader_apply_component_options(sixel_loader_component_t *component,
14,440✔
313
                               sixel_loader_t const *loader,
314
                               int reqcolors)
315
{
316
    typedef struct loader_component_option_entry {
4,836✔
317
        int option;
318
        char const *name;
319
        void const *value;
320
    } loader_component_option_entry_t;
321

322
    loader_component_option_entry_t options[6];
8,490✔
323
    char message[128];
8,490✔
324
    size_t index;
8,490✔
325
    SIXELSTATUS status;
8,490✔
326

327
    /*
328
     * Distribute common execution parameters to every loader component.
329
     *
330
     * +---------------------------+-------------------------------+
331
     * | option                    | value source                  |
332
     * +---------------------------+-------------------------------+
333
     * | REQUIRE_STATIC            | loader->fstatic               |
334
     * | USE_PALETTE               | loader->fuse_palette          |
335
     * | REQCOLORS                 | normalized reqcolors          |
336
     * | BGCOLOR                   | loader->bgcolor or NULL       |
337
     * | LOOP_CONTROL              | loader->loop_control          |
338
     * | START_FRAME_NO            | loader->start_frame_no/NULL   |
339
     * +---------------------------+-------------------------------+
340
     */
341
    options[0].option = SIXEL_LOADER_OPTION_REQUIRE_STATIC;
14,440✔
342
    options[0].name = "require-static";
14,440✔
343
    options[0].value = &loader->fstatic;
14,440✔
344
    options[1].option = SIXEL_LOADER_OPTION_USE_PALETTE;
14,440✔
345
    options[1].name = "use-palette";
14,440✔
346
    options[1].value = &loader->fuse_palette;
14,440✔
347
    options[2].option = SIXEL_LOADER_OPTION_REQCOLORS;
14,440✔
348
    options[2].name = "reqcolors";
14,440✔
349
    options[2].value = &reqcolors;
14,440✔
350
    options[3].option = SIXEL_LOADER_OPTION_BGCOLOR;
14,440✔
351
    options[3].name = "bgcolor";
14,440✔
352
    options[3].value = loader->has_bgcolor ? loader->bgcolor : NULL;
14,440✔
353
    options[4].option = SIXEL_LOADER_OPTION_LOOP_CONTROL;
14,440✔
354
    options[4].name = "loop-control";
14,440✔
355
    options[4].value = &loader->loop_control;
14,440✔
356
    options[5].option = SIXEL_LOADER_OPTION_START_FRAME_NO;
14,440✔
357
    options[5].name = "start-frame-no";
14,440✔
358
    options[5].value = loader->has_start_frame_no
21,775✔
359
        ? &loader->start_frame_no : NULL;
7,441✔
360

361
    status = SIXEL_OK;
14,440✔
362
    message[0] = '\0';
14,440✔
363
    index = 0;
14,440✔
364
    if (component == NULL || loader == NULL) {
14,440!
365
        return SIXEL_BAD_ARGUMENT;
366
    }
367

368
    for (index = 0; index < sizeof(options) / sizeof(options[0]); ++index) {
101,080✔
369
        status = sixel_loader_component_setopt(component,
129,270✔
370
                                               options[index].option,
42,630✔
371
                                               options[index].value);
42,630✔
372
        if (SIXEL_FAILED(status)) {
86,640!
373
            (void)sixel_compat_snprintf(message,
×
374
                                        sizeof(message),
375
                                        "sixel_loader_load_file: "
376
                                        "failed to apply loader option "
377
                                        "'%s'.",
378
                                        options[index].name);
379
            sixel_helper_set_additional_message(
×
380
                message);
381
            return status;
×
382
        }
383
    }
42,630✔
384

385
    return SIXEL_OK;
9,604✔
386
}
7,105✔
387

388
static int
389
loader_plan_contains(sixel_loader_entry_t const **plan,
32,149✔
390
                     size_t plan_length,
391
                     sixel_loader_entry_t const *entry)
392
{
393
    size_t index;
14,450✔
394

395
    for (index = 0; index < plan_length; ++index) {
95,839!
396
        if (plan[index] == entry) {
55,962!
397
            return 1;
398
        }
399
    }
50,835✔
400

401
    return 0;
32,149✔
402
}
27,478✔
403

404

405
static int
406
loader_token_matches(char const *token,
407
                     size_t token_length,
408
                     char const *name);
409

410
#if HAVE_WIC
411
static sixel_suboption_key_t const g_subkeys_loader_wic_loader[] = {
412
    {
413
        "ico_minsize",
414
        NULL,
415
        "SIXEL_LODER_WIC_ICO_MINSIZE",
416
        SIXEL_SUBOPTION_VALUE_FREE,
417
        NULL,
418
        0u
419
    }
420
};
421
#endif
422

423
static sixel_option_value_schema_t const g_schema_loader_values_loader[] = {
424
#if HAVE_LIBPNG
425
    { "libpng", 0, NULL, 0u },
426
#endif
427
#if HAVE_JPEG
428
    { "libjpeg", 0, NULL, 0u },
429
#endif
430
#if HAVE_WEBP
431
    { "libwebp", 0, NULL, 0u },
432
#endif
433
#if HAVE_LIBTIFF
434
    { "libtiff", 0, NULL, 0u },
435
#endif
436
    { "builtin", 0, NULL, 0u },
437
#if HAVE_WIC
438
    {
439
        "wic",
440
        0,
441
        g_subkeys_loader_wic_loader,
442
        sizeof(g_subkeys_loader_wic_loader)
443
            / sizeof(g_subkeys_loader_wic_loader[0])
444
    },
445
#endif
446
#if HAVE_COREGRAPHICS
447
    { "coregraphics", 0, NULL, 0u },
448
#endif
449
#ifdef HAVE_GDK_PIXBUF2
450
    { "gdk-pixbuf2", 0, NULL, 0u },
451
#endif
452
#if HAVE_GD
453
    { "gd", 0, NULL, 0u },
454
#endif
455
#if HAVE_COREGRAPHICS && HAVE_QUICKLOOK
456
    { "quicklook", 0, NULL, 0u },
457
#endif
458
#if HAVE_FREEDESKTOP_THUMBNAILING
459
    { "gnome-thumbnailer", 0, NULL, 0u },
460
#endif
461
};
462

463
static sixel_option_argument_schema_t const g_schema_loaders_loader = {
464
    SIXEL_OPTFLAG_LOADERS,
465
    "--loaders",
466
    g_schema_loader_values_loader,
467
    sizeof(g_schema_loader_values_loader)
468
        / sizeof(g_schema_loader_values_loader[0])
469
};
470

471
static size_t
472
loader_token_name_length(char const *token, size_t token_length)
1,333✔
473
{
474
    size_t index;
584✔
475

476
    index = 0;
1,333✔
477
    while (index < token_length) {
15,535!
478
        if (token[index] == ':') {
13,700!
479
            break;
8✔
480
        }
481
        ++index;
13,684✔
482
    }
483

484
    return index;
2,435✔
485
}
584✔
486

487
static int
488
loader_parse_positive_int(char const *text, size_t length, int *value_out)
16✔
489
{
490
    size_t index;
8✔
491
    int value;
8✔
492
    unsigned char digit;
8✔
493

494
    if (text == NULL || value_out == NULL || length == 0) {
16!
495
        return 0;
496
    }
497

498
    value = 0;
8✔
499
    index = 0;
8✔
500
    while (index < length) {
48!
501
        digit = (unsigned char)text[index];
32✔
502
        if (digit < (unsigned char)'0' || digit > (unsigned char)'9') {
32!
503
            return 0;
504
        }
505
        if (value > (INT_MAX - 9) / 10) {
32!
506
            return 0;
507
        }
508
        value = value * 10 + (digit - (unsigned char)'0');
32✔
509
        ++index;
32✔
510
    }
511

512
    if (value <= 0) {
16!
513
        return 0;
514
    }
515

516
    *value_out = value;
16✔
517
    return 1;
16✔
518
}
2✔
519

520
static void
521
loader_apply_loader_suboptions(char const *order)
14,269✔
522
{
523
    char const *cursor;
8,400✔
524
    char const *token_start;
8,400✔
525
    char const *token_end;
8,400✔
526
    char const *order_end;
8,400✔
527
    size_t token_length;
8,400✔
528
    char token_buffer[256];
8,400✔
529
    char match_detail[128];
8,400✔
530
    sixel_option_argument_resolution_t resolution;
8,400✔
531
    size_t assignment_index;
8,400✔
532
    int parsed_value;
8,400✔
533

534
    cursor = order;
14,269✔
535
    token_start = order;
14,269✔
536
    token_end = order;
14,269✔
537
    order_end = NULL;
14,269✔
538
    token_length = 0;
14,269✔
539
    token_buffer[0] = '\0';
14,269✔
540
    match_detail[0] = '\0';
14,269✔
541
    resolution.resolved_base_value = 0;
14,269✔
542
    resolution.base_def = NULL;
14,269✔
543
    resolution.assignments = NULL;
14,269✔
544
    resolution.assignment_count = 0u;
14,269✔
545
    assignment_index = 0u;
14,269✔
546
    parsed_value = 0;
14,269✔
547

548
    sixel_helper_set_wic_ico_minsize(0);
14,269✔
549

550
    if (order == NULL || order[0] == '\0') {
14,269!
551
        return;
12,418✔
552
    }
553

554
    order_end = order + strlen(order);
1,851✔
555
    while (order_end > order && isspace((unsigned char)order_end[-1])) {
1,851!
556
        --order_end;
×
557
    }
558
    if (order_end > order && order_end[-1] == '!') {
1,851!
559
        --order_end;
1,851✔
560
    }
1,060✔
561

562
    token_start = order;
1,851✔
563
    cursor = order;
1,851✔
564
    while (cursor <= order_end) {
17,626✔
565
        if (cursor == order_end || *cursor == ',') {
15,775!
566
            token_end = cursor;
1,851✔
567
            while (token_start < token_end &&
1,851!
568
                   isspace((unsigned char)*token_start)) {
1,851!
569
                ++token_start;
×
570
            }
571
            while (token_end > token_start &&
1,851!
572
                   isspace((unsigned char)token_end[-1])) {
1,851!
573
                --token_end;
×
574
            }
575
            token_length = (size_t)(token_end - token_start);
1,851✔
576
            if (token_length > 0 && token_length < sizeof(token_buffer)) {
1,851!
577
                memcpy(token_buffer, token_start, token_length);
1,851✔
578
                token_buffer[token_length] = '\0';
1,851✔
579
                if (SIXEL_SUCCEEDED(sixel_option_parse_argument_with_suboptions(
2,820!
580
                        token_buffer,
581
                        &g_schema_loaders_loader,
582
                        &resolution,
583
                        match_detail,
584
                        sizeof(match_detail))) &&
1,851!
585
                    resolution.base_def != NULL &&
1,851!
586
                    strcmp(resolution.base_def->name, "wic") == 0) {
1,851!
587
                    assignment_index = 0u;
136✔
588
                    while (assignment_index < resolution.assignment_count) {
288!
589
                        if (strcmp(resolution.assignments[assignment_index]
18!
590
                                   .resolved_key_name,
2✔
591
                                   "ico_minsize") == 0 &&
16!
592
                            loader_parse_positive_int(
16✔
593
                                resolution.assignments[assignment_index]
8✔
594
                                .resolved_value_text,
8✔
595
                                strlen(resolution.assignments[assignment_index]
10✔
596
                                       .resolved_value_text),
16✔
597
                                &parsed_value)) {
598
                            sixel_helper_set_wic_ico_minsize(parsed_value);
16✔
599
                        }
2✔
600
                        ++assignment_index;
16✔
601
                    }
602
                }
34✔
603
                sixel_option_free_argument_resolution(&resolution);
1,851✔
604
            }
1,060✔
605
            token_start = cursor + 1;
1,851✔
606
        }
1,060✔
607
        ++cursor;
15,775✔
608
    }
609
}
6,997!
610

611
static int
612
loader_token_matches(char const *token,
3,824✔
613
                     size_t token_length,
614
                     char const *name)
615
{
616
    size_t index;
2,297✔
617
    unsigned char left;
2,297✔
618
    unsigned char right;
2,297✔
619

620
    for (index = 0; index < token_length && name[index] != '\0'; ++index) {
17,795!
621
        left = (unsigned char)token[index];
15,944✔
622
        right = (unsigned char)name[index];
15,944✔
623
        if (tolower(left) != tolower(right)) {
15,944✔
624
            return 0;
1,797✔
625
        }
626
    }
8,986✔
627

628
    if (index != token_length || name[index] != '\0') {
1,851!
629
        return 0;
×
630
    }
631

632
    return 1;
1,333✔
633
}
2,755✔
634

635
static sixel_loader_entry_t const *
636
loader_lookup_token(char const *token,
1,851✔
637
                    size_t token_length,
638
                    sixel_loader_entry_t const *entries,
639
                    size_t entry_count)
640
{
641
    size_t index;
1,102✔
642

643
    for (index = 0; index < entry_count; ++index) {
3,824!
644
        if (loader_token_matches(token,
6,579✔
645
                                 token_length,
2,755✔
646
                                 entries[index].name)) {
3,824✔
647
            return &entries[index];
1,333✔
648
        }
649
    }
1,695✔
650

651
    return NULL;
652
}
1,060✔
653

654
/*
655
 * loader_build_plan
656
 *
657
 * Translate a comma separated list into an execution plan that reorders the
658
 * runtime loader chain.  Tokens are matched case-insensitively.  Unknown names
659
 * are ignored so that new builds remain forward compatible.
660
 *
661
 * When the input ends with "!", the default fallback list is suppressed so
662
 * only the explicit tokens are tried.
663
 *
664
 *    user input "gd,coregraphics"
665
 *                |
666
 *                v
667
 *        +-------------------+
668
 *        | prioritized list  |
669
 *        +-------------------+
670
 *                |
671
 *                v
672
 *        +-------------------+
673
 *        | default fallbacks |
674
 *        +-------------------+
675
 */
676
static size_t
677
loader_build_plan(char const *order,
14,269✔
678
                  sixel_loader_entry_t const *entries,
679
                  size_t entry_count,
680
                  sixel_loader_entry_t const **plan,
681
                  size_t plan_capacity)
682
{
683
    size_t plan_length;
8,400✔
684
    size_t index;
8,400✔
685
    char const *cursor;
8,400✔
686
    char const *token_start;
8,400✔
687
    char const *token_end;
8,400✔
688
    char const *order_end;
8,400✔
689
    size_t token_length;
8,400✔
690
    sixel_loader_entry_t const *entry;
8,400✔
691
    size_t limit;
8,400✔
692
    int allow_fallback;
8,400✔
693

694
    plan_length = 0;
14,269✔
695
    index = 0;
14,269✔
696
    cursor = order;
14,269✔
697
    token_start = order;
14,269✔
698
    token_end = order;
14,269✔
699
    order_end = NULL;
14,269✔
700
    token_length = 0;
14,269✔
701
    entry = NULL;
14,269✔
702
    limit = plan_capacity;
14,269✔
703
    allow_fallback = 1;
14,269✔
704

705
    if (order != NULL) {
14,269✔
706
        order_end = order + strlen(order);
1,851✔
707
        while (order_end > order &&
1,851!
708
               isspace((unsigned char)order_end[-1])) {
1,851!
709
            --order_end;
×
710
        }
711
        if (order_end > order && order_end[-1] == '!') {
1,851!
712
            allow_fallback = 0;
1,851✔
713
            --order_end;
1,851✔
714
            while (order_end > order &&
1,851!
715
                   isspace((unsigned char)order_end[-1])) {
1,851!
716
                --order_end;
×
717
            }
718
        }
1,060✔
719
    }
1,060✔
720

721
    if (order != NULL && plan != NULL && plan_capacity > 0) {
13,443!
722
        token_start = order;
1,333✔
723
        cursor = order;
1,333✔
724
        while (cursor < order_end) {
15,775✔
725
            if (*cursor == ',') {
13,924!
726
                token_end = cursor;
×
727
                while (token_start < token_end &&
×
728
                       isspace((unsigned char)*token_start)) {
×
729
                    ++token_start;
×
730
                }
731
                while (token_end > token_start &&
×
732
                       isspace((unsigned char)token_end[-1])) {
×
733
                    --token_end;
×
734
                }
735
                token_length = (size_t)(token_end - token_start);
×
736
                if (token_length > 0) {
×
737
                    entry = loader_lookup_token(token_start,
×
738
                                                loader_token_name_length(
739
                                                    token_start,
740
                                                    token_length),
741
                                                entries,
742
                                                entry_count);
743
                    if (entry != NULL &&
×
744
                        !loader_plan_contains(plan,
×
745
                                              plan_length,
746
                                              entry) &&
×
747
                        plan_length < limit) {
748
                        plan[plan_length] = entry;
×
749
                        ++plan_length;
×
750
                    }
751
                }
752
                token_start = cursor + 1;
×
753
            }
754
            ++cursor;
13,924✔
755
        }
756

757
        token_end = order_end;
1,851✔
758
        while (token_start < token_end &&
1,851!
759
               isspace((unsigned char)*token_start)) {
1,851!
760
            ++token_start;
×
761
        }
762
        while (token_end > token_start &&
1,851!
763
               isspace((unsigned char)token_end[-1])) {
1,851!
764
            --token_end;
×
765
        }
766
        token_length = (size_t)(token_end - token_start);
1,851✔
767
        if (token_length > 0) {
1,851!
768
            entry = loader_lookup_token(token_start,
2,911✔
769
                                        loader_token_name_length(
1,060✔
770
                                            token_start,
1,060✔
771
                                            token_length),
1,060✔
772
                                        entries,
1,060✔
773
                                        entry_count);
1,060✔
774
            if (entry != NULL &&
3,093!
775
                !loader_plan_contains(plan, plan_length, entry) &&
2,124!
776
                plan_length < limit) {
1,060✔
777
                plan[plan_length] = entry;
1,851✔
778
                ++plan_length;
1,851✔
779
            }
1,060✔
780
        }
1,060✔
781
    }
1,060✔
782

783
    if (allow_fallback) {
14,080✔
784
        for (index = 0; index < entry_count && plan_length < limit; ++index) {
57,002✔
785
            entry = &entries[index];
44,584✔
786
            if (!entry->default_enabled) {
44,584✔
787
                continue;
6,558✔
788
            }
789
            if (!loader_plan_contains(plan, plan_length, entry)) {
38,026!
790
                plan[plan_length] = entry;
38,026✔
791
                ++plan_length;
38,026✔
792
            }
26,418✔
793
        }
26,418✔
794
    }
5,937✔
795

796
    return plan_length;
17,869✔
797
}
3,600✔
798

799
static void
800
loader_append_chunk(char *dest,
206✔
801
                    size_t capacity,
802
                    size_t *offset,
803
                    char const *chunk)
804
{
805
    size_t available;
132✔
806
    size_t length;
132✔
807

808
    if (dest == NULL || offset == NULL || chunk == NULL) {
206!
809
        return;
810
    }
811

812
    if (*offset >= capacity) {
206!
813
        return;
814
    }
815

816
    available = capacity - *offset;
206✔
817
    if (available == 0) {
206✔
818
        return;
819
    }
820

821
    length = strlen(chunk);
206✔
822
    if (length >= available) {
206!
823
        if (available == 0) {
×
824
            return;
825
        }
826
        length = available - 1u;
×
827
    }
828

829
    if (length > 0) {
206!
830
        memcpy(dest + *offset, chunk, length);
206✔
831
        *offset += length;
206✔
832
    }
206✔
833

834
    if (*offset < capacity) {
206!
835
        dest[*offset] = '\0';
206✔
836
    } else {
206✔
837
        dest[capacity - 1u] = '\0';
×
838
    }
839
}
206!
840

841
static void
842
loader_append_key_value(char *dest,
98✔
843
                        size_t capacity,
844
                        size_t *offset,
845
                        char const *label,
846
                        char const *value)
847
{
848
    char line[128];
60✔
849
    int written;
60✔
850

851
    if (value == NULL || value[0] == '\0') {
98!
852
        return;
×
853
    }
854

855
    written = sixel_compat_snprintf(line,
196✔
856
                                    sizeof(line),
857
                                    "  %-10s: %s\n",
858
                                    label,
98✔
859
                                    value);
98✔
860
    if (written < 0) {
98!
861
        return;
862
    }
863

864
    if ((size_t)written >= sizeof(line)) {
98!
865
        line[sizeof(line) - 1u] = '\0';
×
866
    }
867

868
    loader_append_chunk(dest, capacity, offset, line);
98✔
869
}
98!
870

871
static void
872
loader_extract_extension(char const *path, char *buffer, size_t capacity)
27✔
873
{
874
    char const *dot;
18✔
875
    size_t index;
18✔
876

877
    if (buffer == NULL || capacity == 0) {
27!
878
        return;
879
    }
880

881
    buffer[0] = '\0';
27✔
882

883
    if (path == NULL) {
27!
884
        return;
885
    }
886

887
    dot = strrchr(path, '.');
27✔
888
    if (dot == NULL || dot[1] == '\0') {
27!
889
        return;
15✔
890
    }
891

892
#if defined(_WIN32)
893
    {
894
        char const *slash;
895
        char const *backslash;
896

897
        slash = strrchr(path, '/');
898
        backslash = strrchr(path, '\\');
899
        if ((slash != NULL && dot < slash) ||
900
                (backslash != NULL && dot < backslash)) {
901
            return;
902
        }
903
    }
904
#else
905
    {
906
        char const *slash;
8✔
907

908
        slash = strrchr(path, '/');
12✔
909
        if (slash != NULL && dot < slash) {
12!
910
            return;
911
        }
912
    }
8!
913
#endif
914

915
    if (dot[1] == '\0') {
12✔
916
        return;
917
    }
918

919
    dot += 1;
12✔
920

921
    for (index = 0; index + 1 < capacity && dot[index] != '\0'; ++index) {
48!
922
        buffer[index] = (char)tolower((unsigned char)dot[index]);
36✔
923
    }
36✔
924
    buffer[index] = '\0';
12✔
925
}
27!
926

927

928

929

930

931

932

933

934

935

936

937
#if HAVE_COREGRAPHICS && HAVE_QUICKLOOK
938
static void
939
loader_copy_cfstring(CFStringRef source, char *buffer, size_t capacity)
16✔
940
{
941
    if (buffer == NULL || capacity == 0) {
16!
942
        return;
943
    }
944

945
    buffer[0] = '\0';
16✔
946
    if (source == NULL) {
16!
947
        return;
948
    }
949

950
    if (!CFStringGetCString(source,
32!
951
                             buffer,
16✔
952
                             (CFIndex)capacity,
16✔
953
                             kCFStringEncodingUTF8)) {
954
        buffer[0] = '\0';
955
    }
956
}
16✔
957
#endif
958

959

960
static void
961
loader_publish_diagnostic(sixel_chunk_t const *pchunk,
27✔
962
                          char const *filename)
963
{
964
    enum { description_length = 128 };
965
    enum { uttype_length = 128 };
966
    enum { extension_length = 32 };
967
    enum { message_length = 768 };
968
    char message[message_length];
18✔
969
    char type_value[description_length];
18✔
970
    char extension_text[extension_length + 2];
18✔
971
    char uttype[uttype_length];
18✔
972
    char desc_buffer[description_length];
18✔
973
    char extension[extension_length];
18✔
974
    char const *path;
18✔
975
    char const *display_path;
18✔
976
    char const *metadata_path;
18✔
977
    char const *description_text;
18✔
978
    char *mime_string;
18✔
979
    char *description_string;
18✔
980
    size_t offset;
18✔
981
    int gnome_available;
18✔
982
    int gnome_has_dirs;
18✔
983
    int gnome_has_match;
18✔
984
    int suggestions;
18✔
985

986
    message[0] = '\0';
27✔
987
    type_value[0] = '\0';
27✔
988
    extension_text[0] = '\0';
27✔
989
    uttype[0] = '\0';
27✔
990
    desc_buffer[0] = '\0';
27✔
991
    extension[0] = '\0';
27✔
992
    path = NULL;
27✔
993
    display_path = "(stdin)";
27✔
994
    metadata_path = NULL;
27✔
995
    description_text = NULL;
27✔
996
    mime_string = NULL;
27✔
997
    description_string = NULL;
27✔
998
    offset = 0u;
27✔
999
    gnome_available = 0;
27✔
1000
    gnome_has_dirs = 0;
27✔
1001
    gnome_has_match = 0;
27✔
1002
    suggestions = 0;
27✔
1003

1004
    if (pchunk != NULL && pchunk->source_path != NULL) {
27!
1005
        path = pchunk->source_path;
24✔
1006
    } else if (filename != NULL) {
27!
1007
        path = filename;
3✔
1008
    }
3✔
1009

1010
    if (path != NULL && strcmp(path, "-") != 0) {
27!
1011
        display_path = path;
24✔
1012
    }
24✔
1013

1014
    if (path != NULL && strcmp(path, "-") != 0 &&
27!
1015
            strstr(path, "://") == NULL) {
24!
1016
        metadata_path = path;
24✔
1017
    }
24✔
1018

1019
    loader_extract_extension(path, extension, sizeof(extension));
26✔
1020

1021
#if HAVE_FREEDESKTOP_THUMBNAILING
1022
    if (metadata_path != NULL) {
26!
1023
        /*
1024
         * Collect MIME metadata via file(1) when fork() and friends are
1025
         * available.  Windows builds compiled with clang64 lack these
1026
         * interfaces, so the thumbnail helpers remain disabled there.
1027
         */
1028
        mime_string = thumbnailer_guess_content_type(metadata_path);
24✔
1029
        description_string = thumbnailer_run_file(metadata_path, NULL);
24✔
1030
    }
24✔
1031
#else
1032
    (void)metadata_path;
1033
#endif
1034

1035
#if HAVE_COREGRAPHICS && HAVE_QUICKLOOK
1036
#if defined(__clang__)
1037
    /*
1038
     * Allow use of legacy UTType C APIs when compiling with the
1039
     * macOS 12 SDK.  The replacement interfaces are Objective-C only,
1040
     * so we must intentionally silence the deprecation warnings here.
1041
     */
1042
#pragma clang diagnostic push
1043
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
1044
#endif
1045
    {
1046
        CFStringRef uti_ref;
1047
        CFStringRef mime_ref;
1048
        CFStringRef ext_ref;
1049
        CFStringRef desc_ref;
1050
        CFStringRef preferred_mime;
1051
        char uti_local[uttype_length];
1052
        char desc_local[description_length];
1053
        char mime_local[64];
1054

1055
        uti_ref = NULL;
8✔
1056
        mime_ref = NULL;
8✔
1057
        ext_ref = NULL;
8✔
1058
        desc_ref = NULL;
8✔
1059
        preferred_mime = NULL;
8✔
1060
        uti_local[0] = '\0';
8✔
1061
        desc_local[0] = '\0';
8✔
1062
        mime_local[0] = '\0';
8✔
1063

1064
        if (mime_string != NULL) {
8!
1065
            mime_ref = CFStringCreateWithCString(kCFAllocatorDefault,
16✔
1066
                                                 mime_string,
8✔
1067
                                                 kCFStringEncodingUTF8);
1068
        }
8✔
1069
        if (mime_ref != NULL) {
8!
1070
            uti_ref = UTTypeCreatePreferredIdentifierForTag(
8✔
1071
                kUTTagClassMIMEType,
8✔
1072
                mime_ref,
8✔
1073
                NULL);
1074
        }
8✔
1075
        if (uti_ref == NULL && extension[0] != '\0') {
15!
1076
            ext_ref = CFStringCreateWithCString(kCFAllocatorDefault,
1077
                                                extension,
1078
                                                kCFStringEncodingUTF8);
1079
            if (ext_ref != NULL) {
×
1080
                uti_ref = UTTypeCreatePreferredIdentifierForTag(
1081
                    kUTTagClassFilenameExtension,
1082
                    ext_ref,
1083
                    NULL);
1084
            }
1085
        }
1086
        if (uti_ref != NULL) {
8!
1087
            loader_copy_cfstring(uti_ref, uti_local, sizeof(uti_local));
8✔
1088
            desc_ref = UTTypeCopyDescription(uti_ref);
8✔
1089
            if (desc_ref != NULL) {
8!
1090
                loader_copy_cfstring(desc_ref,
16✔
1091
                                     desc_local,
8✔
1092
                                     sizeof(desc_local));
1093
                CFRelease(desc_ref);
8✔
1094
                desc_ref = NULL;
8✔
1095
            }
8✔
1096
            if (mime_string == NULL) {
16!
1097
                preferred_mime = UTTypeCopyPreferredTagWithClass(
1098
                    uti_ref,
1099
                    kUTTagClassMIMEType);
1100
                if (preferred_mime != NULL) {
×
1101
                    loader_copy_cfstring(preferred_mime,
1102
                                         mime_local,
1103
                                         sizeof(mime_local));
1104
                    CFRelease(preferred_mime);
1105
                    preferred_mime = NULL;
1106
                }
1107
                if (mime_local[0] != '\0') {
×
1108
                    mime_string = thumbnailer_strdup(mime_local);
1109
                }
1110
            }
1111
        }
8✔
1112
        if (mime_ref != NULL) {
8!
1113
            CFRelease(mime_ref);
8✔
1114
        }
8✔
1115
        if (ext_ref != NULL) {
16!
1116
            CFRelease(ext_ref);
1117
        }
1118
        if (uti_ref != NULL) {
8!
1119
            CFRelease(uti_ref);
8✔
1120
        }
8✔
1121
        if (uti_local[0] != '\0') {
8!
1122
            sixel_compat_snprintf(uttype,
16✔
1123
                                  sizeof(uttype),
1124
                                  "%s",
1125
                                  uti_local);
8✔
1126
        }
8✔
1127
        if (desc_local[0] != '\0') {
8!
1128
            sixel_compat_snprintf(desc_buffer,
16✔
1129
                                  sizeof(desc_buffer),
1130
                                  "%s",
1131
                                  desc_local);
8✔
1132
        }
8✔
1133
    }
1134
#if defined(__clang__)
1135
#pragma clang diagnostic pop
1136
#endif
1137
#endif
1138

1139
    if (description_string != NULL && description_string[0] != '\0') {
26!
1140
        description_text = description_string;
24✔
1141
    } else if (desc_buffer[0] != '\0') {
27!
1142
        description_text = desc_buffer;
1143
    } else {
1144
        description_text = "unknown content";
3✔
1145
    }
1146

1147
    sixel_compat_snprintf(type_value,
54✔
1148
                          sizeof(type_value),
1149
                          "%s",
1150
                          description_text);
27✔
1151

1152
    loader_append_chunk(message,
27✔
1153
                        sizeof(message),
1154
                        &offset,
1155
                        "diagnostic:\n");
1156
    loader_append_key_value(message,
54✔
1157
                            sizeof(message),
1158
                            &offset,
1159
                            "file",
1160
                            display_path);
27✔
1161
    loader_append_key_value(message,
54✔
1162
                            sizeof(message),
1163
                            &offset,
1164
                            "type",
1165
                            type_value);
27✔
1166

1167
    if (mime_string != NULL && mime_string[0] != '\0') {
27!
1168
        loader_append_key_value(message,
48✔
1169
                                sizeof(message),
1170
                                &offset,
1171
                                "mime",
1172
                                mime_string);
24✔
1173
    }
24✔
1174

1175
    if (uttype[0] != '\0') {
26!
1176
        loader_append_key_value(message,
16✔
1177
                                sizeof(message),
1178
                                &offset,
1179
                                "uti",
1180
                                uttype);
8✔
1181
    }
8✔
1182

1183
    if (extension[0] != '\0') {
30!
1184
        sixel_compat_snprintf(extension_text,
24✔
1185
                              sizeof(extension_text),
1186
                              ".%s",
1187
                              extension);
12✔
1188
        loader_append_key_value(message,
24✔
1189
                                sizeof(message),
1190
                                &offset,
1191
                                "extension",
1192
                                extension_text);
12✔
1193
    }
12✔
1194

1195
    loader_append_chunk(message,
27✔
1196
                        sizeof(message),
1197
                        &offset,
1198
                        "  suggestions:\n");
1199

1200
#if HAVE_COREGRAPHICS && HAVE_QUICKLOOK
1201
    int quicklook_available;
1202
    int quicklook_supported;
1203

1204
    quicklook_available = 0;
9✔
1205
    quicklook_supported = 0;
9✔
1206

1207
    quicklook_available = loader_registry_entry_available("quicklook");
9✔
1208
    if (quicklook_available) {
9!
1209
        quicklook_supported = loader_quicklook_can_decode(pchunk, filename);
9✔
1210
    }
9✔
1211
    if (quicklook_supported) {
18!
1212
        loader_append_chunk(message,
1213
                            sizeof(message),
1214
                            &offset,
1215
                            "    - QuickLook rendered a preview during "
1216
                            "the probe; try -L quicklook.\n");
1217
        suggestions += 1;
1218
    }
1219
#endif
1220

1221
#if HAVE_FREEDESKTOP_THUMBNAILING
1222
    gnome_available = loader_registry_entry_available("gnome-thumbnailer");
27✔
1223
    if (gnome_available) {
27!
1224
        loader_probe_gnome_thumbnailers(mime_string,
27✔
1225
                                        &gnome_has_dirs,
1226
                                        &gnome_has_match);
1227
        if (gnome_has_dirs && gnome_has_match) {
27!
1228
            loader_append_chunk(message,
1229
                                sizeof(message),
1230
                                &offset,
1231
                                "    - GNOME thumbnailer definitions match "
1232
                                "this MIME type; try -L gnome-thumbnailer.\n"
1233
                                );
1234
            suggestions += 1;
1235
        }
1236
    }
27✔
1237
#else
1238
    (void)gnome_available;
1239
    (void)gnome_has_dirs;
1240
    (void)gnome_has_match;
1241
#endif
1242

1243
    if (suggestions == 0) {
27!
1244
        loader_append_chunk(message,
27✔
1245
                            sizeof(message),
1246
                            &offset,
1247
                            "    (no thumbnail helper hints)\n");
1248
    }
27✔
1249

1250
    if (suggestions > 0) {
27!
1251
        loader_append_chunk(message,
1252
                            sizeof(message),
1253
                            &offset,
1254
                            "  hint       : Enable one of the suggested "
1255
                            "loaders with -L.\n");
1256
    } else {
1257
        loader_append_chunk(message,
27✔
1258
                            sizeof(message),
1259
                            &offset,
1260
                            "  hint       : Convert the file to PNG or "
1261
                            "enable optional loaders.\n");
1262
    }
1263

1264
    sixel_helper_set_additional_message(message);
27✔
1265

1266
    free(mime_string);
27✔
1267
    free(description_string);
27✔
1268
}
27✔
1269

1270
SIXELAPI SIXELSTATUS
1271
sixel_loader_new(
14,325✔
1272
    sixel_loader_t   /* out */ **pploader,
1273
    sixel_allocator_t/* in */  *allocator)
1274
{
1275
    SIXELSTATUS status = SIXEL_FALSE;
14,325✔
1276
    sixel_loader_t *loader;
8,428✔
1277
    sixel_allocator_t *local_allocator;
8,428✔
1278

1279
    loader = NULL;
14,325✔
1280
    local_allocator = allocator;
14,325✔
1281

1282
    if (pploader == NULL) {
14,325!
1283
        sixel_helper_set_additional_message(
×
1284
            "sixel_loader_new: pploader is null.");
1285
        status = SIXEL_BAD_ARGUMENT;
×
1286
        goto end;
×
1287
    }
1288

1289
    if (local_allocator == NULL) {
14,325!
1290
        status = sixel_allocator_new(&local_allocator,
×
1291
                                     NULL,
1292
                                     NULL,
1293
                                     NULL,
1294
                                     NULL);
1295
        if (SIXEL_FAILED(status)) {
×
1296
            goto end;
×
1297
        }
1298
    } else {
1299
        sixel_allocator_ref(local_allocator);
14,325✔
1300
    }
1301

1302
    loader = (sixel_loader_t *)sixel_allocator_malloc(local_allocator,
14,325✔
1303
                                                      sizeof(*loader));
1304
    if (loader == NULL) {
14,325!
1305
        sixel_helper_set_additional_message(
×
1306
            "sixel_loader_new: sixel_allocator_malloc() failed.");
1307
        status = SIXEL_BAD_ALLOCATION;
×
1308
        sixel_allocator_unref(local_allocator);
×
1309
        goto end;
×
1310
    }
1311

1312
    loader->ref = 1U;
14,325✔
1313
    loader->fstatic = 0;
14,325✔
1314
    loader->fuse_palette = 0;
14,325✔
1315
    loader->reqcolors = SIXEL_PALETTE_MAX;
14,325✔
1316
    loader->bgcolor[0] = 0;
14,325✔
1317
    loader->bgcolor[1] = 0;
14,325✔
1318
    loader->bgcolor[2] = 0;
14,325✔
1319
    loader->has_bgcolor = 0;
14,325✔
1320
    loader->loop_control = SIXEL_LOOP_AUTO;
14,325✔
1321
    loader->finsecure = 0;
14,325✔
1322
    loader->has_start_frame_no = 0;
14,325✔
1323
    loader->start_frame_no = INT_MIN;
14,325✔
1324
    loader->cancel_flag = NULL;
14,325✔
1325
    loader->context = NULL;
14,325✔
1326
    /*
1327
     * Initialize a private logger. The helper reuses an existing global
1328
     * logger sink when present so loader markers share the timeline with
1329
     * upstream stages without requiring sixel_loader_setopt().
1330
     */
1331
    sixel_logger_init(&loader->logger);
14,325✔
1332
    (void)sixel_logger_prepare_env(&loader->logger);
14,325✔
1333
    loader->loader_order = NULL;
14,325✔
1334
    loader->allocator = local_allocator;
14,325✔
1335
    loader->last_loader_name[0] = '\0';
14,325✔
1336
    loader->last_source_path[0] = '\0';
14,325✔
1337
    loader->last_input_bytes = 0u;
14,325✔
1338
    loader->callback_failed = 0;
14,325✔
1339
    loader->log_loader_finished = 0;
14,325✔
1340
    loader->log_path[0] = '\0';
14,325✔
1341
    loader->log_loader_name[0] = '\0';
14,325✔
1342
    loader->log_input_bytes = 0u;
14,325✔
1343

1344
    *pploader = loader;
14,325✔
1345
    status = SIXEL_OK;
14,325✔
1346

1347
end:
7,296✔
1348
    return status;
17,941✔
1349
}
3,616✔
1350

1351
SIXELAPI void
1352
sixel_loader_ref(
129,455✔
1353
    sixel_loader_t /* in */ *loader)
1354
{
1355
    if (loader == NULL) {
129,455!
1356
        return;
1357
    }
1358

1359
    (void)sixel_atomic_fetch_add_u32(&loader->ref, 1U);
129,455✔
1360
}
63,743✔
1361

1362
SIXELAPI void
1363
sixel_loader_unref(
143,780✔
1364
    sixel_loader_t /* in */ *loader)
1365
{
1366
    sixel_allocator_t *allocator;
84,608✔
1367
    unsigned int previous;
84,608✔
1368

1369
    if (loader == NULL) {
143,780!
1370
        return;
1371
    }
1372

1373
    previous = sixel_atomic_fetch_sub_u32(&loader->ref, 1U);
143,780✔
1374
    if (previous == 1U) {
143,780✔
1375
        allocator = loader->allocator;
14,325✔
1376
        sixel_logger_close(&loader->logger);
14,325✔
1377
        sixel_allocator_free(allocator, loader->loader_order);
14,325✔
1378
        sixel_allocator_free(allocator, loader);
14,325✔
1379
        sixel_allocator_unref(allocator);
14,325✔
1380
    }
7,029✔
1381
}
70,772!
1382

1383
SIXELAPI SIXELSTATUS
1384
sixel_loader_setopt(
115,130✔
1385
    sixel_loader_t /* in */ *loader,
1386
    int            /* in */ option,
1387
    void const     /* in */ *value)
1388
{
1389
    SIXELSTATUS status = SIXEL_FALSE;
115,130✔
1390
    int const *flag;
67,752✔
1391
    unsigned char const *color;
67,752✔
1392
    char const *order;
67,752✔
1393
    char *copy;
67,752✔
1394
    sixel_allocator_t *allocator;
67,752✔
1395

1396
    flag = NULL;
115,130✔
1397
    color = NULL;
115,130✔
1398
    order = NULL;
115,130✔
1399
    copy = NULL;
115,130✔
1400
    allocator = NULL;
115,130✔
1401

1402
    if (loader == NULL) {
115,130!
1403
        sixel_helper_set_additional_message(
×
1404
            "sixel_loader_setopt: loader is null.");
1405
        status = SIXEL_BAD_ARGUMENT;
×
1406
        goto end0;
×
1407
    }
1408

1409
    sixel_loader_ref(loader);
115,130✔
1410

1411
    switch (option) {
115,130!
1412
    case SIXEL_LOADER_OPTION_REQUIRE_STATIC:
7,296✔
1413
        flag = (int const *)value;
14,325✔
1414
        loader->fstatic = flag != NULL ? *flag : 0;
14,325!
1415
        status = SIXEL_OK;
14,325✔
1416
        break;
14,325✔
1417
    case SIXEL_LOADER_OPTION_USE_PALETTE:
7,296✔
1418
        flag = (int const *)value;
14,325✔
1419
        loader->fuse_palette = flag != NULL ? *flag : 0;
14,325!
1420
        status = SIXEL_OK;
14,325✔
1421
        break;
14,325✔
1422
    case SIXEL_LOADER_OPTION_REQCOLORS:
7,296✔
1423
        flag = (int const *)value;
14,325✔
1424
        loader->reqcolors = flag != NULL ? *flag : SIXEL_PALETTE_MAX;
14,325!
1425
        if (loader->reqcolors < 1) {
14,325!
1426
            sixel_helper_set_additional_message(
×
1427
                "sixel_loader_setopt: reqcolors must be 1 or greater.");
1428
            status = SIXEL_BAD_ARGUMENT;
×
1429
            goto end;
×
1430
        }
1431
        if (loader->reqcolors > SIXEL_PALETTE_MAX) {
14,325!
1432
            loader->reqcolors = SIXEL_PALETTE_MAX;
×
1433
        }
1434
        status = SIXEL_OK;
9,513✔
1435
        break;
9,513✔
1436
    case SIXEL_LOADER_OPTION_BGCOLOR:
3,660✔
1437
        if (value == NULL) {
7,295✔
1438
            loader->has_bgcolor = 0;
7,159✔
1439
        } else {
3,544✔
1440
            color = (unsigned char const *)value;
136✔
1441
            loader->bgcolor[0] = color[0];
136✔
1442
            loader->bgcolor[1] = color[1];
136✔
1443
            loader->bgcolor[2] = color[2];
136✔
1444
            loader->has_bgcolor = 1;
136✔
1445
        }
1446
        status = SIXEL_OK;
4,871✔
1447
        break;
4,871✔
1448
    case SIXEL_LOADER_OPTION_LOOP_CONTROL:
7,296✔
1449
        flag = (int const *)value;
14,325✔
1450
        loader->loop_control = flag != NULL ? *flag : SIXEL_LOOP_AUTO;
14,325!
1451
        status = SIXEL_OK;
14,325✔
1452
        break;
14,325✔
1453
    case SIXEL_LOADER_OPTION_INSECURE:
7,296✔
1454
        flag = (int const *)value;
14,325✔
1455
        loader->finsecure = flag != NULL ? *flag : 0;
14,325!
1456
        status = SIXEL_OK;
14,325✔
1457
        break;
14,325✔
1458
    case SIXEL_LOADER_OPTION_START_FRAME_NO:
3,660✔
1459
        if (value == NULL) {
7,295✔
1460
            loader->has_start_frame_no = 0;
7,145✔
1461
            loader->start_frame_no = INT_MIN;
7,145✔
1462
        } else {
3,529✔
1463
            flag = (int const *)value;
150✔
1464
            loader->start_frame_no = *flag;
150✔
1465
            loader->has_start_frame_no = 1;
150✔
1466
        }
1467
        status = SIXEL_OK;
4,871✔
1468
        break;
4,871✔
1469
    case SIXEL_LOADER_OPTION_CANCEL_FLAG:
3,660✔
1470
        loader->cancel_flag = (int const *)value;
7,295✔
1471
        status = SIXEL_OK;
7,295✔
1472
        break;
7,295✔
1473
    case SIXEL_LOADER_OPTION_LOADER_ORDER:
3,660✔
1474
        allocator = loader->allocator;
7,295✔
1475
        sixel_allocator_free(allocator, loader->loader_order);
7,295✔
1476
        loader->loader_order = NULL;
7,295✔
1477
        if (value != NULL) {
7,295✔
1478
            order = (char const *)value;
1,842✔
1479
            copy = loader_strdup(order, allocator);
1,842✔
1480
            if (copy == NULL) {
1,842!
1481
                sixel_helper_set_additional_message(
×
1482
                    "sixel_loader_setopt: loader_strdup() failed.");
1483
                status = SIXEL_BAD_ALLOCATION;
×
1484
                goto end;
×
1485
            }
1486
            loader->loader_order = copy;
1,842✔
1487
        }
1,053✔
1488
        status = SIXEL_OK;
4,871✔
1489
        break;
4,871✔
1490
    case SIXEL_LOADER_OPTION_CONTEXT:
7,296✔
1491
        loader->context = (void *)value;
14,325✔
1492
        status = SIXEL_OK;
14,325✔
1493
        break;
14,325✔
1494
    default:
1495
        sixel_helper_set_additional_message(
×
1496
            "sixel_loader_setopt: unknown option.");
1497
        status = SIXEL_BAD_ARGUMENT;
×
1498
        goto end;
×
1499
    }
56,714✔
1500

1501
end:
58,416✔
1502
    sixel_loader_unref(loader);
115,130✔
1503

1504
end0:
58,416✔
1505
    return status;
144,314✔
1506
}
29,184✔
1507

1508
SIXELAPI char const *
1509
sixel_loader_get_last_success_name(sixel_loader_t const *loader)
13,384✔
1510
{
1511
    if (loader == NULL || loader->last_loader_name[0] == '\0') {
13,384!
1512
        return NULL;
1513
    }
1514
    return loader->last_loader_name;
13,384✔
1515
}
6,634✔
1516

1517
SIXELAPI char const *
1518
sixel_loader_get_last_source_path(sixel_loader_t const *loader)
13,111✔
1519
{
1520
    if (loader == NULL || loader->last_source_path[0] == '\0') {
13,111!
1521
        return NULL;
183✔
1522
    }
1523
    return loader->last_source_path;
12,838✔
1524
}
6,499✔
1525

1526
SIXELAPI size_t
1527
sixel_loader_get_last_input_bytes(sixel_loader_t const *loader)
6,692✔
1528
{
1529
    if (loader == NULL) {
6,692!
1530
        return 0u;
1531
    }
1532
    return loader->last_input_bytes;
6,692✔
1533
}
3,317✔
1534

1535
int
1536
sixel_loader_get_start_frame_no(sixel_loader_t const *loader,
×
1537
                                int *start_frame_no)
1538
{
1539
    if (start_frame_no != NULL) {
×
1540
        *start_frame_no = INT_MIN;
×
1541
    }
1542
    if (loader == NULL || start_frame_no == NULL ||
×
1543
        loader->has_start_frame_no == 0) {
×
1544
        return 0;
1545
    }
1546

1547
    *start_frame_no = loader->start_frame_no;
×
1548
    return 1;
×
1549
}
1550

1551
SIXELAPI SIXELSTATUS
1552
sixel_loader_load_file(
14,325✔
1553
    sixel_loader_t         /* in */ *loader,
1554
    char const             /* in */ *filename,
1555
    sixel_load_image_function /* in */ fn_load)
1556
{
1557
    SIXELSTATUS status = SIXEL_FALSE;
14,325✔
1558
    sixel_chunk_t *pchunk;
8,428✔
1559
    sixel_loader_entry_t const **plan;
8,428✔
1560
    sixel_loader_entry_t const *entries;
8,428✔
1561
    size_t entry_count;
8,428✔
1562
    size_t plan_length;
8,428✔
1563
    size_t plan_index;
8,428✔
1564
    sixel_loader_component_t *component;
8,428✔
1565
    int reqcolors;
8,428✔
1566
    char const *order_override;
8,428✔
1567
    char const *env_order;
8,428✔
1568
    sixel_loader_callback_state_t callback_state;
8,428✔
1569

1570
    pchunk = NULL;
14,325✔
1571
    plan = NULL;
14,325✔
1572
    entries = NULL;
14,325✔
1573
    entry_count = 0;
14,325✔
1574
    plan_length = 0;
14,325✔
1575
    plan_index = 0;
14,325✔
1576
    component = NULL;
14,325✔
1577
    reqcolors = 0;
14,325✔
1578
    order_override = NULL;
14,325✔
1579
    env_order = NULL;
14,325✔
1580

1581
    if (loader == NULL) {
14,325!
1582
        sixel_helper_set_additional_message(
×
1583
            "sixel_loader_load_file: loader is null.");
1584
        status = SIXEL_BAD_ARGUMENT;
×
1585
        goto end0;
×
1586
    }
1587

1588
    sixel_loader_ref(loader);
14,325✔
1589

1590
    loader->log_loader_finished = 0;
14,325✔
1591
    loader->log_loader_name[0] = '\0';
14,325✔
1592
    loader->log_input_bytes = 0u;
14,325✔
1593
    loader->log_path[0] = '\0';
14,325✔
1594
    if (filename != NULL) {
14,325✔
1595
        (void)sixel_compat_snprintf(loader->log_path,
20,979✔
1596
                                    sizeof(loader->log_path),
1597
                                    "%s",
1598
                                    filename);
6,909✔
1599
    }
6,909✔
1600
    loader_log_stage(loader, "start", "path=%s", loader->log_path);
15,314✔
1601

1602
    memset(&callback_state, 0, sizeof(callback_state));
15,314✔
1603
    callback_state.loader = loader;
15,314✔
1604
    callback_state.fn = fn_load;
15,314✔
1605
    callback_state.context = loader->context;
15,314✔
1606
    loader->callback_failed = 0;
15,314✔
1607

1608
    entry_count = loader_registry_get_entries(&entries);
15,314✔
1609

1610
    reqcolors = loader->reqcolors;
15,314✔
1611
    if (reqcolors > SIXEL_PALETTE_MAX) {
15,314!
1612
        reqcolors = SIXEL_PALETTE_MAX;
1613
    }
1614

1615
    status = sixel_chunk_new(&pchunk,
14,325✔
1616
                             filename,
7,029✔
1617
                             loader->finsecure,
7,029✔
1618
                             loader->cancel_flag,
7,029✔
1619
                             loader->allocator);
7,029✔
1620
    if (status != SIXEL_OK) {
14,325!
1621
        goto end;
56✔
1622
    }
1623

1624
    if (pchunk->size == 0 || (pchunk->size == 1 && *pchunk->buffer == '\n')) {
14,269!
1625
        status = SIXEL_OK;
×
1626
        goto end;
×
1627
    }
1628

1629
    if (pchunk->source_path != NULL && pchunk->source_path[0] != '\0') {
14,251!
1630
        (void)sixel_compat_snprintf(loader->log_path,
20,833✔
1631
                                    sizeof(loader->log_path),
1632
                                    "%s",
1633
                                    pchunk->source_path);
9,275✔
1634
    }
6,854✔
1635

1636
    if (pchunk->buffer == NULL || pchunk->max_size == 0) {
14,269!
1637
        status = SIXEL_LOGIC_ERROR;
×
1638
        goto end;
×
1639
    }
1640

1641
    status = SIXEL_FALSE;
14,269✔
1642
    order_override = loader->loader_order;
14,269✔
1643
    /*
1644
     * Honour SIXEL_LOADER_PRIORITY_LIST when callers do not supply
1645
     * a loader order via -L/--loaders or sixel_loader_setopt().
1646
     */
1647
    if (order_override == NULL) {
14,269✔
1648
        env_order = sixel_compat_getenv("SIXEL_LOADER_PRIORITY_LIST");
12,427✔
1649
        if (env_order != NULL && env_order[0] != '\0') {
12,427!
1650
            order_override = env_order;
4,807✔
1651
        }
7✔
1652
    }
5,944✔
1653

1654
    loader_apply_loader_suboptions(order_override);
14,269✔
1655

1656
    plan = sixel_allocator_malloc(loader->allocator,
24,558✔
1657
                                  entry_count * sizeof(*plan));
12,761✔
1658
    if (plan == NULL) {
14,269!
1659
        status = SIXEL_BAD_ALLOCATION;
×
1660
        goto end;
×
1661
    }
1662

1663
    plan_length = loader_build_plan(order_override,
21,266✔
1664
                                    entries,
6,997✔
1665
                                    entry_count,
6,997✔
1666
                                    plan,
6,997✔
1667
                                    entry_count);
6,997✔
1668
    if (plan_length == 0u) {
14,269!
1669
        if (order_override != NULL && order_override[0] != '\0') {
×
1670
            sixel_helper_set_additional_message(
×
1671
                "sixel_loader_load_file: no supported loader in loader "
1672
                "order.");
1673
            status = SIXEL_BAD_ARGUMENT;
×
1674
        } else {
1675
            sixel_helper_set_additional_message(
×
1676
                "sixel_loader_load_file: no available loader backend.");
1677
            status = SIXEL_LOADER_FAILED;
×
1678
        }
1679
        goto end;
×
1680
    }
1681

1682
    for (plan_index = 0; plan_index < plan_length; ++plan_index) {
20,448✔
1683
        if (plan[plan_index] == NULL) {
20,139!
1684
            continue;
×
1685
        }
1686
        if (plan[plan_index]->predicate != NULL &&
20,440!
1687
            plan[plan_index]->predicate(pchunk) == 0) {
7,592✔
1688
            continue;
5,699✔
1689
        }
1690
        loader->log_input_bytes = pchunk != NULL ? pchunk->size : 0u;
14,440!
1691
        if (plan[plan_index]->name != NULL) {
14,440!
1692
            (void)sixel_compat_snprintf(loader->log_loader_name,
21,545✔
1693
                                        sizeof(loader->log_loader_name),
1694
                                        "%s",
1695
                                        plan[plan_index]->name);
9,604✔
1696
        } else {
7,105✔
1697
            loader->log_loader_name[0] = '\0';
×
1698
        }
1699
        component = sixel_loader_component_legacy_new(plan[plan_index],
21,545✔
1700
                                                   loader->allocator);
7,105✔
1701
        if (component == NULL) {
14,440!
1702
            status = SIXEL_BAD_ALLOCATION;
×
1703
            goto end;
×
1704
        }
1705

1706
        status = loader_apply_component_options(component, loader, reqcolors);
14,440✔
1707
        if (SIXEL_FAILED(status)) {
14,440!
1708
            sixel_loader_component_unref(component);
×
1709
            component = NULL;
×
1710
            goto end;
×
1711
        }
1712

1713
        loader_trace_try(sixel_loader_component_get_name(component));
14,440✔
1714
        status = sixel_loader_component_load(component,
21,545✔
1715
                                             pchunk,
7,105✔
1716
                                             loader_callback_trampoline,
1717
                                             &callback_state);
1718
        loader_trace_result(sixel_loader_component_get_name(component), status);
14,440✔
1719
        sixel_loader_component_unref(component);
14,440✔
1720
        component = NULL;
14,440✔
1721
        if (SIXEL_SUCCEEDED(status)) {
14,440✔
1722
            break;
9,250✔
1723
        }
1724
    }
282✔
1725

1726
    if (SIXEL_FAILED(status)) {
14,269✔
1727
        if (status == SIXEL_FALSE) {
309!
1728
            if (!loader->callback_failed &&
54!
1729
                    plan_index >= plan_length &&
27!
1730
                    pchunk != NULL) {
27!
1731
                status = SIXEL_LOADER_FAILED;
27✔
1732
                loader_publish_diagnostic(pchunk, filename);
27✔
1733
            } else {
27✔
1734
                sixel_helper_set_additional_message(
×
1735
                    "sixel_loader_load_file: loader returned "
1736
                    "unspecified failure.");
1737
                status = SIXEL_LOADER_FAILED;
×
1738
            }
1739
        }
27✔
1740
        goto end;
309✔
1741
    }
1742

1743
    if (plan_index < plan_length &&
19,974!
1744
            plan[plan_index] != NULL &&
13,960!
1745
            plan[plan_index]->name != NULL) {
13,960!
1746
        (void)sixel_compat_snprintf(loader->last_loader_name,
20,783✔
1747
                                    sizeof(loader->last_loader_name),
1748
                                    "%s",
1749
                                    plan[plan_index]->name);
9,250✔
1750
    } else {
6,823✔
1751
        loader->last_loader_name[0] = '\0';
×
1752
    }
1753
    loader->last_input_bytes = pchunk->size;
13,960✔
1754
    if (pchunk->source_path != NULL) {
20,783✔
1755
        size_t path_len;
8,059✔
1756

1757
        path_len = strlen(pchunk->source_path);
13,687✔
1758
        if (path_len >= sizeof(loader->last_source_path)) {
13,687!
1759
            path_len = sizeof(loader->last_source_path) - 1u;
1760
        }
1761
        memcpy(loader->last_source_path, pchunk->source_path, path_len);
13,687✔
1762
        loader->last_source_path[path_len] = '\0';
13,687✔
1763
    } else {
6,688✔
1764
        loader->last_source_path[0] = '\0';
273✔
1765
    }
1766

1767
end:
2,484✔
1768
    if (component != NULL) {
14,325!
1769
        sixel_loader_component_unref(component);
1770
        component = NULL;
1771
    }
1772
    if (plan != NULL) {
14,321!
1773
        sixel_allocator_free(loader->allocator, plan);
14,269✔
1774
        plan = NULL;
14,269✔
1775
    }
6,997✔
1776
    sixel_chunk_destroy(pchunk);
14,325✔
1777
    sixel_loader_unref(loader);
14,325✔
1778

1779
end0:
7,296✔
1780
    return status;
17,941✔
1781
}
3,616✔
1782

1783
/* load image from file */
1784

1785
SIXELAPI SIXELSTATUS
1786
sixel_helper_load_image_file(
×
1787
    char const                /* in */     *filename,     /* source file name */
1788
    int                       /* in */     fstatic,       /* whether to */
1789
                                                             /* extract a */
1790
                                                             /* static image */
1791
                                                             /* from an */
1792
                                                             /* animated gif */
1793
    int                       /* in */     fuse_palette,  /* whether to */
1794
                                                             /* use a */
1795
                                                             /* paletted */
1796
                                                             /* image; set */
1797
                                                             /* non-zero to */
1798
                                                             /* request one */
1799
    int                       /* in */     reqcolors,     /* requested */
1800
                                                             /* number of */
1801
                                                             /* colors; */
1802
                                                             /* should be */
1803
                                                             /* equal to or */
1804
                                                             /* less than */
1805
                                                             /* SIXEL_ */
1806
                                                             /* PALETTE_ */
1807
                                                             /* MAX */
1808
    unsigned char             /* in */     *bgcolor,      /* background */
1809
                                                             /* color, may */
1810
                                                             /* be NULL */
1811
    int                       /* in */     loop_control,  /* one of enum */
1812
                                                             /* loopControl */
1813
    sixel_load_image_function /* in */     fn_load,       /* callback */
1814
    int                       /* in */     finsecure,     /* true if do */
1815
                                                             /* not verify */
1816
                                                             /* SSL */
1817
    int const                 /* in */     *cancel_flag,  /* cancel flag, */
1818
                                                             /* may be */
1819
                                                             /* NULL */
1820
    void                      /* in/out */ *context,      /* private data */
1821
                                                             /* passed to */
1822
                                                             /* callback */
1823
                                                             /* function, */
1824
                                                             /* may be */
1825
                                                             /* NULL */
1826
    sixel_allocator_t         /* in */     *allocator     /* allocator */
1827
                                                             /* object, */
1828
                                                             /* may be */
1829
                                                             /* NULL */
1830
)
1831
{
1832
    SIXELSTATUS status = SIXEL_FALSE;
×
1833
    sixel_loader_t *loader;
1834

1835
    loader = NULL;
×
1836

1837
    status = sixel_loader_new(&loader, allocator);
×
1838
    if (SIXEL_FAILED(status)) {
×
1839
        goto end;
×
1840
    }
1841

1842
    status = sixel_loader_setopt(loader,
×
1843
                                 SIXEL_LOADER_OPTION_REQUIRE_STATIC,
1844
                                 &fstatic);
1845
    if (SIXEL_FAILED(status)) {
×
1846
        goto end;
×
1847
    }
1848

1849
    status = sixel_loader_setopt(loader,
×
1850
                                 SIXEL_LOADER_OPTION_USE_PALETTE,
1851
                                 &fuse_palette);
1852
    if (SIXEL_FAILED(status)) {
×
1853
        goto end;
×
1854
    }
1855

1856
    status = sixel_loader_setopt(loader,
×
1857
                                 SIXEL_LOADER_OPTION_REQCOLORS,
1858
                                 &reqcolors);
1859
    if (SIXEL_FAILED(status)) {
×
1860
        goto end;
×
1861
    }
1862

1863
    status = sixel_loader_setopt(loader,
×
1864
                                 SIXEL_LOADER_OPTION_BGCOLOR,
1865
                                 bgcolor);
1866
    if (SIXEL_FAILED(status)) {
×
1867
        goto end;
×
1868
    }
1869

1870
    status = sixel_loader_setopt(loader,
×
1871
                                 SIXEL_LOADER_OPTION_LOOP_CONTROL,
1872
                                 &loop_control);
1873
    if (SIXEL_FAILED(status)) {
×
1874
        goto end;
×
1875
    }
1876

1877
    status = sixel_loader_setopt(loader,
×
1878
                                 SIXEL_LOADER_OPTION_INSECURE,
1879
                                 &finsecure);
1880
    if (SIXEL_FAILED(status)) {
×
1881
        goto end;
×
1882
    }
1883

1884
    status = sixel_loader_setopt(loader,
×
1885
                                 SIXEL_LOADER_OPTION_CANCEL_FLAG,
1886
                                 cancel_flag);
1887
    if (SIXEL_FAILED(status)) {
×
1888
        goto end;
×
1889
    }
1890

1891
    status = sixel_loader_setopt(loader,
×
1892
                                 SIXEL_LOADER_OPTION_CONTEXT,
1893
                                 context);
1894
    if (SIXEL_FAILED(status)) {
×
1895
        goto end;
×
1896
    }
1897

1898
    status = sixel_loader_load_file(loader, filename, fn_load);
×
1899

1900
end:
1901
    sixel_loader_unref(loader);
×
1902

1903
    return status;
×
1904
}
1905

1906

1907
SIXELAPI size_t
1908
sixel_helper_get_available_loader_names(char const **names, size_t max_names)
136✔
1909
{
1910
    sixel_loader_entry_t const *entries;
80✔
1911
    size_t entry_count;
80✔
1912
    size_t limit;
80✔
1913
    size_t index;
80✔
1914

1915
    entries = NULL;
136✔
1916
    entry_count = loader_registry_get_entries(&entries);
136✔
1917

1918
    if (names != NULL && max_names > 0) {
136!
1919
        limit = entry_count;
68✔
1920
        if (limit > max_names) {
68!
1921
            limit = max_names;
1922
        }
1923
        for (index = 0; index < limit; ++index) {
308✔
1924
            names[index] = entries[index].name;
240✔
1925
        }
168✔
1926
    }
32✔
1927

1928
    return entry_count;
168✔
1929
}
32✔
1930

1931

1932
/* emacs Local Variables:      */
1933
/* emacs mode: c               */
1934
/* emacs tab-width: 4          */
1935
/* emacs indent-tabs-mode: nil */
1936
/* emacs c-basic-offset: 4     */
1937
/* emacs End:                  */
1938
/* vim: set expandtab ts=4 sts=4 sw=4 : */
1939
/* 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