/* See LICENSE file for copyright and license details. */ #include "liblss16.h" #include #include #include #include #include #if defined(__clang__) # pragma clang diagnostic ignored "-Wunsafe-buffer-usage" #endif static const char *argv0 = "lss16toppm"; static void writeall(char *buf, size_t n) { ssize_t r; while (n) { r = write(STDOUT_FILENO, buf, n); if (r < 0) { if (errno == EINTR) continue; fprintf(stderr, "%s: write : %s\n", argv0, strerror(errno)); exit(2); } buf += (size_t)r; n -= (size_t)r; } } int main(int argc, char *argv[]) { char buf[1024], wbuf[1024]; uint8_t pixels[1024]; struct liblss16_decoder decoder; enum liblss16_decode_state state; ssize_t r; size_t n, off, consumed, npixels, pending; enum liblss16_decode_error error; int head_printed = 0; int print_colour_map = 0; int have_stdout = 0, len; size_t i; /* cmdline syntax is inherited from the SYSLINUX implementation, * it's bad, but it's the way it is, and the way it will remain */ if (argc) { argv0 = *argv++; argc--; } for (; argc--; argv++) { if (!strcmp(*argv, "-map")) { print_colour_map = 1; } else { fprintf(stderr, "%s: Unknown option: %s\n", argv0, *argv); return 2; } } liblss16_decoder_init(&decoder); pending = 0; for (;;) { r = read(STDIN_FILENO, buf, sizeof(buf)); if (r <= 0) { if (!r) break; if (errno == EINTR) continue; fprintf(stderr, "%s: read : %s\n", argv0, strerror(errno)); return 2; } n = (size_t)r; for (off = 0; off < n;) { state = liblss16_decode_to_colour_index(&decoder, &buf[off], n - off, &consumed, pixels, sizeof(pixels) / sizeof(*pixels), &npixels, &error); off += consumed; if (state == LIBLSS16_DECODE_ERROR) { goto error; } else if (state == LIBLSS16_DECODE_END_OF_IMAGE) { if (off != n) { fprintf(stderr, "%s: image file contains extraneous data\n", argv0); return 1; } } if (!npixels) continue; if (!head_printed) { head_printed = 1; len = sprintf(wbuf, "P6\n%u %u\n255\n", decoder.image.width, decoder.image.height); if (len < 0 || (size_t)len >= sizeof(wbuf)) abort(); pending = (size_t)len; if (print_colour_map) { for (i = 0; i < sizeof(decoder.image.colour_map) / sizeof(*decoder.image.colour_map); i++) { fprintf(stderr, "#%02x%02x%02x=%zu\n", decoder.image.colour_map[i].r, decoder.image.colour_map[i].g, decoder.image.colour_map[i].b, i); } } } for (i = 0; i < npixels; i++) { if (3U > sizeof(wbuf) - pending) { writeall(wbuf, pending); pending = 0; } wbuf[pending++] = (char)decoder.image.colour_map[pixels[i]].r; wbuf[pending++] = (char)decoder.image.colour_map[pixels[i]].g; wbuf[pending++] = (char)decoder.image.colour_map[pixels[i]].b; } } } if (pending) writeall(wbuf, pending); state = liblss16_decode_to_colour_index(&decoder, NULL, 0, &consumed, pixels, sizeof(pixels) / sizeof(*pixels), &npixels, &error); if (state == LIBLSS16_DECODE_ERROR) { error: fprintf(stderr, "%s: %s\n", argv0, liblss16_decode_strerror(error)); return 1; } else if (state != LIBLSS16_DECODE_END_OF_IMAGE) { fprintf(stderr, "%s: image file truncated\n", argv0); return 1; } while (close(STDOUT_FILENO)) { if (have_stdout && errno == EBADF) break; if (errno == EINTR) { have_stdout = 1; } else { fprintf(stderr, "%s: write : %s\n", argv0, strerror(errno)); return 2; } } return 0; }