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 |
} |