/*
* Tool for testing PCX run-length encoding.
* Copyright © 2012 Nick Bowler
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "common.h"
#define PROGNAME "pcxrle"
static const char *progname = PROGNAME;
static const char sopts[] = "VH";
static const struct option lopts[] = {
{ "version", 0, NULL, 'V' },
{ "help", 0, NULL, 'H' },
{ 0 }
};
static void print_usage(FILE *f)
{
fprintf(f, "Usage: %s [options] bytes [bytes ...]\n", progname);
}
static void print_version(void)
{
printf("%s (%s) %s\n", PROGNAME, PACKAGE_NAME, PACKAGE_VERSION);
puts("Copyright (C) 2012 Nick Bowler.");
puts("License GPLv3+: GNU GPL version 3 or later .");
puts("This is free software: you are free to change and redistribute it.");
puts("There is NO WARRANTY, to the extent permitted by law.");
}
static void rle_error(const unsigned char *raw, size_t n, const char *str)
{
fprintf(stderr, "%s: %s\n", progname, str);
fprintf(stderr, "%s: raw input was:\n", progname);
fprintf(stderr, "%*s", 4, "");
for (size_t i = 0; i < n; i++)
printf("%.2hhx", raw[i]);
putchar('\n');
}
static int compare_rle(const unsigned char *rle_buf, size_t rle_len,
const unsigned char *raw_buf, size_t raw_len)
{
unsigned char tmp[raw_len];
size_t offset = 0;
for (size_t i = 0; i < rle_len; i++) {
size_t count = 1;
if ((rle_buf[i] & 0xc0) == 0xc0) {
count = rle_buf[i] & 0x3f;
if (++i >= rle_len) {
rle_error(raw_buf, raw_len, "invalid RLE encoding");
return -1;
}
}
if (offset + count > raw_len) {
rle_error(raw_buf, raw_len, "RLE expansion exceeds raw size");
return -1;
}
memset(tmp+offset, rle_buf[i], count);
offset += count;
}
if (offset < raw_len) {
rle_error(raw_buf, raw_len, "RLE expansion falls short of raw size");
return -1;
}
if (memcmp(raw_buf, tmp, raw_len) != 0) {
rle_error(raw_buf, raw_len, "RLE expansion does not match raw data");
return -1;
}
return 0;
}
static int encode_rle(FILE *tmp, const char *hex)
{
unsigned char buf[256], rle_buf[sizeof buf*2];
struct pcx_head head;
int rc, ret = -1;
long offset;
size_t n;
n = test_decode_hex(hex, buf, sizeof buf);
if (n == -1) {
fprintf(stderr, "%s: invalid hex sequence: %s\n",
progname, hex);
return -1;
} else if (n == 0) {
fprintf(stderr, "%s: empty argument\n", progname);
return -1;
} else if (n > sizeof buf) {
fprintf(stderr, "%s: hex sequence too long: %s\n",
progname, hex);
return -1;
}
rewind(tmp);
head.width = head.height = n;
rc = pcx_write_scanline(&head, buf, tmp);
if (rc != 0) {
fprintf(stderr, "%s: failed to encode data: %s\n",
progname, strerror(errno));
return -1;
}
offset = ftell(tmp);
if (offset < 0) {
fprintf(stderr, "%s: failed to get file offset: %s\n",
progname, strerror(errno));
return -1;
}
rewind(tmp);
if (fread(rle_buf, offset, 1, tmp) != 1) {
fprintf(stderr, "%s: failed to read RLE data: %s\n",
progname, strerror(errno));
return -1;
}
ret = compare_rle(rle_buf, offset, buf, n);
for (long i = 0; i < offset; i++)
printf("%.2hhx", rle_buf[i]);
putchar('\n');
return ret;
}
int main(int argc, char **argv)
{
int opt, ret = EXIT_SUCCESS;
FILE *tmp;
if (argc > 0)
progname = argv[0];
while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
switch (opt) {
case 'V':
print_version();
return EXIT_SUCCESS;
case 'H':
print_usage(stdout);
return EXIT_SUCCESS;
default:
print_usage(stderr);
return EXIT_FAILURE;
}
}
if (!argv[optind]) {
print_usage(stderr);
return EXIT_FAILURE;
}
tmp = tmpfile();
if (!tmp) {
fprintf(stderr, "%s: failed to create temporary file: %s\n",
progname, strerror(errno));
return EXIT_FAILURE;
}
for (int i = optind; i < argc; i++) {
if (encode_rle(tmp, argv[i]) != 0) {
ret = EXIT_FAILURE;
}
}
return ret;
}