| 1 |
jmc |
1.1 |
/* macbhex.c -- simple binhex decoding routine */ |
| 2 |
|
|
/* (C) Copyright 1993,1994 by Carnegie Mellon University |
| 3 |
|
|
* All Rights Reserved. |
| 4 |
|
|
* |
| 5 |
|
|
* Permission to use, copy, modify, distribute, and sell this software |
| 6 |
|
|
* and its documentation for any purpose is hereby granted without |
| 7 |
|
|
* fee, provided that the above copyright notice appear in all copies |
| 8 |
|
|
* and that both that copyright notice and this permission notice |
| 9 |
|
|
* appear in supporting documentation, and that the name of Carnegie |
| 10 |
|
|
* Mellon University not be used in advertising or publicity |
| 11 |
|
|
* pertaining to distribution of the software without specific, |
| 12 |
|
|
* written prior permission. Carnegie Mellon University makes no |
| 13 |
|
|
* representations about the suitability of this software for any |
| 14 |
|
|
* purpose. It is provided "as is" without express or implied |
| 15 |
|
|
* warranty. |
| 16 |
|
|
* |
| 17 |
|
|
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO |
| 18 |
|
|
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
| 19 |
|
|
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE |
| 20 |
|
|
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 21 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
| 22 |
|
|
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
| 23 |
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
| 24 |
|
|
* SOFTWARE. |
| 25 |
|
|
*/ |
| 26 |
|
|
|
| 27 |
|
|
#include <stdio.h> |
| 28 |
|
|
#include <ctype.h> |
| 29 |
|
|
#include <memory.h> |
| 30 |
|
|
#include "part.h" |
| 31 |
|
|
#include "macnapp.h" |
| 32 |
|
|
#include "macmpack.h" |
| 33 |
|
|
|
| 34 |
|
|
/* from macos.c: */ |
| 35 |
|
|
extern void renameDescFile(char *, short, long); |
| 36 |
|
|
|
| 37 |
|
|
char binhex_decode[256] = { |
| 38 |
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| 39 |
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| 40 |
|
|
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, |
| 41 |
|
|
13, 14, 15, 16, 17, 18, 19, -1, 20, 21, -1, -1, -1, -1, -1, -1, |
| 42 |
|
|
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, |
| 43 |
|
|
37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, -1, -1, -1, -1, |
| 44 |
|
|
48, 49, 50, 51, 52, 53, 54, -1, 55, 56, 57, 58, 59, 60, -1, -1, |
| 45 |
|
|
61, 62, 63, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| 46 |
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| 47 |
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| 48 |
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| 49 |
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| 50 |
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| 51 |
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| 52 |
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| 53 |
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
| 54 |
|
|
}; |
| 55 |
|
|
#define BHEXVAL(c) (binhex_decode[(unsigned char) c]) |
| 56 |
|
|
|
| 57 |
|
|
typedef union { |
| 58 |
|
|
unsigned char c[4]; |
| 59 |
|
|
unsigned long val; |
| 60 |
|
|
} longbuf; |
| 61 |
|
|
|
| 62 |
|
|
typedef struct { |
| 63 |
|
|
OSType type, creator; |
| 64 |
|
|
unsigned short flags; |
| 65 |
|
|
long dlen, rlen; |
| 66 |
|
|
} binhex_header; |
| 67 |
|
|
|
| 68 |
|
|
#define STATE_START 0 |
| 69 |
|
|
#define STATE_FNAME 1 |
| 70 |
|
|
#define STATE_HEADER 2 |
| 71 |
|
|
#define STATE_HCRC 3 |
| 72 |
|
|
#define STATE_DFORK 4 |
| 73 |
|
|
#define STATE_DCRC 5 |
| 74 |
|
|
#define STATE_RFORK 6 |
| 75 |
|
|
#define STATE_RCRC 7 |
| 76 |
|
|
#define STATE_DONE 8 |
| 77 |
|
|
#define STATE_ERROR 9 |
| 78 |
|
|
|
| 79 |
|
|
typedef struct binhex_state { |
| 80 |
|
|
short state; /* current state */ |
| 81 |
|
|
short part; /* current part number */ |
| 82 |
|
|
unsigned short CRC; /* cumulative CRC */ |
| 83 |
|
|
unsigned short fileCRC; /* CRC value from file */ |
| 84 |
|
|
longbuf octetbuf; /* buffer for decoded 6-bit values */ |
| 85 |
|
|
short octetin; /* current input position in octetbuf */ |
| 86 |
|
|
short donepos; /* ending position in octetbuf */ |
| 87 |
|
|
short inCRC; /* flag set when reading a CRC */ |
| 88 |
|
|
long count; /* generic counter */ |
| 89 |
|
|
FILE *outfile; /* output file */ |
| 90 |
|
|
short marker; /* flag indicating maker */ |
| 91 |
|
|
unsigned char rlebuf; /* buffer for last run length encoding value */ |
| 92 |
|
|
PCstr namebuf[65]; /* buffer for binhex filename */ |
| 93 |
|
|
binhex_header head; /* buffer for header */ |
| 94 |
|
|
FSSpec fspec; /* output file */ |
| 95 |
|
|
} binhex_state; |
| 96 |
|
|
|
| 97 |
|
|
/* global state */ |
| 98 |
|
|
static binhex_state bh; |
| 99 |
|
|
|
| 100 |
|
|
/* process a binhex character |
| 101 |
|
|
*/ |
| 102 |
|
|
static void binhex_process(struct part *inpart) |
| 103 |
|
|
{ |
| 104 |
|
|
unsigned short tmpcrc, cval; |
| 105 |
|
|
unsigned char ctmp, c = bh.rlebuf; |
| 106 |
|
|
StandardFileReply reply; |
| 107 |
|
|
FInfo finfo; |
| 108 |
|
|
char buf[256]; |
| 109 |
|
|
|
| 110 |
|
|
/* do CRC */ |
| 111 |
|
|
ctmp = bh.inCRC ? c : 0; |
| 112 |
|
|
cval = bh.CRC & 0xf000; |
| 113 |
|
|
tmpcrc = ((unsigned short) (bh.CRC << 4) | (ctmp >> 4)) |
| 114 |
|
|
^ (cval | (cval >> 7) | (cval >> 12)); |
| 115 |
|
|
cval = tmpcrc & 0xf000; |
| 116 |
|
|
bh.CRC = ((unsigned short) (tmpcrc << 4) | (ctmp & 0x0f)) |
| 117 |
|
|
^ (cval | (cval >> 7) | (cval >> 12)); |
| 118 |
|
|
|
| 119 |
|
|
/* handle state */ |
| 120 |
|
|
switch (bh.state) { |
| 121 |
|
|
case STATE_START: |
| 122 |
|
|
bh.state = STATE_FNAME; |
| 123 |
|
|
bh.count = 1; |
| 124 |
|
|
*bh.namebuf = (c & 63); |
| 125 |
|
|
break; |
| 126 |
|
|
case STATE_FNAME: |
| 127 |
|
|
bh.namebuf[bh.count] = c; |
| 128 |
|
|
if (bh.count++ > *bh.namebuf) { |
| 129 |
|
|
bh.state = STATE_HEADER; |
| 130 |
|
|
bh.count = 0; |
| 131 |
|
|
} |
| 132 |
|
|
break; |
| 133 |
|
|
case STATE_HEADER: |
| 134 |
|
|
((char *)&bh.head)[bh.count] = c; |
| 135 |
|
|
if (++bh.count == 18) { |
| 136 |
|
|
bh.state = STATE_HCRC; |
| 137 |
|
|
bh.inCRC = 1; |
| 138 |
|
|
bh.count = 0; |
| 139 |
|
|
} |
| 140 |
|
|
break; |
| 141 |
|
|
case STATE_DFORK: |
| 142 |
|
|
case STATE_RFORK: |
| 143 |
|
|
putc(c, bh.outfile); |
| 144 |
|
|
if (--bh.count == 0) { |
| 145 |
|
|
fclose(bh.outfile); |
| 146 |
|
|
bh.outfile = NULL; |
| 147 |
|
|
++bh.state; |
| 148 |
|
|
bh.inCRC = 1; |
| 149 |
|
|
} |
| 150 |
|
|
break; |
| 151 |
|
|
case STATE_HCRC: |
| 152 |
|
|
case STATE_DCRC: |
| 153 |
|
|
case STATE_RCRC: |
| 154 |
|
|
if (!bh.count++) { |
| 155 |
|
|
bh.fileCRC = (unsigned short) c << 8; |
| 156 |
|
|
} else { |
| 157 |
|
|
if ((bh.fileCRC | c) != bh.CRC) { |
| 158 |
|
|
if (bh.state > STATE_HCRC) { |
| 159 |
|
|
HDelete(bh.fspec.vRefNum, bh.fspec.parID, bh.fspec.name); |
| 160 |
|
|
SetCursor(&arrow); |
| 161 |
|
|
yell("BinHex file corrupted in transit"); |
| 162 |
|
|
SetCursor(&watch); |
| 163 |
|
|
} |
| 164 |
|
|
bh.state = STATE_ERROR; |
| 165 |
|
|
break; |
| 166 |
|
|
} |
| 167 |
|
|
bh.CRC = 0; |
| 168 |
|
|
if (++bh.state == STATE_DONE) { |
| 169 |
|
|
finfo.fdType = bh.head.type; |
| 170 |
|
|
finfo.fdCreator = bh.head.creator; |
| 171 |
|
|
finfo.fdFlags = bh.head.flags & 0xf800; |
| 172 |
|
|
HSetFInfo(bh.fspec.vRefNum, bh.fspec.parID, bh.fspec.name, &finfo); |
| 173 |
|
|
PtoCstr(bh.fspec.name); |
| 174 |
|
|
renameDescFile((char *)bh.fspec.name, bh.fspec.vRefNum, bh.fspec.parID); |
| 175 |
|
|
break; |
| 176 |
|
|
} |
| 177 |
|
|
bh.count = bh.head.rlen; |
| 178 |
|
|
if (bh.state == STATE_DFORK) { |
| 179 |
|
|
/* prompt user */ |
| 180 |
|
|
sprintf(buf, "Saving BinHex file %s", C(bh.namebuf)); |
| 181 |
|
|
chat(buf); |
| 182 |
|
|
SetCursor(&arrow); |
| 183 |
|
|
NAputFile("\pSave decoded BinHex file as:", P(bh.namebuf), &reply); |
| 184 |
|
|
SetCursor(&watch); |
| 185 |
|
|
statrefresh(); |
| 186 |
|
|
if (!reply.sfGood) { |
| 187 |
|
|
didchat = -1; |
| 188 |
|
|
bh.state = STATE_ERROR; |
| 189 |
|
|
} else { |
| 190 |
|
|
bh.fspec = reply.sfFile; |
| 191 |
|
|
HCreate(bh.fspec.vRefNum, bh.fspec.parID, bh.fspec.name, |
| 192 |
|
|
bh.head.creator, bh.head.type); |
| 193 |
|
|
bh.count = bh.head.dlen; |
| 194 |
|
|
} |
| 195 |
|
|
} |
| 196 |
|
|
if (bh.count) { |
| 197 |
|
|
bh.inCRC = 0; |
| 198 |
|
|
bh.outfile = Macopen(inpart->infile, bh.fspec.name, bh.fspec.vRefNum, |
| 199 |
|
|
bh.fspec.parID, 1, bh.state == STATE_DFORK ? 0 : 1, fsWrPerm); |
| 200 |
|
|
if (!bh.outfile) { |
| 201 |
|
|
bh.state = STATE_ERROR; |
| 202 |
|
|
HDelete(bh.fspec.vRefNum, bh.fspec.parID, bh.fspec.name); |
| 203 |
|
|
SetCursor(&arrow); |
| 204 |
|
|
yell("Failed to open file for writing"); |
| 205 |
|
|
SetCursor(&watch); |
| 206 |
|
|
} |
| 207 |
|
|
} else { |
| 208 |
|
|
++bh.state; |
| 209 |
|
|
} |
| 210 |
|
|
} |
| 211 |
|
|
break; |
| 212 |
|
|
} |
| 213 |
|
|
} |
| 214 |
|
|
|
| 215 |
|
|
/* |
| 216 |
|
|
* decode a binhex file |
| 217 |
|
|
* returns -1 on fatal error, 0 for continue, 1 for done |
| 218 |
|
|
*/ |
| 219 |
|
|
int os_binhex(struct part *inpart, int part, int nparts) |
| 220 |
|
|
{ |
| 221 |
|
|
long val; |
| 222 |
|
|
int c; |
| 223 |
|
|
char *bptr; |
| 224 |
|
|
short octetpos; |
| 225 |
|
|
static char buf[1024]; |
| 226 |
|
|
|
| 227 |
|
|
/* reset state */ |
| 228 |
|
|
if (part == 1) { |
| 229 |
|
|
bh.state = STATE_START; |
| 230 |
|
|
bh.part = 0; |
| 231 |
|
|
bh.CRC = 0; |
| 232 |
|
|
bh.octetbuf.val = 0; |
| 233 |
|
|
bh.octetin = 26; |
| 234 |
|
|
bh.donepos = 3; |
| 235 |
|
|
bh.inCRC = 0; |
| 236 |
|
|
bh.outfile = NULL; |
| 237 |
|
|
bh.marker = 0; |
| 238 |
|
|
} |
| 239 |
|
|
if (++bh.part != part) bh.state = STATE_ERROR; |
| 240 |
|
|
|
| 241 |
|
|
/* do nothing on error/completion */ |
| 242 |
|
|
if (!inpart) { |
| 243 |
|
|
if (bh.state < STATE_DONE) bh.state = STATE_ERROR; |
| 244 |
|
|
} else { |
| 245 |
|
|
/* skip blank lines */ |
| 246 |
|
|
do { |
| 247 |
|
|
if (part_gets(buf, sizeof (buf), inpart) == NULL) return (0); |
| 248 |
|
|
} while (*buf == '\n'); |
| 249 |
|
|
bptr = buf; |
| 250 |
|
|
if (part == 1 && *bptr++ != ':') bh.state = STATE_ERROR; |
| 251 |
|
|
|
| 252 |
|
|
/* line reading loop */ |
| 253 |
|
|
do { |
| 254 |
|
|
/* check line for separator */ |
| 255 |
|
|
if (!strncmp(buf, "--- ", 4)) break; |
| 256 |
|
|
buf[strlen(buf) - 1] = '\0'; |
| 257 |
|
|
|
| 258 |
|
|
/* loop through line of binhex charaters */ |
| 259 |
|
|
while (bh.state < STATE_DONE) { |
| 260 |
|
|
/* fill in octetbuf */ |
| 261 |
|
|
do { |
| 262 |
|
|
if ((val = BHEXVAL(*bptr++)) == -1) { |
| 263 |
|
|
if (bptr[-1]) { |
| 264 |
|
|
--bh.donepos; |
| 265 |
|
|
if (bh.octetin >= 14) --bh.donepos; |
| 266 |
|
|
if (bh.octetin >= 20) --bh.donepos; |
| 267 |
|
|
} |
| 268 |
|
|
break; |
| 269 |
|
|
} |
| 270 |
|
|
bh.octetbuf.val |= val << bh.octetin; |
| 271 |
|
|
} while ((bh.octetin -= 6) > 2); |
| 272 |
|
|
if (!bptr[-1]) break; |
| 273 |
|
|
|
| 274 |
|
|
/* handle decoded characters -- run length encoding (rle) detection */ |
| 275 |
|
|
for (octetpos = 0; octetpos < bh.donepos; ++octetpos) { |
| 276 |
|
|
/* get character & handle rle */ |
| 277 |
|
|
c = bh.octetbuf.c[octetpos]; |
| 278 |
|
|
if (c == 0x90 && !bh.marker++) continue; |
| 279 |
|
|
if (bh.marker) { |
| 280 |
|
|
if (c == 0) { |
| 281 |
|
|
bh.rlebuf = 0x90; |
| 282 |
|
|
binhex_process(inpart); |
| 283 |
|
|
} else { |
| 284 |
|
|
while (--c > 0) { |
| 285 |
|
|
binhex_process(inpart); |
| 286 |
|
|
} |
| 287 |
|
|
} |
| 288 |
|
|
bh.marker = 0; |
| 289 |
|
|
} else { |
| 290 |
|
|
bh.rlebuf = (unsigned char) c; |
| 291 |
|
|
binhex_process(inpart); |
| 292 |
|
|
} |
| 293 |
|
|
if (bh.state >= STATE_DONE) break; |
| 294 |
|
|
} |
| 295 |
|
|
if (bh.donepos < 3 && bh.state < STATE_DONE) bh.state = STATE_ERROR; |
| 296 |
|
|
bh.octetin = 26; |
| 297 |
|
|
bh.octetbuf.val = 0; |
| 298 |
|
|
} |
| 299 |
|
|
} while (bh.state < STATE_DONE && part_gets(bptr = buf, sizeof (buf), inpart) != NULL); |
| 300 |
|
|
} |
| 301 |
|
|
|
| 302 |
|
|
/* error clean up */ |
| 303 |
|
|
if (bh.state == STATE_ERROR && bh.outfile) { |
| 304 |
|
|
fclose(bh.outfile); |
| 305 |
|
|
bh.outfile = NULL; |
| 306 |
|
|
HDelete(bh.fspec.vRefNum, bh.fspec.parID, bh.fspec.name); |
| 307 |
|
|
} |
| 308 |
|
|
|
| 309 |
|
|
return (bh.state == STATE_ERROR ? 1 : 0); |
| 310 |
|
|
} |