#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#if HAVE_SYS_UNISTD_H
# include <sys/unistd.h>
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#if HAVE_TIME_H
# include <time.h>
#endif
#if HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#if HAVE_ERRNO_H
# include <errno.h>
#endif
#if HAVE_TERMIOS_H
# include <termios.h>
#endif
#if HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#if HAVE_IO_H
# include <io.h>
#endif
#include "decoder.h"
static char *
strdup_with_allocator(
char const *s,
sixel_allocator_t *allocator)
{
char *p;
p = (char *)sixel_allocator_malloc(allocator, (size_t)(strlen(s) + 1));
if (p) {
strcpy(p, s);
}
return p;
}
SIXELAPI SIXELSTATUS
sixel_decoder_new(
sixel_decoder_t **ppdecoder,
sixel_allocator_t *allocator)
{
SIXELSTATUS status = SIXEL_FALSE;
if (allocator == NULL) {
status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
if (SIXEL_FAILED(status)) {
goto end;
}
} else {
sixel_allocator_ref(allocator);
}
*ppdecoder = sixel_allocator_malloc(allocator, sizeof(sixel_decoder_t));
if (*ppdecoder == NULL) {
sixel_allocator_unref(allocator);
sixel_helper_set_additional_message(
"sixel_decoder_new: sixel_allocator_malloc() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
(*ppdecoder)->ref = 1;
(*ppdecoder)->output = strdup_with_allocator("-", allocator);
(*ppdecoder)->input = strdup_with_allocator("-", allocator);
(*ppdecoder)->allocator = allocator;
if ((*ppdecoder)->output == NULL || (*ppdecoder)->input == NULL) {
sixel_decoder_unref(*ppdecoder);
*ppdecoder = NULL;
sixel_helper_set_additional_message(
"sixel_decoder_new: strdup_with_allocator() failed.");
status = SIXEL_BAD_ALLOCATION;
sixel_allocator_unref(allocator);
goto end;
}
status = SIXEL_OK;
end:
return status;
}
SIXELAPI sixel_decoder_t *
sixel_decoder_create(void)
{
SIXELSTATUS status = SIXEL_FALSE;
sixel_decoder_t *decoder = NULL;
status = sixel_decoder_new(&decoder, NULL);
if (SIXEL_FAILED(status)) {
goto end;
}
end:
return decoder;
}
static void
sixel_decoder_destroy(sixel_decoder_t *decoder)
{
sixel_allocator_t *allocator;
if (decoder) {
allocator = decoder->allocator;
sixel_allocator_free(allocator, decoder->input);
sixel_allocator_free(allocator, decoder->output);
sixel_allocator_free(allocator, decoder);
sixel_allocator_unref(allocator);
}
}
SIXELAPI void
sixel_decoder_ref(sixel_decoder_t *decoder)
{
++decoder->ref;
}
SIXELAPI void
sixel_decoder_unref(sixel_decoder_t *decoder)
{
if (decoder != NULL && --decoder->ref == 0) {
sixel_decoder_destroy(decoder);
}
}
SIXELAPI SIXELSTATUS
sixel_decoder_setopt(
sixel_decoder_t *decoder,
int arg,
char const *value
)
{
SIXELSTATUS status = SIXEL_FALSE;
sixel_decoder_ref(decoder);
switch(arg) {
case SIXEL_OPTFLAG_INPUT:
free(decoder->input);
decoder->input = strdup_with_allocator(value, decoder->allocator);
if (decoder->input == NULL) {
sixel_helper_set_additional_message(
"sixel_decoder_setopt: strdup_with_allocator() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
break;
case SIXEL_OPTFLAG_OUTPUT:
free(decoder->output);
decoder->output = strdup_with_allocator(value, decoder->allocator);
if (decoder->output == NULL) {
sixel_helper_set_additional_message(
"sixel_decoder_setopt: strdup_with_allocator() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
break;
case '?':
default:
status = SIXEL_BAD_ARGUMENT;
goto end;
}
status = SIXEL_OK;
end:
sixel_decoder_unref(decoder);
return status;
}
SIXELAPI SIXELSTATUS
sixel_decoder_decode(
sixel_decoder_t *decoder)
{
SIXELSTATUS status = SIXEL_FALSE;
unsigned char *raw_data;
int sx;
int sy;
int raw_len;
int max;
int n;
FILE *input_fp = NULL;
unsigned char *indexed_pixels;
unsigned char *palette;
int ncolors;
unsigned char *pixels = NULL;
sixel_decoder_ref(decoder);
if (strcmp(decoder->input, "-") == 0) {
#if defined(O_BINARY)
# if HAVE__SETMODE
_setmode(fileno(stdin), O_BINARY);
# elif HAVE_SETMODE
setmode(fileno(stdin), O_BINARY);
# endif
#endif
input_fp = stdin;
} else {
input_fp = fopen(decoder->input, "rb");
if (!input_fp) {
sixel_helper_set_additional_message(
"sixel_decoder_decode: fopen() failed.");
status = (SIXEL_LIBC_ERROR | (errno & 0xff));
goto end;
}
}
raw_len = 0;
max = 64 * 1024;
raw_data = (unsigned char *)sixel_allocator_malloc(decoder->allocator, (size_t)max);
if (raw_data == NULL) {
sixel_helper_set_additional_message(
"sixel_decoder_decode: sixel_allocator_malloc() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
for (;;) {
if ((max - raw_len) < 4096) {
max *= 2;
raw_data = (unsigned char *)sixel_allocator_realloc(decoder->allocator, raw_data, (size_t)max);
if (raw_data == NULL) {
sixel_helper_set_additional_message(
"sixel_decoder_decode: sixel_allocator_realloc() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
}
if ((n = (int)fread(raw_data + raw_len, 1, 4096, input_fp)) <= 0) {
break;
}
raw_len += n;
}
if (input_fp != stdout) {
fclose(input_fp);
}
status = sixel_decode_raw(
raw_data,
raw_len,
&indexed_pixels,
&sx,
&sy,
&palette,
&ncolors,
decoder->allocator);
if (SIXEL_FAILED(status)) {
goto end;
}
status = sixel_helper_write_image_file(indexed_pixels, sx, sy, palette,
SIXEL_PIXELFORMAT_PAL8,
decoder->output,
SIXEL_FORMAT_PNG,
decoder->allocator);
if (SIXEL_FAILED(status)) {
goto end;
}
end:
sixel_allocator_free(decoder->allocator, pixels);
sixel_decoder_unref(decoder);
return status;
}
#if HAVE_TESTS
static int
test1(void)
{
int nret = EXIT_FAILURE;
sixel_decoder_t *decoder = NULL;
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
decoder = sixel_decoder_create();
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic pop
#endif
if (decoder == NULL) {
goto error;
}
sixel_decoder_ref(decoder);
sixel_decoder_unref(decoder);
nret = EXIT_SUCCESS;
error:
sixel_decoder_unref(decoder);
return nret;
}
static int
test2(void)
{
int nret = EXIT_FAILURE;
sixel_decoder_t *decoder = NULL;
SIXELSTATUS status;
status = sixel_decoder_new(&decoder, NULL);
if (SIXEL_FAILED(status)) {
goto error;
}
sixel_decoder_ref(decoder);
sixel_decoder_unref(decoder);
nret = EXIT_SUCCESS;
error:
sixel_decoder_unref(decoder);
return nret;
}
static int
test3(void)
{
int nret = EXIT_FAILURE;
sixel_decoder_t *decoder = NULL;
sixel_allocator_t *allocator = NULL;
SIXELSTATUS status;
sixel_debug_malloc_counter = 1;
status = sixel_allocator_new(&allocator, sixel_bad_malloc, NULL, NULL, NULL);
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_new(&decoder, allocator);
if (status != SIXEL_BAD_ALLOCATION) {
goto error;
}
nret = EXIT_SUCCESS;
error:
return nret;
}
static int
test4(void)
{
int nret = EXIT_FAILURE;
sixel_decoder_t *decoder = NULL;
sixel_allocator_t *allocator = NULL;
SIXELSTATUS status;
sixel_debug_malloc_counter = 2;
status = sixel_allocator_new(&allocator, sixel_bad_malloc, NULL, NULL, NULL);
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_new(&decoder, allocator);
if (status != SIXEL_BAD_ALLOCATION) {
goto error;
}
nret = EXIT_SUCCESS;
error:
return nret;
}
static int
test5(void)
{
int nret = EXIT_FAILURE;
sixel_decoder_t *decoder = NULL;
sixel_allocator_t *allocator = NULL;
SIXELSTATUS status;
sixel_debug_malloc_counter = 4;
status = sixel_allocator_new(&allocator, sixel_bad_malloc, NULL, NULL, NULL);
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_new(&decoder, allocator);
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_setopt(decoder, SIXEL_OPTFLAG_INPUT, "/");
if (status != SIXEL_BAD_ALLOCATION) {
goto error;
}
nret = EXIT_SUCCESS;
error:
return nret;
}
static int
test6(void)
{
int nret = EXIT_FAILURE;
sixel_decoder_t *decoder = NULL;
sixel_allocator_t *allocator = NULL;
SIXELSTATUS status;
sixel_debug_malloc_counter = 4;
status = sixel_allocator_new(&allocator, sixel_bad_malloc, NULL, NULL, NULL);
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_new(&decoder, allocator);
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_setopt(decoder, SIXEL_OPTFLAG_OUTPUT, "/");
if (status != SIXEL_BAD_ALLOCATION) {
goto error;
}
nret = EXIT_SUCCESS;
error:
return nret;
}
static int
test7(void)
{
int nret = EXIT_FAILURE;
sixel_decoder_t *decoder = NULL;
sixel_allocator_t *allocator = NULL;
SIXELSTATUS status;
status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_new(&decoder, allocator);
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_setopt(decoder, SIXEL_OPTFLAG_INPUT, "../images/file");
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_decode(decoder);
if ((status >> 8) != (SIXEL_LIBC_ERROR >> 8)) {
goto error;
}
nret = EXIT_SUCCESS;
error:
return nret;
}
static int
test8(void)
{
int nret = EXIT_FAILURE;
sixel_decoder_t *decoder = NULL;
sixel_allocator_t *allocator = NULL;
SIXELSTATUS status;
sixel_debug_malloc_counter = 5;
status = sixel_allocator_new(&allocator, sixel_bad_malloc, NULL, NULL, NULL);
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_new(&decoder, allocator);
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_setopt(decoder, SIXEL_OPTFLAG_INPUT, "../images/map8.six");
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_decoder_decode(decoder);
if (status != SIXEL_BAD_ALLOCATION) {
goto error;
}
nret = EXIT_SUCCESS;
error:
return nret;
}
int
sixel_decoder_tests_main(void)
{
int nret = EXIT_FAILURE;
size_t i;
typedef int (* testcase)(void);
static testcase const testcases[] = {
test1,
test2,
test3,
test4,
test5,
test6,
test7,
test8
};
for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
nret = testcases[i]();
if (nret != EXIT_SUCCESS) {
goto error;
}
}
nret = EXIT_SUCCESS;
error:
return nret;
}
#endif