home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Dream 52
/
Amiga_Dream_52.iso
/
Amiga
/
Workbench
/
Archivers
/
mcvertWOS.lha
/
mcvertppc
/
hqxify.c
next >
Wrap
C/C++ Source or Header
|
1998-04-12
|
39KB
|
1,016 lines
#include "mactypes.h"
/* HQXBUFLEN must be large enough to hold a complete hqx header */
#define HQXBUFLEN 512
byte hqxbuf[HQXBUFLEN + 1], *buf_ptr, *buf_end, *buf_start = hqxbuf + 1;
/* Note: line[0] stores the run length character,
* so line is one greater than MAXLINE */
#define MAXLINE 2048
byte line[MAXLINE + 1], *line_ptr, *line_end, *line_start = line + 1;
/* keep the compiler happy */
#define LINE_START ((char*)(line_start))
extern char *cmdname;
int line_count, file_count;
int save_state, total_bytes, save_run_length;
word save_accum;
char binfname[SYSNAMELEN], hqxfname[SYSNAMELEN];
FILE *hqxfile, *binfile;
/*
* hqxsuspect caches whether or not we have gotten past all suspect data
* at the head of a file, for example, a mail header. hqxsuspect is set
* on every new hqx file read and on detecting bogus lines,
* and reset only in non_suspect().
*/
int hqxsuspect;
extern suspect_shorter;
extern suspect_same;
extern int info_only;
/* This routine reads the header of a hqxed file and appropriately twiddles it,
determines if it has CRC problems, creates the .bin file, and puts the info
into the .bin file.
Output is hqx_datalen, hqx_rsrclen, type, binfname, binfile.
Returns 0 iff failed to read header.
*/
int
hqx_to_bin_hdr(char *type, ulong *hqx_datalen, ulong *hqx_rsrclen, int same_file_only)
{
register byte *hqx_ptr, *hqx_end;
register ulong calc_crc;
hqx_buf *hqx_block;
hqx_header *hqx;
info_header info;
ulong mtim;
short crc;
long len;
extern word magic[];
extern char *dir, *ext;
extern short calc_mb_crc();
/* read the hqx header,
* assuming that I won't exhaust hqxbuf in so doing --
* that is, that hqxbuf is always large enough (it is)
*/
(void)fill_hqxbuf(same_file_only);
/*
* If we are reading multiple files, then we could have the last
* unread line of the "previous" file be just a colon (since we are
* length driven, and stopped when we processed the expected
* lengths), and the prior fill_hqxbuf() call would find it and
* return 0, leaving the buffer unfilled. So, if we have zero
* bytes so far, just fill it again.
*/
if (total_bytes == 0) {
#ifndef NDEBUG
fprintf(debug,
"%s: Note: had to call fill_hqxbuf again in hqx_to_bin_hdr\n",
cmdname);
fflush(debug);
#endif
(void)fill_hqxbuf(same_file_only);
}
if (same_file_only == 1 && total_bytes == 0)
return 0;
if (total_bytes < MIN_HQX_HDR) {
fprintf(verbose, "%s: %s (%d < %d): bad file format? -- exiting\n",
cmdname, "error: hqx_header too short", total_bytes, MIN_HQX_HDR);
fflush(verbose);
exit(2);
}
hqx_block = (hqx_buf *) buf_ptr;
hqx = (hqx_header *) (hqx_block->name + hqx_block->nlen);
hqx_ptr = buf_ptr;
hqx_end = (byte *) hqx + sizeof(hqx_header) - 1;
calc_crc = 0;
while (hqx_ptr < hqx_end)
calc_crc = (((calc_crc & 0xff) << 8) | *hqx_ptr++) ^ magic[calc_crc >> 8];
calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
buf_ptr = hqx_ptr;
/* stuff the hqx header data into the info header */
bzero((char*)&info, sizeof(info_header));
info.nlen = hqx_block->nlen;
if (info.nlen > sizeof(info.name))
error("Error: corrupt BinHex data format", "");
strncpy((char*)info.name, (char*)hqx_block->name, (int)info.nlen);/* name */
/* now make sure the resulting name is valid for length only */
info.nlen = macify((char*)info.name, (int)info.nlen, 0);
bcopy((char*)hqx->type, (char*)info.type, 9); /* type, author, flag */
info.flags &= 0x7e; /* reset lock bit, init bit */
if (hqx->protect & 0x40)
info.protect = 1; /* copy protect bit */
bcopy((char*)hqx->dlen, (char*)info.dlen, 8); /* dlen, rlen */
mtim = unix2mac((ulong)time((long*)0));
bcopy((char*)&mtim, (char*)info.mtim, 4);
bcopy((char*)&mtim, (char*)info.ctim, 4);
info.uploadvers = '\201';
info.readvers = '\201';
/* calculate MacBinary CRC */
crc = calc_mb_crc((unsigned char*)&info, 124L, 0);
info.crc[0] = (char) (crc >> 8);
info.crc[1] = (char) crc;
/* Create the .bin file and write the info to it */
unixify((char*)hqx_block->name);
converting(info.name, (int)info.nlen, info.type, info.auth);
print_bin_hdr("Creating", &info);
len = strlen(dir) + strlen((char*)hqx_block->name) + strlen(ext) + 1;
if (len >= sizeof(binfname))
error("Error: generated binfname would be too long", "");
sprintf(binfname, "%s%s", dir, hqx_block->name, ext);
binfile = mopen(binfname, "", "w");
check_hqx_crc((word)calc_crc, "File header CRC mismatch in %s", binfname);
if (1 != fwrite((char*)&info, sizeof(info), 1, binfile))
error("fwrite failed on binfile", "");
/* Get a couple of items we'll need later */
bcopy((char*)info.dlen, (char*)hqx_datalen, 4);
*hqx_datalen = mac2long(*hqx_datalen);
bcopy((char*)info.rlen, (char*)hqx_rsrclen, 4);
*hqx_rsrclen = mac2long(*hqx_rsrclen);
bcopy((char*)info.type, (char*)type, 4);
/* emit useful debugging info */
#ifndef NDEBUG
fprintf(debug, "\tdata_len=%10ld\t\trsrc_len=%10ld\n",
*hqx_datalen, *hqx_rsrclen);
fflush(debug);
#endif
return 1;
}
/* This routine reads the header of a bin file and appropriately twiddles it,
creates the .hqx file, and puts the info into the .hqx file.
Output is hqx_datalen, hqx_rsrclen, type, hqxfname, hqxfile */
bin_to_hqx_hdr(ulong *hqx_datalen, ulong *hqx_rsrclen)
{
register byte *hqx_ptr, *hqx_end;
register ulong calc_crc;
hqx_buf *hqx_block;
hqx_header *hqx;
info_header info;
extern word magic[];
extern char **hqxnames_left;
extern char *ext;
long len;
len = strlen(*hqxnames_left);
if (len >= sizeof(binfname))
error("Error: specified binfname is too long", "");
strcpy(binfname, *hqxnames_left++);
binfile = mopen(binfname, ext, "r");
if (!fread((char*)&info, sizeof(info), 1, binfile))
error("Unexpected EOF in MacBinary header of %s", binfname);
/* stuff the info header into the hqx header */
hqx_block = (hqx_buf *) buf_ptr;
hqx_block->nlen = info.nlen;
if (info.nlen > sizeof(hqx_block->name))
error("Error: corrupt MacBinary data format", "");
strncpy((char*)hqx_block->name, (char*)info.name, (int)info.nlen);
/*
* We expect a valid Macintosh file name since it came from a MacBinary file
* so we don't potentially corrupt a valid copied name via macify translate.
* File name length should not be a problem, so we do nothing.
*/
hqx = (hqx_header *) (hqx_block->name + hqx_block->nlen);
hqx->version = 0;
bcopy((char*)info.type, (char*)hqx->type, 9); /* type, author, flags */
hqx->flags &= 0x7e; /* reset lock bit, init bit */
if (info.protect == 1)
hqx->protect = 0; /* protect bit: 0x40 */
else
hqx->protect = 0;
bcopy((char*)info.dlen, (char*)hqx->dlen, 8); /* dlen, rlen */
/* Create the .hqx file and write the info to it */
#ifdef notdef
This is the right thing to check but the Sun optimizing
compiler gives a (valid) warning that info.nlen (one char,
255 max) is always less than sizeofhqxfname) which is
hardwired to 1024 since version 1.99. Since we have already
checked info.nlen above, we skip the test and avoid the warning.
if (info.nlen >= sizeof(hqxfname))
error("Error: generated hqxfname would be too long", "");
#endif
strncpy(hqxfname, (char*)info.name, (int)info.nlen);
hqxfname[info.nlen] = '\0';
unixify(hqxfname);
converting(info.name, (int)info.nlen, info.type, info.auth);
print_bin_hdr("Reading", &info);
calc_crc = 0;
hqx_ptr = (byte *) hqx_block;
hqx_end = hqx_ptr + hqx_block->nlen + sizeof(hqx_header);
while (hqx_ptr < hqx_end)
calc_crc = (((calc_crc & 0xff) << 8) | *hqx_ptr++) ^ magic[calc_crc >> 8];
calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
buf_ptr = hqx_end;
write_hqx_crc((word)calc_crc);
/* Get a couple of items we'll need later */
bcopy((char*)info.dlen, (char*)hqx_datalen, 4);
*hqx_datalen = mac2long(*hqx_datalen);
bcopy((char*)info.rlen, (char*)hqx_rsrclen, 4);
*hqx_rsrclen = mac2long(*hqx_rsrclen);
}
/* This routine copies bytes from the decoded input stream to the output.
It also pads to a multiple of 128 bytes on the output, which is part
of the .bin format */
word
hqx_to_bin_fork(register ulong nbytes)
{
register byte *cp;
register ulong calc_crc;
register int c_length;
ulong extra_bytes;
extern word magic[];
long avail = 0; /* used for internal consistency checking */
int wrote;
extra_bytes = 127 - (nbytes + 127) % 128; /* pad fork to mult of
* 128 bytes */
calc_crc = 0;
for (;;) {
cp = buf_ptr;
c_length = (cp + nbytes > buf_end) ? buf_end - cp : nbytes;
/* we can only check readily if we read it here */
if (avail && c_length > avail)
error("hqx_to_bin_fork: writing %ld too many bytes",
(char*)c_length - avail);
nbytes -= c_length;
wrote = fwrite((char*)cp, sizeof(byte), c_length, binfile);
if (wrote != c_length)
error("hqx_to_bin_fork: fwrite on binfile wrote %ld bytes too few",
(char*)c_length-wrote);
while (c_length--)
calc_crc = (((calc_crc & 0xff) << 8) | *cp++) ^ magic[calc_crc >> 8];
if (!nbytes)
break;
avail = fill_hqxbuf(0);
}
buf_ptr = cp;
while (extra_bytes--)
if (EOF == putc(0, binfile))
error("Error: putc failed on binfile", "");
calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
return (word) calc_crc;
}
/* This routine copies bytes from the input stream to the encoded output.
It also pads to a multiple of 128 bytes on the input, which is part
of the .bin format */
word
bin_to_hqx_fork(register ulong nbytes)
{
register byte *cp;
register ulong calc_crc;
register int c_length;
ulong extra_bytes;
extern word magic[];
extra_bytes = 127 - (nbytes + 127) % 128; /* pad fork to mult of
* 128 bytes */
calc_crc = 0;
for (;;) {
cp = buf_ptr;
c_length = (cp + nbytes > buf_end) ? buf_end - cp : nbytes;
nbytes -= c_length;
if (c_length != fread((char*)cp, sizeof(byte), c_length, binfile))
error("fread failed on binfile", "");
buf_ptr += c_length;
while (c_length--)
calc_crc = (((calc_crc & 0xff) << 8) | *cp++) ^ magic[calc_crc >> 8];
if (!nbytes)
break;
empty_hqxbuf();
}
buf_ptr = cp;
fseek(binfile, (long)extra_bytes, 1);
calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
return (word) calc_crc;
}
/* Essentials for Binhex 8to6 run length encoding */
#define RUNCHAR 0x90
#define MAXRUN 255
#define IS_LEGAL <0x40
#define ISNT_LEGAL >0x3f
#define DONE 0x7F /* tr68[':'] = DONE, since Binhex terminator is ':' */
#define SKIP 0x7E /* tr68['\n'|'\r'] = SKIP, i. e. end of line char. */
/* We also treat '\0' as SKIP to handle files without trailing newline */
/*
NOTE: we really don't do a very good job of handling lines
with ^M's as the end of line character, since we jettison
the entire UNIX line (which can look like multiple Mac
lines) when rejecting the line. Easiest way to fix this is
to write our own version of fgets which breaks on ^M or ^J.
*/
#define FAIL 0x7D /* character illegal in binhex file */
byte tr86[] =
"!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
byte tr68[] = {
/* 0x00 */
SKIP, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, SKIP, FAIL, FAIL, SKIP, FAIL, FAIL,
/* 0x10 */
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 0x20 */
FAIL, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, FAIL, FAIL,
/* 0x30 */
0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, FAIL,
0x14, 0x15, DONE, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 0x40 */
0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, FAIL,
/* 0x50 */
0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, FAIL,
0x2C, 0x2D, 0x2E, 0x2F, FAIL, FAIL, FAIL, FAIL,
/* 0x60 */
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, FAIL,
0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, FAIL, FAIL,
/* 0x70 */
0x3D, 0x3E, 0x3F, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 0x80 */
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 0x90 */
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 0xA0 */
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 0xB0 */
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 0xC0 */
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 0xD0 */
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 0xE0 */
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 0xF0 */
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
};
/*
* This procedure transparently reads and decodes the hqx input.
* It does run length and 6 to 8 decoding.
* As many lines as required are read to fill up buf.
* Global Input
* buf_start
* line_ptr
* save_*
* Global Output
* buf_ptr end of buffer used
* line_ptr
* save_*
* Internal
* line holds the encoded incoming ascii line.
* buf holds the decoding binary binary. [what the heck does this mean?]
* buf[-1] holds the character to be repeated for run length encoding.
*/
#define READING 0
#define SKIPPING 1
#define FIND_START_COLON 2
#define FINDING_COLON 3
/*
* It's too bad we can't look for ``(This file ..'' when hunting for a
* starting colon, but we don't emit such a line at the head of every
* segment, so we can't expect to find one, now, can we?
* We were really too brittle when scanning, since we only
* accepted lines which were exactly 64 (HQXLINELEN) characters long.
* Fixed in version 1.83. But allowing any length valid line
* permitted the false-match-in-mail-headers-problem for lines like "---".
* Better header skipping provided in 1.84.
* XXX: rather than decode line inplace, it would be better
* to leave the original intact to enable better warning messages
*/
/* returns the number of bytes read */
fill_hqxbuf(int same_file_only)
{
register ulong c, accum;
register int not_in_a_run = TRUE, state68;
register byte *fast_buf, *fast_line;
static int phase = FIND_START_COLON;
long avail;
buf_ptr = fast_buf = buf_start;
fast_line = line_ptr;
state68 = save_state;
accum = save_accum;
if (save_run_length > 0) {
c = save_run_length;
save_run_length = 0;
goto continue_run;
}
while (fast_buf < buf_end) {
next_char:
if ((c = *fast_line++) ISNT_LEGAL) {
if (c == DONE) {
/* done processing the file, so get out */
/* phase has already been set by read processing */
break;
}
next_line:
if (!fgets(LINE_START, MAXLINE, hqxfile)) {
if (same_file_only) {
avail = fast_buf - buf_ptr;
if (avail != 0) {
#ifndef NDEBUG
fprintf(debug, "DEBUG: avail: %d\n", avail);
fflush(debug);
#endif
error(
"Premature EOF reading non-initial BinHex 4.0 header from %s",
hqxfname);
} else {
/* this routine may be called many times in a row,
* and we must ensure that we don't allow fast_line
* to march beyond the end of line (it did), so we jam
* fast_line to the start of the line, and salt the
* line to mark it invalid, so a new line will be fetched.
* Jeepers, how I hate the (il)logic of this routine!
*/
fast_line = line_start;
*fast_line = SKIP;
break;
}
/*NOTREACHED*/
} else if (new_in_hqx_file() == 0) {
/*
* Used to assume if no more input available through error or
* exhaustion while looking for valid data then must be done.
* But we are demand driven (now) and if called, must
* find data. So we don't silently exit any more.
*/
error("Premature EOF while reading BinHex 4.0 %s", hqxfname);
}
}
line_ptr = line_start;
if (MyDebug>1)
{
fprintf(Mydebug, "DEBUG: input: %s", line_start);
fflush(Mydebug);
}
scan_line:
/* ensure the the entire line is valid */
fast_line = line_ptr;
while ((*fast_line = tr68[*fast_line]) IS_LEGAL)
fast_line++;
/* grab the stopper */
c = *fast_line;
/* check for validity, transition phases as required */
switch (phase) {
case READING:
case SKIPPING:
case FINDING_COLON:
if (SKIP == c && fast_line > line_ptr &&
( 0 == hqxsuspect ||
non_suspect((char *)line_ptr, (char *)fast_line) )
) {
/* the entire line is valid (again), so (resume) reading */
/* hack: require the line to be non-empty to simplify logic */
/* require line to non suspect [outside [mail] header] */
phase = READING;
break;
}
if (c == DONE && tr68[fast_line[1]] == SKIP) {
/*
* valid encoded last line, so
* set phase for next line, and process this line --
* we exit the fill-the-buffer loop when the terminal
* colon is encountered during line processing
*/
phase = FIND_START_COLON;
break;
}
/* line is not entirely valid, so do some flavor of skipping */
phase = (phase == FINDING_COLON) ? FIND_START_COLON : SKIPPING;
/* we're suspicious again, since we could be reading a
* concatenated multi-segmented file */
hqxsuspect = 1;
goto next_line;
case FIND_START_COLON:
if (*line_start == DONE) {
/* can't transition to READING
* until know that entire line is valid
* so transition to intermediate state
*/
phase = FINDING_COLON;
/* skip the initial colon */
line_ptr++;
goto scan_line;
}
goto next_line;
}
/* we've read in a valid line, so start processing it */
fast_line = line_ptr;
c = *fast_line++;
/*
* Jskud 15Jul92: fix bug reported by Info-Mac Moderator Bill
* regarding case of last line just :
* The line is valid, but it has no data, so don't ingest it.
*/
if (c == DONE)
break;
if (MyDebug>1) {
fprintf(Mydebug, "DEBUG: processing above line\n\n");
fflush(Mydebug);
}
}
/* Finally, we have the next 6 bits worth of data in "c" as input. */
/* Note: we use "c" as the output of this processing too */
switch (state68++) {
case 0:
accum = c;
goto next_char;
case 1:
accum = (accum << 6) | c;
c = accum >> 4;
break;
case 2:
accum = (accum << 6) | c;
c = (accum >> 2) & 0xff;
break;
case 3:
/* we avoid any miniscule optimizations here
* to maintain parallelism and clarity
* which should enhance program maintainability
*/
accum = (accum << 6) | c;
c = accum & 0xff;
state68 = 0;
break;
}
if (not_in_a_run)
if (c != RUNCHAR)
*fast_buf++ = c;
else {
not_in_a_run = FALSE;
goto next_char;
}
else {
/* "c" has the run total length, not just the incremental,
hence the post decrement is correct */
if (c--) {
avail = buf_end - fast_buf;
if (c > avail) {
save_run_length = c - avail;
c = avail;
}
continue_run:
{
register char ch = fast_buf[-1];
while (c--)
*fast_buf++ = ch;
}
} else
/* handle special case of 0x9000 => 0x90 */
*fast_buf++ = RUNCHAR;
not_in_a_run = TRUE;
}
}
/* exit: */
avail = fast_buf - buf_ptr;
total_bytes += avail;
buf_start[-1] = fast_buf[-1];
line_ptr = fast_line;
save_state = state68;
save_accum = accum;
return avail;
}
non_suspect(char *start, char *beyond)
{
int looking_good;
int len;
register char *cp;
register char *last;
char *skip_msg = "Warning: skipping legal but suspect line in hqx file";
/* ensure it's long enough */
len = beyond - start;
looking_good = len >= suspect_shorter;
#ifndef NDEBUG
fprintf(debug, "DEBUG: non_suspect: long enough: %d\n",
looking_good);
fflush(debug);
#endif
if (!looking_good) {
fprintf(verbose, "%s -- line too short (%d < %d)\n",
skip_msg, len, suspect_shorter);
fflush(verbose);
}
/* ensure it's different enough */
if (suspect_same && looking_good) {
last = beyond - 1;
for (cp = start; cp < last; cp++)
if (*cp != *last) {
break;
}
/* is different */
looking_good = cp != last;
#ifndef NDEBUG
fprintf(debug,
"DEBUG: non_suspect: different enough: %d\n",
looking_good);
fflush(debug);
#endif
if (!looking_good) {
if (*last IS_LEGAL) {
fprintf(verbose, "%s -- all one input char ('%c')\n",
skip_msg, tr86[*last] );
} else {
fprintf(verbose, "%s -- all chars mapped to 0x%02x\n",
skip_msg, *last );
}
fflush(verbose);
}
}
hqxsuspect = !looking_good;
return looking_good;
}
new_in_hqx_file()
{
extern char **hqxnames_left;
int result;
long len;
#ifndef NDEBUG
fprintf(debug, "entering new_in_hqx_file ...\n");
fflush(debug);
#endif
if (*hqxnames_left[0] == '\0' || *hqxnames_left[0] == '-') {
result = FALSE;
goto exit;
}
len = strlen(*hqxnames_left);
if (len >= sizeof(hqxfname))
error("Error: specified hqxfname is too long", "");
strcpy(hqxfname, *hqxnames_left++);
/*
* we used to use freopen,
* but now suffer the slight inefficiency of close/open
* to provide info_only and consistent handling
* with good software engineering methods
*/
mclose(&hqxfile, "hqxfile");
hqxfile = mopen(hqxfname, ".hqx", "r");
hqxsuspect = 1;
(void)fgets(LINE_START, MAXLINE, hqxfile);
result = TRUE;
exit:
#ifndef NDEBUG
if (result == TRUE)
DEBUG && fprintf(debug, "... opened %s\n", hqxfname);
else
DEBUG && fprintf(debug, "... nothing to open\n");
DEBUG && fflush(debug);
#endif
return result;
}
/*
* This procedure transparently encodes and writes the hqx output.
* It does run length and 8 to 6 encoding.
*/
empty_hqxbuf()
{
register ulong c, accum, last_c;
register byte *fast_buf, *fast_line;
register int state86, dont_look_for_runs = FALSE, run_length;
extern int maxlines;
run_length = save_run_length;
last_c = buf_start[-1];
fast_buf = buf_start;
fast_line = line_ptr;
state86 = save_state;
accum = save_accum;
while (fast_buf < buf_ptr) {
c = *fast_buf++;
if (dont_look_for_runs)
dont_look_for_runs = FALSE;
else if (last_c == c && run_length < MAXRUN) {
run_length++;
continue;
} else {
if (run_length > 1) {
--fast_buf;
if (run_length == 2 && last_c != RUNCHAR)
c = last_c;
else {
c = RUNCHAR;
*--fast_buf = run_length;
dont_look_for_runs = TRUE;
}
run_length = 1;
} else
last_c = c;
if (c == RUNCHAR && !dont_look_for_runs) {
*--fast_buf = 0;
dont_look_for_runs = TRUE;
}
}
if (fast_line == line_end) {
if (line_count++ == maxlines)
new_out_hqx_file();
fputs(LINE_START, hqxfile);
fast_line = line_start;
}
switch (state86++) {
case 0:
*fast_line++ = tr86[c >> 2];
accum = (c << 4) & 0x3f;
break;
case 1:
*fast_line++ = tr86[(c >> 4) | accum];
accum = (c << 2) & 0x3f;
break;
case 2:
*fast_line++ = tr86[(c >> 6) | accum];
if (fast_line == line_end) {
if (line_count++ == maxlines)
new_out_hqx_file();
fputs(LINE_START, hqxfile);
fast_line = line_start;
}
*fast_line++ = tr86[c & 0x3f];
state86 = 0;
break;
}
}
save_run_length = run_length;
buf_start[-1] = last_c;
buf_ptr = buf_start;
line_ptr = fast_line;
save_state = state86;
save_accum = accum;
}
new_out_hqx_file()
{
char filename[SYSNAMELEN + 7];
extern int maxlines;
fprintf(hqxfile, "<<< End of Part %2d >>>\n", file_count);
mclose(&hqxfile, "hqxfile");
file_count++;
if (maxlines)
sprintf(filename, "%s%02d.hqx", hqxfname, file_count);
else
sprintf(filename, "%s.hqx", hqxfname);
hqxfile = mopen(filename, "", "w");
if (file_count > 1)
fprintf(hqxfile, "<<< Start of Part %2d >>>\n", file_count);
else
fprintf(hqxfile, "(This file must be converted with BinHex 4.0)\n\n");
line_count = 3;
}
check_hqx_crc(word calc_crc, char msg[], char name[])
{
word read_crc;
if (buf_ptr >= buf_end)
(void)fill_hqxbuf(0);
read_crc = *buf_ptr++ << 8;
if (buf_ptr >= buf_end)
(void)fill_hqxbuf(0);
read_crc |= *buf_ptr++;
if (read_crc != calc_crc)
error(msg, name);
}
write_hqx_crc(word calc_crc)
{
if (buf_ptr == buf_end)
empty_hqxbuf();
*buf_ptr++ = calc_crc >> 8;
if (buf_ptr == buf_end)
empty_hqxbuf();
*buf_ptr++ = calc_crc;
}
un_hqx(int unpit_flag)
{
char type[5]; /* stuff EOS */
ulong hqx_datalen, hqx_rsrclen;
word un_pit();
int unpitting, bytes_read;
word calc_crc;
extern char **hqxnames_left;
int same_file_only = 0;
int active;
/* we read EOF on this to transision, so the stream must be valid */
hqxfile = devnull;
line_end = line_start + HQXLINELEN;
buf_end = buf_start + HQXBUFLEN;
while (1) {
total_bytes = 0;
line_ptr = line_start;
/* ensure that the line buffer is considered empty */
/* why we use SKIP and not newline, I'm not sure */
line_ptr[0] = SKIP;
save_state = 0;
save_run_length = 0;
active = hqx_to_bin_hdr(type, &hqx_datalen, &hqx_rsrclen, same_file_only);
if (active == 0)
break;
same_file_only = 1;
type[4] = 0; /* set EOS */
unpitting = unpit_flag && !strcmp(type, "PIT ");
#ifndef NDEBUG
DEBUG && fprintf(debug,
"DEBUG: unpit_flag=%d type=%s unpitting=%d\n",
unpit_flag, type, unpitting);
DEBUG && fflush(debug);
#endif
if (unpitting) {
mclose(&binfile, "binfile");
/* Do not unlink files we did not open */
if (!info_only)
unlink(binfname);
bytes_read = total_bytes - (buf_end - buf_ptr);
calc_crc = un_pit();
bytes_read = total_bytes - (buf_end - buf_ptr) - bytes_read;
if (bytes_read != hqx_datalen) {
fprintf(verbose,
"Warning - Extraneous characters ignored in %s\n", binfname);
fflush(verbose);
}
} else {
calc_crc = hqx_to_bin_fork(hqx_datalen);
}
check_hqx_crc(calc_crc, "File data CRC mismatch in %s", binfname);
calc_crc = hqx_to_bin_fork(hqx_rsrclen);
check_hqx_crc(calc_crc, "File rsrc CRC mismatch in %s", binfname);
if (!unpitting) {
mclose(&binfile, "binfile");
}
}
mclose(&hqxfile, "hqxfile");
}
re_hqx()
{
word calc_crc;
ulong hqx_datalen, hqx_rsrclen;
extern char **hqxnames_left;
extern int maxlines;
line_end = line_start + HQXLINELEN;
buf_end = buf_start + HQXBUFLEN;
while (*hqxnames_left[0] != '-') {
/* we write the trailer, so the stream must be valid */
hqxfile = devnull;
/*
* We use the trick of setting our line_count at the limit to
* force an immediate transition on overflow.
*/
line_count = maxlines;
file_count = 0;
line_ptr = line_start;
*line_ptr++ = ':';
strcpy((char*)line_end, "\n");
buf_ptr = buf_start;
save_state = 0;
save_run_length = 1;
bin_to_hqx_hdr(&hqx_datalen, &hqx_rsrclen); /* calculates hqxfname */
/*
* Now that we have hqxfname, start the new file if
* not yet started. We no longer wait until the output
* buffer overflows, since empty files with short names didn't overflow!
*/
if (file_count == 0)
new_out_hqx_file();
calc_crc = bin_to_hqx_fork(hqx_datalen);
write_hqx_crc(calc_crc);
calc_crc = bin_to_hqx_fork(hqx_rsrclen);
write_hqx_crc(calc_crc);
/*
* To end a run and to get the last stray bits,
* temporarily add a char.
*/
*buf_ptr = !buf_ptr[-1];
buf_ptr++;
empty_hqxbuf();
/* now toss any extra output character generated */
if (save_state != 2)
--line_ptr;
/* if we're at the end of the line now, write it out */
if (line_ptr == line_end) {
fputs(LINE_START, hqxfile);
line_ptr = line_start;
}
/* paste the trailing colon onto the end of the line */
/* recall that line_ptr points into the line, not at the line */
strcpy((char*)line_ptr, ":\n");
/* and flush the output buffer */
fputs(LINE_START, hqxfile);
mclose(&hqxfile, "hqxfile");
mclose(&binfile, "binfile");
}
}