/*
* version 1.5 2001-06-26
* slightly more robust
* "compression" levels
* '-I' for decode
* '-n' option
* ident strings can not contain whitespace
* version 1.4 2001-06-24
* version 1.3 1999-09-12
* version 1.2 1995-03-01
* version 1.1 1995-05-30
* version 1.0 1994-12-13
*
* remember to transfer files with
* Content-Encoding: 8bit
* Content-Transfer-Encoding: 8bit
*
* this is written in ISO-C89
* + getopt (e.g. -D_POSIX_C_SOURCE=2)
*
* basex -v -i "id" output
* basex -d -I -v output
*
* -V show version
* -d decode
* -v verbose
* -i use identifier id
* -I print id to stderr on decryption
* -n do NOT create output
* -1 VERY conservative
* -5 rather conservative (almost rfc2822)
* -7 non-rfc2822, but should work (default)
* -9 maximum level (not that much better)
*
* ESCAPE -> ESCAPE 'A'
* 0x00 -> ESCAPE 'B'
* 0x0a -> ESCAPE 'C'
* 0x0d -> ESCAPE 'D'
* ESCAPE x, x != A-D unchanged
*
* uses adler32 checksum
*/
/* next include is for getopt only */
#include
#include
#include
#include
#define MAXLINE (60000+3)
#define ESCAPE 0xff
#define MAXBLK 5000000L /* max. blksize on decode */
#define ID1 "BASEX ENCODED DATA" /* file header */
#define ID2 "X" /* control header */
#define ID3 '|' /* data header */
char version[] = "basex version 1.5 - 2001-06-26";
typedef unsigned long u32; /* 4 byte unsigned int */
typedef unsigned char u8; /* 1 byte unsigned int */
int verbose = 0;
int showident = 0;
int complevel = 7;
int nooutput = 0;
int linelen;
int blklen;
extern char *optarg;
/*extern int optind, opterr, optopt; */
u32
adler32 (u8 * data, int len)
{
u32 s1 = 1;
u32 s2 = 0;
u32 modulo = 65521;
while (--len)
{
s1 += *data++;
if (s1 >= modulo)
s1 -= modulo;
s2 += s1;
if (s2 >= modulo)
s2 -= modulo;
};
return (s2 << 16) + s1;
}
void
out_char (FILE * o, int code)
{
static char line[MAXLINE + 1];
static int llen = 0;
if (code == -2)
{
if (llen)
{
line[llen] = 0;
if (!nooutput)
fprintf (o, "%c%s\n", ID3, line);
}
}
else if (code == -1)
{
llen = 0;
}
else
{
line[llen++] = code;
if (llen >= linelen)
{
llen = 0;
line[linelen] = 0;
if (!nooutput)
fprintf (o, "%c%s\n", ID3, line);
}
}
}
int
encode (FILE * i, FILE * o, char *id)
{
u8 *block;
long blklen;
int len;
if (complevel <= 1)
{
linelen = 120;
blklen = 50000;
}
else if (complevel <= 5)
{
linelen = 996;
blklen = 50000;
}
else if (complevel <= 7)
{
linelen = 7996;
blklen = 50000;
}
else
{
linelen = 60000;
blklen = 5000000;
}
block = malloc (blklen);
if (!block)
{
fprintf (stderr, "unable to allocate %ld bytes", blklen * 2);
return 11;
}
if (!nooutput)
{
fprintf (o, "%s\n", ID1);
fprintf (o, "%s VERSION 2\n", ID2);
if (id)
fprintf (o, "%s IDENT %s\n", ID2, id);
}
/*fprintf (o, "%s CODEC AFE0C0DE\n", ID2);*/ /* default */
while ((len = fread (block, 1, blklen, i)) > 0)
{
u32 sum = adler32 (block, len);
u8 *p = block;
if (!nooutput)
fprintf (o, "%s BLOCK %d %08lx\n", ID2, len, sum);
out_char (o, -1);
while (len--)
{
int c = *p++ ^ (u8) sum++;
if (c == ESCAPE)
{
out_char (o, ESCAPE);
if (len)
{
switch (*p ^ (u8) sum)
{
case 'A':
case 'B':
case 'C':
case 'D':
out_char (o, 'A');
break;
default:
/* nothing */;
}
}
else
out_char (o, 'A');
}
else if (c == 0x00)
{
out_char (o, ESCAPE);
out_char (o, 'B');
}
else if (c == 0x0a)
{
out_char (o, ESCAPE);
out_char (o, 'C');
}
else if (c == 0x0d)
{
out_char (o, ESCAPE);
out_char (o, 'D');
}
else
{
out_char (o, c);
}
}
out_char (o, -2);
}
if (!nooutput)
fprintf (o, "%s END\n", ID2);
free (block);
return 0;
}
int
in_char (FILE * i, int code)
{
static int llen;
static char line[MAXLINE + 3];
if (code == -1)
{
llen = MAXLINE + 1;
return 0;
}
else
{
if (llen > MAXLINE
|| line[llen] == 0
|| line[llen] == '\r'
|| line[llen] == '\n')
{
do {
if (!fgets (line, sizeof (line), i))
{
perror ("error on block read (file truncated?)");
return -1;
}
} while (line[0] != ID3);
llen = 1;
}
return (unsigned char) line[llen++];
}
}
int
decodeblock (FILE * i, u8 * data, int len, int sum)
{
int ch;
(void) in_char (i, -1);
while (len--)
{
ch = in_char (i, 0);
if (ch < 0)
return 8;
retry:
if (ch == ESCAPE)
{
switch ((ch = in_char (i, 0)))
{
case 'A': ch = ESCAPE; break;
case 'B': ch = 0x00; break;
case 'C': ch = 0x0a; break;
case 'D': ch = 0x0d; break;
default:
*data++ = (u8) ESCAPE ^ (u8) sum++; len--;
goto retry;
}
}
*data++ = (u8) ch ^ (u8) sum++;
}
return 0;
}
int
decode (FILE * i, FILE * o)
{
char input[255];
char *opt = input + sizeof (ID2) - 1 + 1;
long blklen;
int err;
u32 sum, sum2;
u32 codec = 0xafe1c0de;
int version = 0;
char ident[255];
ident[0] = 0;
do
{
if (!fgets (input, sizeof (input), i))
{
perror ("unable to find start of encoded data");
return 1;
}
}
while (strncmp (input, ID1, sizeof (ID1) - 1));
if (verbose)
fprintf (stderr, "basex header found, processing options\n");
for (;;)
{
if (!fgets (input, sizeof (input), i))
{
perror ("error while processing options");
return 2;
}
if (strncmp (input, ID2, sizeof (ID2) - 1) || *(opt - 1) != ' ')
{
fprintf (stderr, "garbage instead of option, ignored\n");
}
else
{
if (sscanf (opt, "VERSION %d\n", &version) == 1)
{
if (verbose)
fprintf (stderr, "option version %d\n", version);
if (version != 2)
{
fprintf (stderr, "unsupported version %d\n", version);
return 5;
}
}
else if (sscanf (opt, "CODEC %8lx\n", &codec) == 1)
{
if (verbose)
fprintf (stderr, "option codec %lx\n", codec);
}
else if (sscanf (opt, "IDENT %[^\n]\n", ident) == 1)
{
if (verbose || showident)
fprintf (stderr, "option ident %s\n", ident);
}
else if (strcmp (opt, "END\n") == 0)
{
if (verbose)
fprintf (stderr, "option end\n");
return 0;
}
else if (sscanf (opt, "BLOCK %ld %08lx\n", &blklen, &sum) == 2)
{
u8 *block;
if (verbose)
fprintf (stderr, "option block len %ld, checksum %08lx\n", blklen, sum);
if (blklen > MAXBLK)
{
fprintf (stderr, "block length too large (%ld>%ld)\n", blklen, MAXBLK);
return 4;
}
switch (codec)
{
case 0xafe1c0de:
block = malloc (blklen);
if (!block)
{
fprintf (stderr, "unable to allocate %ld bytes", blklen);
return 11;
}
err = decodeblock (i, block, blklen, sum);
if (err)
return err;
sum2 = adler32 (block, blklen);
if (sum != sum2)
{
fprintf (stderr, "checksumming error %08lx != %08lx\n", sum, sum2);
return 9;
}
if (!nooutput && fwrite (block, blklen, 1, o) <= 0)
{
perror ("write error");
return 10;
}
free (block);
break;
default:
fprintf (stderr, "unknown codec %08lx\n", codec);
return 3;
}
}
else
{
fprintf (stderr, "unknown or unrecognized option %s\n", opt);
return 2;
}
}
}
}
int
main (int argc, char *argv[])
{
int dec = 0;
char *id = 0;
for (;;)
{
int c = getopt (argc, argv, "Vvdi:In123456789");
if (c == EOF)
break;
switch (c)
{
case 'V':
puts (version);
exit (EXIT_SUCCESS);
break;
case 'v':
verbose = 1;
break;
case 'd':
dec = 1;
break;
case 'i':
id = optarg;
break;
case 'I':
showident = 1;
break;
case 'n':
nooutput = 1;
break;
case '1':
case '2':
case '3':
case '4':
complevel = 1;
break;
case '5':
case '6':
complevel = 5;
break;
case '7':
case '8':
complevel = 7;
break;
case '9':
complevel = 9;
break;
case '?':
fprintf (stderr, "encode: basex [-v] [-n] [-i id] [-1 | -5 | -7 | -9]\n");
fprintf (stderr, "decode: basex [-d] [-v] [-n] [-I]\n");
break;
}
}
if (verbose)
fprintf (stderr, "mode %s\n", dec ? "decoding" : "encoding");
if (dec)
return decode (stdin, stdout);
else
return encode (stdin, stdout, id);
}