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

saitoha / libsixel / 19918707358

04 Dec 2025 05:12AM UTC coverage: 38.402% (-4.0%) from 42.395%
19918707358

push

github

saitoha
tests: fix meson msys dll lookup

9738 of 38220 branches covered (25.48%)

12841 of 33438 relevant lines covered (38.4%)

782420.02 hits per line

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

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

25
#include "config.h"
26

27
/* STDC_HEADERS */
28
#include <stdio.h>
29
#include <stdlib.h>
30

31
#if HAVE_STRING_H
32
# include <string.h>
33
#endif  /* HAVE_STRING_H */
34
#if HAVE_SYS_TYPES_H
35
# include <sys/types.h>
36
#endif  /* HAVE_SYS_TYPES_H */
37
#if HAVE_STDINT_H
38
# include <stdint.h>
39
#endif  /* HAVE_STDINT_H */
40
#if HAVE_SYS_STAT_H
41
# include <sys/stat.h>
42
#endif  /* HAVE_SYS_STAT_H */
43
#if HAVE_UNISTD_H
44
# include <unistd.h>
45
#endif  /* HAVE_UNISTD_H */
46
#if HAVE_FCNTL_H
47
# include <fcntl.h>
48
#endif  /* HAVE_FCNTL_H */
49
#if HAVE_IO_H
50
# include <io.h>
51
#endif  /* HAVE_IO_H */
52
#ifdef HAVE_ERRNO_H
53
# include <errno.h>
54
#endif  /* HAVE_ERRNO_H */
55
#ifdef HAVE_LIBCURL
56
# include <curl/curl.h>
57
#endif  /* HAVE_LIBCURL */
58
#if HAVE_SYS_SELECT_H
59
# include <sys/select.h>
60
#endif  /* HAVE_SYS_SELECT_H */
61

62
#if HAVE_WINHTTP && HAVE_WINDOWS_H
63
# if !defined(UNICODE)
64
#  define UNICODE
65
# endif
66
# if !defined(_UNICODE)
67
#  define _UNICODE
68
# endif
69
# if !defined(WIN32_LEAN_AND_MEAN)
70
#  define WIN32_LEAN_AND_MEAN
71
# endif
72
# include <windows.h>
73
# include <winhttp.h>
74
#endif
75

76
#if !defined(HAVE_MEMCPY)
77
# define memcpy(d, s, n) (bcopy ((s), (d), (n)))
78
#endif
79

80
#if !defined(HAVE_MEMMOVE)
81
# define memmove(d, s, n) (bcopy ((s), (d), (n)))
82
#endif
83

84
/* for msvc */
85
#ifndef STDIN_FILENO
86
# define STDIN_FILENO 0
87
#endif
88
#ifndef STDOUT_FILENO
89
# define STDOUT_FILENO 1
90
#endif
91
#ifndef STDERR_FILENO
92
# define STDERR_FILENO 2
93
#endif
94

95
#if !defined(O_BINARY) && defined(_O_BINARY)
96
# define O_BINARY _O_BINARY
97
#endif  /* !defined(O_BINARY) && !defined(_O_BINARY) */
98

99
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
100
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
101
#endif
102
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
103
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
104
#endif
105

106
#include "stdio_stub.h"
107
#include "chunk.h"
108
#include "allocator.h"
109

110

111
/* initialize chunk object with specified size */
112
static SIXELSTATUS
113
sixel_chunk_init(
453✔
114
    sixel_chunk_t * const /* in */ pchunk,
115
    size_t                /* in */ initial_size)
116
{
117
    SIXELSTATUS status = SIXEL_FALSE;
453✔
118

119
    pchunk->max_size = initial_size;
453✔
120
    pchunk->size = 0;
453✔
121
    pchunk->buffer
453✔
122
        = (unsigned char *)sixel_allocator_malloc(pchunk->allocator, pchunk->max_size);
453✔
123

124
    if (pchunk->buffer == NULL) {
453!
125
        sixel_helper_set_additional_message(
×
126
            "sixel_chunk_init: sixel_allocator_malloc() failed.");
127
        status = SIXEL_BAD_ALLOCATION;
×
128
        goto end;
×
129
    }
130

131
    pchunk->source_path = NULL;
453✔
132

133
    status = SIXEL_OK;
453✔
134

135
end:
453✔
136
    return status;
453✔
137
}
138

139

140
void
141
sixel_chunk_destroy(
456✔
142
    sixel_chunk_t * const /* in */ pchunk)
143
{
144
    sixel_allocator_t *allocator;
456✔
145

146
    if (pchunk) {
456✔
147
        allocator = pchunk->allocator;
453✔
148
        sixel_allocator_free(allocator, pchunk->buffer);
453✔
149
        sixel_allocator_free(allocator, pchunk->source_path);
453✔
150
        sixel_allocator_free(allocator, pchunk);
453✔
151
        sixel_allocator_unref(allocator);
453✔
152
    }
153
}
456✔
154

155

156
# ifdef HAVE_LIBCURL
157
static size_t
158
memory_write(void   /* in */ *ptr,
159
             size_t /* in */ size,
160
             size_t /* in */ len,
161
             void   /* in */ *memory)
162
{
163
    size_t nbytes = 0;
164
    sixel_chunk_t *chunk;
165

166
    if (ptr == NULL || memory == NULL) {
2!
167
        goto end;
168
    }
169

170
    chunk = (sixel_chunk_t *)memory;
171
    if (chunk->buffer == NULL) {
1!
172
        goto end;
173
    }
174

175
    nbytes = size * len;
176
    if (nbytes == 0) {
1!
177
        goto end;
178
    }
179

180
    if (chunk->max_size <= chunk->size + nbytes) {
2✔
181
        do {
182
            chunk->max_size *= 2;
183
        } while (chunk->max_size <= chunk->size + nbytes);
1!
184
        chunk->buffer = (unsigned char*)sixel_allocator_realloc(chunk->allocator,
185
                                                                chunk->buffer,
186
                                                                chunk->max_size);
187
        if (chunk->buffer == NULL) {
1!
188
            nbytes = 0;
189
            goto end;
190
        }
191
    }
192

193
    memcpy(chunk->buffer + chunk->size, ptr, nbytes);
194
    chunk->size += nbytes;
195

196
end:
197
    return nbytes;
198
}
199
# endif
200

201

202
#if HAVE_DIAGNOSTIC_SIGN_CONVERSION
203
# pragma GCC diagnostic push
204
# pragma GCC diagnostic ignored "-Wsign-conversion"
205
#endif
206
static int
207
wait_file(int fd, int usec)
×
208
{
209
    int ret = 1;
×
210
#if HAVE_SYS_SELECT_H
211
    fd_set rfds;
×
212
    struct timeval tv;
×
213

214
    tv.tv_sec = usec / 1000000;
×
215
    tv.tv_usec = usec % 1000000;
×
216
    FD_ZERO(&rfds);
×
217
    FD_SET(fd, &rfds);
×
218
    ret = select(fd + 1, &rfds, NULL, NULL, &tv);
×
219
#else
220
    (void) fd;
221
    (void) usec;
222
#endif  /* HAVE_SYS_SELECT_H */
223
    if (ret == 0) {
×
224
        return (1);
225
    }
226
    if (ret < 0) {
×
227
        return ret;
228
    }
229

230
    return (0);
231
}
232
#if HAVE_DIAGNOSTIC_SIGN_CONVERSION
233
# pragma GCC diagnostic pop
234
#endif
235

236
static int
237
sixel_fd_is_console(int fd)
20,547✔
238
{
239
#if HAVE_WINHTTP
240
    intptr_t handle;
241
    DWORD mode;
242

243
    /*
244
     * Windows reports the NUL device as a TTY.  Guard the wait loop with a
245
     * console-specific probe so redirected stdin does not hang on _isatty().
246
     */
247
    if (fd < 0) {
248
        return 0;
249
    }
250
# if HAVE__ISATTY
251
    if (!_isatty(fd)) {
252
        return 0;
253
    }
254
# elif HAVE_ISATTY
255
    if (!isatty(fd)) {
256
        return 0;
257
    }
258
# else
259
    return 0;
260
# endif
261
    handle = _get_osfhandle(fd);
262
    if (handle == (intptr_t)(-1)) {
263
        return 0;
264
    }
265
    if (GetConsoleMode((HANDLE)handle, &mode)) {
266
        return 1;
267
    }
268
    return 0;
269
#else
270
# if HAVE_ISATTY
271
    if (fd < 0) {
20,547!
272
        return 0;
273
    }
274
    return isatty(fd);
20,547✔
275
# else
276
    (void) fd;
277
    return 0;
278
# endif
279
#endif
280
}
281

282

283
static SIXELSTATUS
284
open_binary_file(
453✔
285
    FILE        /* out */   **f,
286
    char const  /* in */    *filename)
287
{
288
    SIXELSTATUS status = SIXEL_FALSE;
453✔
289
#if HAVE_STAT
290
    struct stat sb;
453✔
291
#endif  /* HAVE_STAT */
292
#if HAVE_FOPEN_S
293
    errno_t e;
294
#endif  /* HAVE_FOPEN_S */
295
    char message[2048];
453✔
296

297
    if (filename == NULL || strcmp(filename, "-") == 0) {
453!
298
        /* for windows */
299
#if defined(O_BINARY)
300
# if HAVE__SETMODE
301
        _setmode(STDIN_FILENO, O_BINARY);
302
# elif HAVE_SETMODE
303
        setmode(STDIN_FILENO, O_BINARY);
304
# endif  /* HAVE_SETMODE */
305
#endif  /* defined(O_BINARY) */
306
        *f = stdin;
141✔
307

308
        status = SIXEL_OK;
141✔
309
        goto end;
141✔
310
    }
311

312
#if HAVE_STAT
313
    if (stat(filename, &sb) != 0) {
312!
314
        status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
315
        (void)snprintf(message, sizeof(message), "stat() for file '%s' failed.", filename);
×
316
        sixel_helper_set_additional_message(message);
×
317
        goto end;
×
318
    }
319
    if (S_ISDIR(sb.st_mode)) {
312✔
320
        status = SIXEL_BAD_INPUT;
3✔
321
        sixel_helper_set_additional_message("specified path is directory.");
3✔
322
        goto end;
3✔
323
    }
324
#endif  /* HAVE_STAT */
325

326
# if HAVE_FOPEN_S
327
    e = fopen_s(f, filename, "rb");
328
    if (e != (0)) {
329
        status = (SIXEL_LIBC_ERROR | (e & 0xff));
330
        sixel_helper_set_additional_message("fopen_s() failed.");
331
        goto end;
332
    }
333
# else
334
    *f = fopen(filename, "rb");
309✔
335
    if (! *f) {
309!
336
        status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
337
        sixel_helper_set_additional_message("fopen() failed.");
×
338
        goto end;
×
339
    }
340
# endif  /* HAVE_FOPEN_S */
341

342
    status = SIXEL_OK;
343

344
end:
453✔
345
    return status;
453✔
346
}
347

348

349
/* get chunk date from specified local file path */
350
static SIXELSTATUS
351
sixel_chunk_from_file(
453✔
352
    char const      /* in */ *filename,
353
    sixel_chunk_t   /* in */ *pchunk,
354
    int const       /* in */ *cancel_flag
355
)
356
{
357
    SIXELSTATUS status = SIXEL_FALSE;
453✔
358
    int ret;
453✔
359
    int fd;
453✔
360
    FILE *f = NULL;
453✔
361
    size_t n;
453✔
362
    size_t const bucket_size = 4096;
453✔
363

364
    status = open_binary_file(&f, filename);
453✔
365
    if (SIXEL_FAILED(status) || f == NULL) {
453!
366
        goto end;
3✔
367
    }
368

369
    fd = sixel_fileno(f);
450✔
370

371
    for (;;) {
40,644✔
372
        if (pchunk->max_size - pchunk->size < bucket_size) {
20,547✔
373
            pchunk->max_size *= 2;
615✔
374
            pchunk->buffer = (unsigned char *)sixel_allocator_realloc(pchunk->allocator,
1,230✔
375
                                                                      pchunk->buffer,
615✔
376
                                                                      pchunk->max_size);
377
            if (pchunk->buffer == NULL) {
615!
378
                sixel_helper_set_additional_message(
×
379
                    "sixel_chunk_from_file: sixel_allocator_realloc() failed.");
380
                status = SIXEL_BAD_ALLOCATION;
×
381
                goto end;
×
382
            }
383
        }
384

385
        if (sixel_fd_is_console(fd)) {
41,094!
386
            for (;;) {
×
387
                if (*cancel_flag) {
×
388
                    status = SIXEL_INTERRUPTED;
×
389
                    goto end;
×
390
                }
391
                ret = wait_file(fd, 10000);
×
392
                if (ret < 0) {
×
393
                    sixel_helper_set_additional_message(
×
394
                        "sixel_chunk_from_file: wait_file() failed.");
395
                    status = SIXEL_RUNTIME_ERROR;
×
396
                    goto end;
×
397
                }
398
                if (ret == 0) {
×
399
                    break;
400
                }
401
            }
402
        }
403
        n = fread(pchunk->buffer + pchunk->size, 1, 4096, f);
20,547!
404
        if (n == 0) {
20,547✔
405
            break;
406
        }
407
        pchunk->size += n;
20,097✔
408
    }
409

410
    if (f != stdin) {
450✔
411
        fclose(f);
309✔
412
    }
413

414
    status = SIXEL_OK;
415

416
end:
453✔
417
    return status;
453✔
418
}
419

420
#if HAVE_WINHTTP
421

422
static wchar_t *
423
utf8_to_wide(char const *s) {
424
    wchar_t *w;
425
    int n;
426

427
    n = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
428
    if (n <= 0) {
429
        return NULL;
430
    }
431
    w = (wchar_t*) malloc(sizeof(wchar_t) * n);
432
    if (!w) {
433
        return NULL;
434
    }
435
    if (!MultiByteToWideChar(CP_UTF8, 0, s, -1, w, n)) {
436
         free(w);
437
         return NULL;
438
    }
439

440
    return w;
441
}
442
#endif  /* HAVE_WINHTTP */
443

444
#if HAVE_WINHTTP
445
static SIXELSTATUS
446
sixel_chunk_from_url_with_winhttp(
447
    char const      /* in */ *url,
448
    sixel_chunk_t   /* in */ *pchunk,
449
    int             /* in */ finsecure
450
)
451
{
452
    SIXELSTATUS status = SIXEL_FALSE;
453
    unsigned long const timeout_ms = 10000;
454
    wchar_t *wua = NULL;
455
    wchar_t *wurl = NULL;
456
    wchar_t hostname[2048];
457
    wchar_t path[4096];
458
    WINHTTP_PROXY_INFO pinfo;
459
    WINHTTP_AUTOPROXY_OPTIONS apo;
460
    HINTERNET hSession = NULL;
461
    HINTERNET hConnect = NULL;
462
    HINTERNET hRequest = NULL;
463
    INTERNET_PORT port;
464
    BOOL bRet;
465
    DWORD dwStatus = 0;
466
    DWORD dwSize = sizeof(dwStatus);
467
    DWORD dwFlags;
468
    DWORD dwRead = 0;
469
    DWORD dwAvail = 0;
470
    DWORD dwEnable = 1;
471
    URL_COMPONENTS uc;
472
    void *p = NULL;
473

474
    wua = utf8_to_wide("libsixel/" LIBSIXEL_VERSION);
475
    if (wua == NULL) {
476
        goto end;
477
    }
478

479
    wurl = utf8_to_wide(url);
480
    if (wurl == NULL) {
481
        goto end;
482
    }
483

484
    RtlZeroMemory(&uc, sizeof(uc));
485
    uc.dwStructSize = sizeof(uc);
486
    uc.lpszHostName = hostname;
487
    uc.dwHostNameLength = sizeof(hostname) / sizeof(hostname[0]);
488
    uc.lpszUrlPath = path;
489
    uc.dwUrlPathLength = sizeof(path) / sizeof(path[0]);
490
    uc.lpszExtraInfo = NULL;
491
    uc.dwExtraInfoLength = 0;
492

493
    bRet = WinHttpCrackUrl(wurl, 0, 0, &uc);
494
    if (! bRet || ! uc.lpszHostName) {
495
        goto end;
496
    }
497

498
    if (uc.nScheme != INTERNET_SCHEME_HTTPS &&
499
        uc.nScheme != INTERNET_SCHEME_HTTP) {
500
        /* unavailable protocol */
501
        status = SIXEL_BAD_ARGUMENT;
502
        goto end;
503
    }
504

505
    port = uc.nPort;
506
    if (! port) {
507
        if (uc.nScheme == INTERNET_SCHEME_HTTPS) {
508
            port = 443;
509
        } else if (uc.nScheme == INTERNET_SCHEME_HTTP) {
510
            port = 80;
511
        }
512
    }
513

514
    hSession = WinHttpOpen(wua,
515
                           WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
516
                           WINHTTP_NO_PROXY_NAME,
517
                           WINHTTP_NO_PROXY_BYPASS, 0);
518
    if (!hSession) {
519
        goto end;
520
    }
521

522
    if (timeout_ms) {
523
        WinHttpSetTimeouts(
524
            hSession,
525
            timeout_ms,  /* dwResolveTimeout */
526
            timeout_ms,  /* dwConnectTimeout */
527
            timeout_ms,  /* dwSendTimeout */
528
            timeout_ms); /* dwReceiveTimeout */
529
    }
530

531
    hConnect = WinHttpConnect(hSession, uc.lpszHostName, port, 0);
532
    if (!hConnect) {
533
        goto end;
534
    }
535

536
    dwFlags = (uc.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0;
537

538
    hRequest = WinHttpOpenRequest(hConnect, L"GET",
539
                                  uc.lpszUrlPath && *uc.lpszUrlPath ?
540
                                      uc.lpszUrlPath :
541
                                      L"/",
542
                                  NULL,
543
                                  WINHTTP_NO_REFERER,
544
                                  WINHTTP_DEFAULT_ACCEPT_TYPES,
545
                                  dwFlags);
546
    if (!hRequest) {
547
        goto end;
548
    }
549
    WinHttpSetOption(hRequest, WINHTTP_OPTION_ENABLE_FEATURE,
550
                     &dwEnable, sizeof(dwEnable));
551
    if (finsecure) {
552
        dwFlags = 0;
553
        dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
554
        dwFlags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
555
        dwFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
556
        WinHttpSetOption(hRequest, WINHTTP_OPTION_SECURITY_FLAGS,
557
                         &dwFlags, sizeof(dwFlags));
558
    }
559

560
    RtlZeroMemory(&apo, sizeof(apo));
561
    apo.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT;
562
    apo.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DHCP;
563
    apo.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DNS_A;
564
    apo.fAutoLogonIfChallenged = TRUE;
565

566
    RtlZeroMemory(&pinfo, sizeof(pinfo));
567
    if (WinHttpGetProxyForUrl(hSession, wurl, &apo, &pinfo)) {
568
        WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY,
569
                         &pinfo, sizeof(pinfo));
570
        if (pinfo.lpszProxy) {
571
            GlobalFree(pinfo.lpszProxy);
572
        }
573
        if (pinfo.lpszProxyBypass) {
574
            GlobalFree(pinfo.lpszProxyBypass);
575
        }
576
    }
577

578
    bRet = WinHttpSendRequest(hRequest,
579
                              WINHTTP_NO_ADDITIONAL_HEADERS, 0,
580
                              WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
581
    if (!bRet) {
582
        goto end;
583
    }
584

585
    bRet = WinHttpReceiveResponse(hRequest, NULL);
586
    if (!bRet) {
587
        goto end;
588
    }
589

590
    (void) WinHttpQueryHeaders(hRequest,
591
            WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
592
            WINHTTP_HEADER_NAME_BY_INDEX,
593
            &dwStatus,
594
            &dwSize,
595
            WINHTTP_NO_HEADER_INDEX);
596

597
    for (;;) {
598
        if (! WinHttpQueryDataAvailable(hRequest, &dwAvail)) {
599
            goto err;
600
        }
601
        if (dwAvail == 0) {
602
            break;
603
        }
604
        if (pchunk->size + dwAvail > pchunk->max_size) {
605
            do {
606
                pchunk->max_size *= 2;
607
            } while (pchunk->max_size < pchunk->size + dwAvail);
608
            p = sixel_allocator_realloc(
609
                pchunk->allocator, pchunk->buffer, pchunk->max_size);
610
            if (! p) {
611
                goto err;
612
            }
613
            pchunk->buffer = p;
614
        }
615

616
        dwRead = 0;
617
        if (! WinHttpReadData(hRequest,
618
                              pchunk->buffer + pchunk->size,
619
                              dwAvail, &dwRead)) {
620
            goto err;
621
        }
622
        if (dwRead == 0) {
623
            break;
624
        }
625

626
        pchunk->size += dwRead;
627
    }
628

629
    status = SIXEL_OK;
630

631
    goto end;
632

633
err:
634
    if (pchunk->buffer) {
635
        free(pchunk->buffer);
636
    }
637

638
end:
639
    if (hRequest) {
640
        WinHttpCloseHandle(hRequest);
641
    }
642
    if (hConnect) {
643
        WinHttpCloseHandle(hConnect);
644
    }
645
    if (hSession) {
646
        WinHttpCloseHandle(hSession);
647
    }
648
    if (wua) {
649
        free(wua);
650
    }
651
    if (wurl) {
652
        free(wurl);
653
    }
654

655
    status = SIXEL_OK;
656

657
    return status;
658
}
659
# endif  /* HAVE_WINHTTP */
660

661

662
# if HAVE_LIBCURL
663
static SIXELSTATUS
664
sixel_chunk_from_url_with_curl(
665
    char const      /* in */ *url,
666
    sixel_chunk_t   /* in */ *pchunk,
667
    int             /* in */ finsecure)
668
{
669
    SIXELSTATUS status = SIXEL_FALSE;
670
    CURL *curl = NULL;
671
    CURLcode code;
672

673
    curl = curl_easy_init();
674
    if (curl == NULL) {
1!
675
        status = SIXEL_CURL_ERROR & CURLE_FAILED_INIT;
676
        sixel_helper_set_additional_message("curl_easy_init() failed.");
677
        goto end;
678
    }
679

680
    code = curl_easy_setopt(curl, CURLOPT_URL, url);
681
    if (code != CURLE_OK) {
1!
682
        status = SIXEL_CURL_ERROR & (code & 0xff);
683
        sixel_helper_set_additional_message(
684
            "curl_easy_setopt(CURLOPT_URL) failed.");
685
        goto end;
686
    }
687

688
    code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
689
    if (code != CURLE_OK) {
1!
690
        status = SIXEL_CURL_ERROR & (code & 0xff);
691
        sixel_helper_set_additional_message(
692
            "curl_easy_setopt(CURLOPT_FOLLOWLOCATION) failed.");
693
        goto end;
694
    }
695

696
    code = curl_easy_setopt(curl, CURLOPT_USERAGENT,
697
                            "libsixel/" LIBSIXEL_VERSION);
698
    if (code != CURLE_OK) {
1!
699
        status = SIXEL_CURL_ERROR & (code & 0xff);
700
        sixel_helper_set_additional_message(
701
            "curl_easy_setopt(CURLOPT_USERAGENT) failed.");
702
        goto end;
703
    }
704

705
    if (finsecure && strncmp(url, "https://", 8) == 0) {
3!
706
        code = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
707
        if (code != CURLE_OK) {
1!
708
            status = SIXEL_CURL_ERROR & (code & 0xff);
709
            sixel_helper_set_additional_message(
710
                "curl_easy_setopt(CURLOPT_SSL_VERIFYPEER) failed.");
711
            goto end;
712
        }
713

714
        code = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
715
        if (code != CURLE_OK) {
1!
716
            status = SIXEL_CURL_ERROR & (code & 0xff);
717
            sixel_helper_set_additional_message(
718
                "curl_easy_setopt(CURLOPT_SSL_VERIFYHOST) failed.");
719
            goto end;
720
        }
721

722
    }
723

724
    code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memory_write);
725
    if (code != CURLE_OK) {
1!
726
        status = SIXEL_CURL_ERROR & (code & 0xff);
727
        sixel_helper_set_additional_message(
728
            "curl_easy_setopt(CURLOPT_WRITEFUNCTION) failed.");
729
        goto end;
730
    }
731

732
    code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)pchunk);
733
    if (code != CURLE_OK) {
1!
734
        status = SIXEL_CURL_ERROR & (code & 0xff);
735
        sixel_helper_set_additional_message(
736
            "curl_easy_setopt(CURLOPT_WRITEDATA) failed.");
737
        goto end;
738
    }
739

740
    code = curl_easy_perform(curl);
741
    if (code != CURLE_OK) {
2✔
742
        status = SIXEL_CURL_ERROR & (code & 0xff);
743
        sixel_helper_set_additional_message("curl_easy_perform() failed.");
744
        goto end;
745
    }
746

747
    status = SIXEL_OK;
748

749
end:
750
    if (curl) {
1!
751
        curl_easy_cleanup(curl);
752
    }
753

754
    return status;
755
}
756
# endif  /* HAVE_LIBCURL */
757

758
/* get chunk of specified resource over libcurl function */
759
static SIXELSTATUS
760
sixel_chunk_from_url(
×
761
    char const      /* in */ *url,
762
    sixel_chunk_t   /* in */ *pchunk,
763
    int             /* in */ finsecure)
764
{
765
    SIXELSTATUS status = SIXEL_NOT_IMPLEMENTED;
×
766
#if HAVE_WINHTTP
767
    status = sixel_chunk_from_url_with_winhttp(url, pchunk, finsecure);
768
    if (SIXEL_SUCCEEDED(status)) {
769
        return status;
770
    }
771
#endif  /* HAVE_WINHTTP */
772

773
#if HAVE_LIBCURL
774
    status = sixel_chunk_from_url_with_curl(url, pchunk, finsecure);
775
    if (SIXEL_SUCCEEDED(status)) {
1!
776
        return status;
777
    }
778
#endif  /* HAVE_LIBCURL */
779

780
#if !defined(HAVE_WINHTTP) && !defined(HAVE_LIBCURL)
781
    (void) url;
×
782
    (void) pchunk;
×
783
    (void) finsecure;
×
784
    sixel_helper_set_additional_message(
×
785
        "To specify URI schemes, you must configure this program "
786
        "at compile time using either the --with-libcurl option "
787
        "or the --with-winhttp (only available on Windows) option.\n");
788
    status = SIXEL_NOT_IMPLEMENTED;
×
789
#endif  /* !defined(HAVE_WINHTTP) && !defined(HAVE_LIBCURL) */
790

791
    return status;
×
792
}
793

794

795
SIXELSTATUS
796
sixel_chunk_new(
453✔
797
    sixel_chunk_t       /* out */   **ppchunk,
798
    char const          /* in */    *filename,
799
    int                 /* in */    finsecure,
800
    int const           /* in */    *cancel_flag,
801
    sixel_allocator_t   /* in */    *allocator)
802
{
803
    SIXELSTATUS status = SIXEL_FALSE;
453✔
804

805
    if (ppchunk == NULL) {
453!
806
        sixel_helper_set_additional_message(
×
807
            "sixel_chunk_new: ppchunk is null.");
808
        status = SIXEL_BAD_ARGUMENT;
×
809
        goto end;
×
810
    }
811

812
    if (allocator == NULL) {
453!
813
        sixel_helper_set_additional_message(
×
814
            "sixel_chunk_new: allocator is null.");
815
        status = SIXEL_BAD_ARGUMENT;
×
816
        goto end;
×
817
    }
818

819
    *ppchunk = (sixel_chunk_t *)sixel_allocator_malloc(allocator, sizeof(sixel_chunk_t));
453✔
820
    if (*ppchunk == NULL) {
453!
821
        sixel_helper_set_additional_message(
×
822
            "sixel_chunk_new: sixel_allocator_malloc() failed.");
823
        status = SIXEL_BAD_ALLOCATION;
×
824
        goto end;
×
825
    }
826

827
    /* set allocator to chunk object */
828
    (*ppchunk)->allocator = allocator;
453✔
829

830
    status = sixel_chunk_init(*ppchunk, 1024 * 32);
453✔
831
    if (SIXEL_FAILED(status)) {
453!
832
        sixel_allocator_free(allocator, *ppchunk);
×
833
        *ppchunk = NULL;
×
834
        goto end;
×
835
    }
836

837
    sixel_allocator_ref(allocator);
453✔
838

839
    if (filename != NULL &&
453✔
840
        strcmp(filename, "-") != 0 &&
312!
841
        strstr(filename, "://") == NULL) {
312!
842
        size_t pathlen = strlen(filename);
312✔
843
        (*ppchunk)->source_path =
624✔
844
            (char *)sixel_allocator_malloc(allocator, pathlen + 1);
312✔
845
        if ((*ppchunk)->source_path == NULL) {
312!
846
            sixel_helper_set_additional_message(
×
847
                "sixel_chunk_new: sixel_allocator_malloc() failed.");
848
            status = SIXEL_BAD_ALLOCATION;
×
849
            sixel_chunk_destroy(*ppchunk);
×
850
            *ppchunk = NULL;
×
851
            goto end;
×
852
        }
853
        memcpy((*ppchunk)->source_path, filename, pathlen + 1);
312✔
854
    }
1!
855

856
    if (filename != NULL && strstr(filename, "://")) {
312!
857
        status = sixel_chunk_from_url(filename, *ppchunk, finsecure);
×
858
    } else {
859
        status = sixel_chunk_from_file(filename, *ppchunk, cancel_flag);
453✔
860
    }
861
    if (SIXEL_FAILED(status)) {
453✔
862
        sixel_chunk_destroy(*ppchunk);
3✔
863
        *ppchunk = NULL;
3✔
864
        goto end;
3✔
865
    }
866

867
    status = SIXEL_OK;
868

869
end:
453✔
870
    return status;
453✔
871
}
872

873

874
#if HAVE_TESTS
875
static int
876
test1(void)
×
877
{
878
    int nret = EXIT_FAILURE;
×
879
    unsigned char *ptr = malloc(16);
×
880

881
#ifdef HAVE_LIBCURL
882
    sixel_chunk_t chunk = {0, 0, 0, NULL, NULL};
883
    int nread;
884

885
    nread = memory_write(NULL, 1, 1, NULL);
886
    if (nread != 0) {
×
887
        goto error;
888
    }
889

890
    nread = memory_write(ptr, 1, 1, &chunk);
891
    if (nread != 0) {
×
892
        goto error;
893
    }
894

895
    nread = memory_write(ptr, 0, 1, &chunk);
896
    if (nread != 0) {
×
897
        goto error;
898
    }
899
#else
900
    nret = EXIT_SUCCESS;
×
901
    goto error;
×
902
#endif  /* HAVE_LIBCURL */
903
    nret = EXIT_SUCCESS;
904

905
error:
×
906
    free(ptr);
×
907
    return nret;
×
908
}
909

910

911
static int
912
test2(void)
×
913
{
914
    int nret = EXIT_FAILURE;
×
915
    sixel_chunk_t *chunk = NULL;
×
916
    SIXELSTATUS status = SIXEL_FALSE;
×
917

918
    status = sixel_chunk_new(&chunk, NULL, 0, NULL, NULL);
×
919
    if (status != SIXEL_BAD_ARGUMENT) {
×
920
        goto error;
921
    }
922

923
    status = sixel_chunk_new(NULL, NULL, 0, NULL, NULL);
×
924
    if (status != SIXEL_BAD_ARGUMENT) {
×
925
        goto error;
926
    }
927

928
    nret = EXIT_SUCCESS;
×
929

930
error:
×
931
    return nret;
×
932
}
933

934

935
static int
936
test3(void)
×
937
{
938
    int nret = EXIT_FAILURE;
×
939
    sixel_chunk_t *chunk;
×
940
    sixel_allocator_t *allocator = NULL;
×
941
    SIXELSTATUS status = SIXEL_FALSE;
×
942

943
    sixel_debug_malloc_counter = 1;
×
944

945
    status = sixel_allocator_new(&allocator, sixel_bad_malloc, NULL, NULL, NULL);
×
946
    if (SIXEL_FAILED(status)) {
×
947
        goto error;
×
948
    }
949

950
    status = sixel_chunk_new(&chunk, "../images/map8.six", 0, NULL, allocator);
×
951
    if (status != SIXEL_BAD_ALLOCATION) {
×
952
        goto error;
×
953
    }
954

955
    nret = EXIT_SUCCESS;
956

957
error:
×
958
    return nret;
×
959
}
960

961

962
static int
963
test4(void)
×
964
{
965
    int nret = EXIT_FAILURE;
×
966
    sixel_chunk_t *chunk;
×
967
    sixel_allocator_t *allocator = NULL;
×
968
    SIXELSTATUS status = SIXEL_FALSE;
×
969

970
    sixel_debug_malloc_counter = 2;
×
971

972
    status = sixel_allocator_new(&allocator, sixel_bad_malloc, NULL, NULL, NULL);
×
973
    if (SIXEL_FAILED(status)) {
×
974
        goto error;
×
975
    }
976
    status = sixel_chunk_new(&chunk, "../images/map8.six", 0, NULL, allocator);
×
977
    if (status != SIXEL_BAD_ALLOCATION) {
×
978
        goto error;
×
979
    }
980

981
    nret = EXIT_SUCCESS;
982

983
error:
×
984
    return nret;
×
985
}
986

987

988
SIXELAPI int
989
sixel_chunk_tests_main(void)
×
990
{
991
    int nret = EXIT_FAILURE;
×
992
    size_t i;
×
993
    typedef int (* testcase)(void);
×
994

995
    static testcase const testcases[] = {
×
996
        test1,
997
        test2,
998
        test3,
999
        test4,
1000
    };
1001

1002
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
1003
        nret = testcases[i]();
×
1004
        if (nret != EXIT_SUCCESS) {
×
1005
            goto error;
×
1006
        }
1007
    }
1008

1009
    nret = EXIT_SUCCESS;
1010

1011
error:
×
1012
    return nret;
×
1013
}
1014
#endif  /* HAVE_TESTS */
1015

1016
/* emacs Local Variables:      */
1017
/* emacs mode: c               */
1018
/* emacs tab-width: 4          */
1019
/* emacs indent-tabs-mode: nil */
1020
/* emacs c-basic-offset: 4     */
1021
/* emacs End:                  */
1022
/* vim: set expandtab ts=4 sts=4 sw=4 : */
1023
/* 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