• 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

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

26
#include "config.h"
27

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

32
#if HAVE_STRING_H
33
# include <string.h>
34
#endif  /* HAVE_STRING_H */
35
#if HAVE_CTYPE_H
36
# include <ctype.h>
37
#endif  /* HAVE_CTYPE_H */
38

39
#include <sixel.h>
40

41
#include "compat_stub.h"
42

43
#define PNM_MAX_WIDTH  (1 << 16)
44
#define PNM_MAX_HEIGHT (1 << 16)
45
#define PNM_MAX_DEPTH  (1 << 16)
46

47
static unsigned char *
48
pnm_get_line(unsigned char *p, unsigned char *end, unsigned char *line)
147,231✔
49
{
50
    int n;
147,231✔
51

52
    do {
147,231✔
53
        /* read the line */
54
        for (n = 0 ; p < end && *p >= ' '; p++) {
12,035,607!
55
            if (n < 255) {
11,888,376!
56
                line[n++] = *p;
11,888,376✔
57
            }
58
        }
59

60
        /* skip invald characters */
61
        if (p < end && *p < ' ') {
147,231!
62
            p++;
147,231✔
63
        }
64

65
        line[n] = '\0';
147,231✔
66

67
    } while (line[0] == '#');
147,231!
68

69
    return p;
147,231✔
70
}
71

72

73
SIXELSTATUS
74
load_pnm(unsigned char      /* in */  *p,
30✔
75
         int                /* in */  length,
76
         sixel_allocator_t  /* in */  *allocator,
77
         unsigned char      /* out */ **result,
78
         int                /* out */ *psx,
79
         int                /* out */ *psy,
80
         unsigned char      /* out */ **ppalette,
81
         int                /* out */ *pncolors,
82
         int                /* out */ *ppixelformat)
83
{
84
    SIXELSTATUS status = SIXEL_FALSE;
30✔
85
    int n;
30✔
86
    int i;
30✔
87
    int b;
30✔
88
    int x;
30✔
89
    int y;
30✔
90
    int component[3];
30✔
91
    int ascii;
30✔
92
    int maps;
30✔
93
    int width;
30✔
94
    int height;
30✔
95
    int deps;
30✔
96
    char message[256];
30✔
97
    unsigned char *s;
30✔
98
    unsigned char *end;
30✔
99
    unsigned char tmp[256];
30✔
100
    size_t size;
30✔
101
    unsigned char pixel0;
30✔
102
    unsigned char pixel1;
30✔
103
    unsigned char pixel2;
30✔
104

105
    (void) ppalette;
30✔
106
    (void) pncolors;
30✔
107

108
    width = height = 0;
30✔
109
    deps = 1;
30✔
110

111
    end = p + length;
30✔
112
    p = pnm_get_line(p, end, tmp);
30✔
113
    *result = NULL;
30✔
114

115
    if (tmp[0] != 'P') {
30!
116
        status = SIXEL_RUNTIME_ERROR;
×
117
        sixel_helper_set_additional_message(
×
118
            "load_pnm: first character is not 'P'.");
119
        goto end;
×
120
    }
121

122
    switch(tmp[1]) {
30!
123
    case '1':
124
        /* Portable bitmap - ASCII */
125
        ascii = 1;
126
        maps  = 0;
127
        break;
128
    case '2':
129
        /* Portable graymap - ASCII */
130
        ascii = 1;
131
        maps  = 1;
132
        break;
133
    case '3':
134
        /* Portable pixmap - ASCII */
135
        ascii = 1;
136
        maps  = 2;
137
        break;
138
    case '4':
139
        /* Portable bitmap - Binary */
140
        ascii = 0;
141
        maps  = 0;
142
        break;
143
    case '5':
144
        /* Portable graymap - Binary */
145
        ascii = 0;
146
        maps  = 1;
147
        break;
148
    case '6':
149
        /* Portable pixmap - Binary */
150
        ascii = 0;
151
        maps  = 2;
152
        break;
153
    default:
×
154
        goto unknown;
×
155
    }
156

157
    p = pnm_get_line(p, end, tmp);
30✔
158
    if (p == end) {
30!
159
        /* check empty content */
160
        /* Issue 71: https://github.com/saitoha/libsixel/issues/71 */
161
        sixel_helper_set_additional_message(
×
162
            "load_pnm: the 2nd line (width height) is empty");
163
        goto invalid;
×
164
    }
165

166
    s = tmp;
167

168
    /* parse width */
169
    width = 0;
170
    for (; *s >= '0' && *s <= '9'; ++s) {
120✔
171
        width = width * 10 + (*s - '0');
90✔
172
        if (width > PNM_MAX_WIDTH) {
90!
173
            status = SIXEL_RUNTIME_ERROR;
×
174
            sixel_compat_snprintf(
×
175
                message,
176
                sizeof(message),
177
                "load_pnm: image width exceeds the limit %d.",
178
                PNM_MAX_WIDTH);
179
            sixel_helper_set_additional_message(message);
×
180
            goto end;
×
181
        }
182
    }
183

184
    while (*s == ' ') {
60✔
185
        s++;
30✔
186
    }
187

188
    /* parse height */
189
    height = 0;
190
    for (; *s >= '0' && *s <= '9'; ++s) {
120✔
191
        height = height * 10 + (*s - '0');
90✔
192
        if (height > PNM_MAX_HEIGHT) {
90!
193
            status = SIXEL_RUNTIME_ERROR;
×
194
            sixel_compat_snprintf(
×
195
                message,
196
                sizeof(message),
197
                "load_pnm: image height exceeds the limit %d.",
198
                PNM_MAX_HEIGHT);
199
            sixel_helper_set_additional_message(message);
×
200
            goto end;
×
201
        }
202
    }
203

204
    while (*s != '\0') {
30!
205
        s++;
×
206
    }
207

208
    if (maps > 0) {
30✔
209
        p = pnm_get_line(p, end, tmp);
24✔
210
        if (p == end) {
24!
211
            /* check empty content */
212
            /* Issue 71: https://github.com/saitoha/libsixel/issues/71 */
213
            sixel_helper_set_additional_message(
×
214
                "load_pnm: the 3nd line (depth) is empty");
215
            goto invalid;
×
216
        }
217
        s = tmp;
218
        deps = 0;
219
        for (; *s >= '0' && *s <= '9'; ++s) {
96✔
220
            deps = deps * 10 + (*s - '0');
72✔
221
        }
222
        if (deps > PNM_MAX_DEPTH) {
24!
223
            status = SIXEL_RUNTIME_ERROR;
×
224
            sixel_compat_snprintf(
×
225
                message,
226
                sizeof(message),
227
                "load_pnm: image depth exceeds the limit %d.",
228
                PNM_MAX_DEPTH);
229
            sixel_helper_set_additional_message(message);
×
230
            goto end;
×
231
        }
232
    }
233

234
    if (width < 1 || height < 1 || deps < 1) {
30!
235
        sixel_helper_set_additional_message(
×
236
            "load_pnm: invalid data detected: "
237
            "width < 1 || height < 1 || deps < 1");
238
        goto invalid;
×
239
    }
240

241
    size = (size_t)width * (size_t)height * 3 + 1;
30✔
242
    *result = (unsigned char *)sixel_allocator_malloc(allocator, size);
30✔
243

244
    if (*result == NULL) {
30!
245
        sixel_helper_set_additional_message(
×
246
            "load_pnm: sixel_allocator_malloc() failed.");
247
        status = SIXEL_BAD_ALLOCATION;
×
248
        goto end;
×
249
    }
250

251
    (void) memset(*result, 0, size);
30✔
252

253
    for (y = 0 ; y < height ; y++) {
13,530✔
254
        for (x = 0 ; x < width ; x++) {
8,113,500✔
255
            b = (maps == 2 ? 3 : 1);
8,100,000✔
256
            for (i = 0 ; i < b ; i++) {
25,920,000✔
257
                if (ascii) {
17,820,000✔
258
                    while (*s == '\0') {
4,197,147✔
259
                        if (p >= end) {
147,147!
260
                            break;
261
                        }
262
                        p = pnm_get_line(p, end, tmp);
147,147✔
263
                        s = tmp;
147,147✔
264
                    }
265
                    n = 0;
4,050,000✔
266
                    if (maps == 0) {
4,050,000✔
267
                        n = *s == '0';
810,000✔
268
                        if (*s != '\0')
810,000!
269
                            s++;
810,000✔
270
                    } else {
271
                        while (isdigit(*s) && n >= 0) {
11,078,034!
272
                            n = n * 10 + (*s++ - '0');
7,838,034✔
273
                        }
274
                        while (*s == ' ') {
6,480,000✔
275
                            s++;
3,240,000✔
276
                        }
277
                    }
278
                } else {
279
                    if (p >= end) {
13,770,000!
280
                        break;
281
                    }
282
                    if (maps == 0) {
13,770,000✔
283
                        n = ((*p << (x & 0x7) >> 0x7) & 1) == 0;
810,000✔
284
                        if ((x & 0x7) == 0x7) {
810,000✔
285
                            p++;
101,250✔
286
                        }
287
                    } else {
288
                        n = *(p++);
12,960,000✔
289
                    }
290
                }
291
                component[i] = n;
17,820,000✔
292
            }
293
            if (i < b) {
8,100,000!
294
                break;
295
            }
296

297
            switch(maps) {
8,100,000!
298
            case 0:        /* bitmap */
1,620,000✔
299
                if (component[0] == 0) {
1,620,000✔
300
                    component[0] = component[1] = component[2] = 0;
971,352✔
301
                } else {
302
                    component[0] = component[1] = component[2] = 255;
648,648✔
303
                }
304
                break;
305
            case 1:        /* graymap */
1,620,000✔
306
                component[0] = component[1] = component[2] = component[0] * 255 / deps;
1,620,000✔
307
                break;
1,620,000✔
308
            case 2:        /* pixmap */
4,860,000✔
309
                component[0] = (component[0] * 255 / deps);
4,860,000✔
310
                component[1] = (component[1] * 255 / deps);
4,860,000✔
311
                component[2] = (component[2] * 255 / deps);
4,860,000✔
312
                break;
4,860,000✔
313
            default:
314
                goto unknown;
315
            }
316

317
            /*
318
             * Pack each channel into the RGB888 buffer while keeping the
319
             * ASCII art overview below for clarity.
320
             *
321
             *   +---------+---------+---------+
322
             *   |   Red   |  Green  |  Blue   |
323
             *   +---------+---------+---------+
324
             */
325
            pixel0 = (unsigned char)component[0];
8,100,000✔
326
            pixel1 = (unsigned char)component[1];
8,100,000✔
327
            pixel2 = (unsigned char)component[2];
8,100,000✔
328
            *(*result + (y * width + x) * 3 + 0) = pixel0;
8,100,000✔
329
            *(*result + (y * width + x) * 3 + 1) = pixel1;
8,100,000✔
330
            *(*result + (y * width + x) * 3 + 2) = pixel2;
8,100,000✔
331
        }
332
    }
333

334
    *psx = width;
30✔
335
    *psy = height;
30✔
336
    *ppixelformat = SIXEL_PIXELFORMAT_RGB888;
30✔
337

338
    status = SIXEL_OK;
30✔
339
    goto end;
30✔
340

341
unknown:
×
342
    status = SIXEL_RUNTIME_ERROR;
×
343
    sixel_helper_set_additional_message(
×
344
        "load_pnm: unknown ppm format.");
345
    sixel_allocator_free(allocator, *result);
×
346
    *result = NULL;
×
347
    goto end;
×
348

349
invalid:
×
350
    status = SIXEL_RUNTIME_ERROR;
×
351
    sixel_allocator_free(allocator, *result);
×
352
    *result = NULL;
×
353
    goto end;
×
354

355
end:
30✔
356
    return status;
30✔
357
}
358

359
/* emacs Local Variables:      */
360
/* emacs mode: c               */
361
/* emacs tab-width: 4          */
362
/* emacs indent-tabs-mode: nil */
363
/* emacs c-basic-offset: 4     */
364
/* emacs End:                  */
365
/* vim: set expandtab ts=4 sts=4 sw=4 : */
366
/* 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