2 * $Id: hqx.c,v 1.4 2001-03-29 16:40:35 rufustfirefly Exp $
10 #include <sys/types.h>
13 #include <sys/param.h>
16 # include <machine/endian.h>
18 # include <netinet/in.h>
26 #include <atalk/adouble.h>
27 #include <netatalk/endian.h>
32 /* String used to indicate standard input instead of a disk
33 file. Should be a string not normally used for a file
44 /* Looking for the first or any other line of a binhex file
49 /* This is the binhex run length encoding character
53 /* These are field sizes in bytes of various pieces of the
67 FILE *rawhex, *expandhex;
70 struct hqx_file_data {
71 u_int32_t forklen[ NUMFORKS ];
72 u_short forkcrc[ NUMFORKS ];
73 char path[ MAXPATHLEN + 1];
78 extern char *forkname[];
79 u_char hqx7_buf[8192];
85 hqx_open must be called first. pass it a filename that is supposed
86 to contain a binhqx file. an hqx struct will be allocated and
87 somewhat initialized; hqx_fd is set. skip_junk is called from
88 here; skip_junk leaves hqx7_first and hqx7_last set.
91 hqx_open( hqxfile, flags, fh, options )
99 fprintf( stderr, "megatron: entering hqx_open\n" );
101 if ( flags == O_RDONLY ) {
104 rawhex = fopen( "rawhex.unhex", "w" );
105 expandhex = fopen( "expandhex.unhex", "w" );
110 if ( strcmp( hqxfile, STDIN ) == 0 ) {
111 hqx.filed = fileno( stdin );
112 } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
117 if ( skip_junk( FIRST ) == 0 ) {
118 if ( hqx_header_read( fh ) == 0 ) {
122 pos = lseek( hqx.filed, 0, L_INCR );
123 fprintf( stderr, "megatron: current position is %ld\n", pos );
129 fprintf( stderr, "%s\n", hqxfile );
132 maxlen = sizeof( hqx.path ) -1;
133 strncpy( hqx.path, fh->name, maxlen );
134 strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
135 strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
136 if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
140 if ( hqx_header_write( fh ) != 0 ) {
142 fprintf( stderr, "%s\n", hqx.path );
150 * hqx_close must be called before a second file can be opened using
151 * hqx_open. Upon successful completion, a value of 0 is returned.
152 * Otherwise, a value of -1 is returned.
155 hqx_close( keepflag )
158 if ( keepflag == KEEP ) {
159 return( close( hqx.filed ));
160 } else if ( keepflag == TRASH ) {
161 if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
169 * hqx_read is called until it returns zero for each fork. when it is
170 * and finds that there is zero left to give, it reads in and compares
171 * the crc with the calculated one, and returns zero if all is well.
172 * it returns negative is the crc was bad or if has been called too many
173 * times for the same fork. hqx_read must be called enough times to
174 * return zero and no more than that.
177 hqx_read( fork, buffer, length )
189 pos = lseek( hqx.filed, 0, L_INCR );
190 fprintf( stderr, "hqx_read: current position is %ld\n", pos );
192 fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
193 fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
196 if ( hqx.forklen[ fork ] < 0 ) {
197 fprintf( stderr, "This should never happen, dude!\n" );
198 return( hqx.forklen[ fork ] );
201 if ( hqx.forklen[ fork ] == 0 ) {
202 cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
203 if ( cc == sizeof( storedcrc )) {
204 storedcrc = ntohs ( storedcrc );
206 fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
207 fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
209 if ( storedcrc == hqx.forkcrc[ fork ] ) {
212 fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
218 if ( hqx.forklen[ fork ] < length ) {
219 readlen = hqx.forklen[ fork ];
224 fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
227 cc = hqx_7tobin( buffer, readlen );
229 hqx.forkcrc[ fork ] =
230 updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
231 hqx.forklen[ fork ] -= cc;
234 fprintf( stderr, "hqx_read: chars read is %d\n", cc );
240 * hqx_header_read is called by hqx_open, and before any information can
241 * read from the hqx_header substruct. it must be called before any
242 * of the bytes of the other two forks can be read, as well.
243 * returns a negative number if it was unable to pull enough information
244 * to fill the hqx_header fields.
247 hqx_header_read( fh )
250 char *headerbuf, *headerptr;
251 u_int32_t time_seconds;
258 headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
261 mask = htons( 0xfcee );
264 if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
265 fprintf( stderr, "Premature end of file :" );
268 hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen,
272 write( headerfork, &namelen, sizeof( namelen ));
276 (char *)malloc( (unsigned int)( namelen + HEADSIZ ))) == 0 ) {
279 if ( hqx_7tobin( headerbuf, ( namelen + HEADSIZ )) == 0 ) {
281 fprintf( stderr, "Premature end of file :" );
284 headerptr = headerbuf;
285 hqx.headercrc = updcrc( hqx.headercrc,
286 (u_char *)headerbuf, ( namelen + HEADSIZ - CRCSIZ ));
289 write( headerfork, headerbuf, ( namelen + HEADSIZ ));
293 * stuff from the hqx file header
296 memcpy( fh->name, headerptr, (int)namelen );
297 headerptr += namelen;
298 headerptr += BVERSION;
299 memcpy(&fh->finder_info, headerptr, TCSIZ );
301 memcpy(&fh->finder_info.fdFlags, headerptr, FLAGSIZ );
302 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
303 headerptr += FLAGSIZ;
304 memcpy(&fh->forklen[ DATA ], headerptr, DATASIZ );
305 hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
306 headerptr += DATASIZ;
307 memcpy( &fh->forklen[ RESOURCE ], headerptr, RESSIZ );
308 hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
310 memcpy(&header_crc, headerptr, CRCSIZ );
312 header_crc = ntohs( header_crc );
315 * stuff that should be zero'ed out
318 fh->comment[0] = '\0';
319 fh->finder_info.fdLocation = 0;
320 fh->finder_info.fdFldr = 0;
326 fprintf( stderr, "Values read by hqx_header_read\n" );
327 fprintf( stderr, "name length\t\t%d\n", namelen );
328 fprintf( stderr, "file name\t\t%s\n", fh->name );
329 fprintf( stderr, "get info comment\t%s\n", fh->comment );
330 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
331 &fh->finder_info.fdType );
332 fprintf( stderr, "creator\t\t\t%.*s\n",
333 sizeof( fh->finder_info.fdCreator ),
334 &fh->finder_info.fdCreator );
335 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
336 flags = ntohs( flags );
337 fprintf( stderr, "flags\t\t\t%x\n", flags );
338 fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
339 fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
340 fprintf( stderr, "header_crc\t\t%x\n", header_crc );
341 fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
342 fprintf( stderr, "\n" );
347 * create and modify times are figured from right now
350 time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
351 memcpy( &fh->create_date, &time_seconds,
352 sizeof( fh->create_date ));
353 memcpy( &fh->mod_date, &time_seconds,
354 sizeof( fh->mod_date ));
355 fh->backup_date = AD_DATE_START;
358 * stuff that should be zero'ed out
361 fh->comment[0] = '\0';
362 memset( &fh->finder_info.fdLocation, 0,
363 sizeof( fh->finder_info.fdLocation ));
364 memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
366 hqx.forkcrc[ DATA ] = 0;
367 hqx.forkcrc[ RESOURCE ] = 0;
370 if ( header_crc != hqx.headercrc ) {
371 fprintf( stderr, "Bad Header crc, dude :" );
381 hqx_header_write( fh )
388 * hqx7_fill is called from skip_junk and hqx_7tobin. it pulls from the
389 * binhqx file into the hqx7 buffer. returns number of bytes read
390 * or a zero for end of file.
391 * it sets the pointers to the hqx7 buffer up to point to the valid data.
394 hqx7_fill( hqx7_ptr )
400 cs = hqx7_ptr - hqx7_buf;
401 if ( cs >= sizeof( hqx7_buf )) return( -1 );
402 hqx7_first = hqx7_ptr;
403 cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
408 hqx7_last = ( hqx7_first + cc );
413 char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
414 0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
416 Input characters are translated to a number between 0 and 63 by direct
417 array lookup. 0xFF signals a bad character. 0xFE is signals a legal
418 character that should be skipped, namely '\n', '\r'. 0xFD signals ':'.
419 0xFC signals a whitespace character.
422 u_char hqxlookup[] = {
423 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
424 0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
425 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
426 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
427 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
428 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
429 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
430 0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
431 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
432 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
433 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
434 0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
435 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
436 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
437 0x3D, 0x3E, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
438 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
439 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
440 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
441 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
442 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
443 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
444 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
445 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
446 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
447 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
448 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
449 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
450 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
451 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
452 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
453 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
454 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
458 * skip_junk is called from hqx_open. it skips over junk in the file until
459 * it comes to a line containing a valid first line of binhqx encoded file.
460 * returns a 0 for success, negative if it never finds good data.
461 * pass a FIRST when looking for the first valid binhex line, a value of
462 * OTHER when looking for any subsequent line.
474 if ( line == FIRST ) {
475 if ( hqx7_fill( hqx7_buf ) <= 0 ) {
476 fprintf( stderr, "Premature end of file :" );
481 while ( found == NOWAY ) {
482 if ( line == FIRST ) {
483 if ( *hqx7_first == ':' ) {
487 while (( stopflag == NOWAY ) &&
488 ( nc < ( hqx7_last - hqx7_first ))) {
489 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
494 stopflag = SURETHANG;
501 if (( nc > 30 ) && ( nc < 64 ) &&
502 (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
507 if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
511 while (( stopflag == NOWAY ) &&
512 ( nc < ( hqx7_last - hqx7_first ))) {
513 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
516 if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
522 stopflag = SURETHANG;
532 } else if (( nc > 30 ) && ( c == 0xFE )) {
540 if (( hqx7_last - hqx7_first ) == nc ) {
541 if ( line == FIRST ) {
543 } else *hqx7_buf = '\n';
544 memcpy(hqx7_buf + 1, hqx7_first, nc );
545 hqx7_first = hqx7_buf + ( ++nc );
546 if ( hqx7_fill( hqx7_first ) <= 0 ) {
547 fprintf( stderr, "Premature end of file :" );
550 hqx7_first = hqx7_buf;
558 * hqx_7tobin is used to read the data, converted to binary. It is
559 * called by hqx_header_read to get the header information, and must be
560 * called to get the data for each fork, and the crc data for each
561 * fork. it has the same basic calling structure as unix read. the
562 * number of valid bytes read is returned. It does buffering so as to
563 * return the requested length of data every time, unless the end of
567 hqx_7tobin( outbuf, datalen )
571 static u_char hqx8[3];
573 static u_char prev_hqx8;
574 static u_char prev_out;
575 static u_char prev_hqx7;
583 fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
584 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
587 if ( first_flag == 0 ) {
597 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
601 out_last = out_first + datalen;
603 while (( out_first < out_last ) && ( eofflag == 0 )) {
605 if ( hqx7_first == hqx7_last ) {
606 if ( hqx7_fill( hqx7_buf ) == 0 ) {
614 while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
615 hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
616 switch ( hqx7[ hqx7i ] ) {
618 if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
625 while ( hqx7i < 4 ) {
630 prev_hqx7 = hqx7[ hqx7i ];
631 if ( skip_junk( OTHER ) < 0 ) {
632 fprintf( stderr, "\n" );
634 while ( hqx7i < 4 ) {
635 hqx7[ hqx7i++ ] = 0; }
639 prev_hqx7 = hqx7[ hqx7i++ ];
646 hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
647 hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
648 hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
653 while (( hqx8i < 3 ) && ( out_first < out_last )) {
656 putc( hqx8i, rawhex );
657 putc( hqx8[ hqx8i ], rawhex );
660 if ( prev_hqx8 == RUNCHAR ) {
661 if ( hqx8[ hqx8i ] == 0 ) {
662 *out_first = prev_hqx8;
664 putc( *out_first, expandhex );
666 prev_out = prev_hqx8;
669 while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
670 *out_first = prev_out;
672 putc( *out_first, expandhex );
677 if ( hqx8[ hqx8i ] < 2 ) {
678 prev_hqx8 = hqx8[ hqx8i ];
684 prev_hqx8 = hqx8[ hqx8i ];
685 if ( prev_hqx8 != RUNCHAR ) {
686 *out_first = prev_hqx8;
688 putc( *out_first, expandhex );
690 prev_out = prev_hqx8;
698 return( out_first - outbuf );