2 * $Id: hqx.c,v 1.5 2001-05-01 13:21:22 rufustfirefly Exp $
10 #include <sys/types.h>
13 #include <sys/param.h>
16 # include <machine/endian.h>
18 # include <netinet/in.h>
27 #include <atalk/adouble.h>
28 #include <netatalk/endian.h>
33 /* String used to indicate standard input instead of a disk
34 file. Should be a string not normally used for a file
45 /* Looking for the first or any other line of a binhex file
50 /* This is the binhex run length encoding character
54 /* These are field sizes in bytes of various pieces of the
63 #define BHH_HEADSIZ 21
68 FILE *rawhex, *expandhex;
71 struct hqx_file_data {
72 u_int32_t forklen[ NUMFORKS ];
73 u_short forkcrc[ NUMFORKS ];
74 char path[ MAXPATHLEN + 1];
79 extern char *forkname[];
80 u_char hqx7_buf[8192];
86 hqx_open must be called first. pass it a filename that is supposed
87 to contain a binhqx file. an hqx struct will be allocated and
88 somewhat initialized; hqx_fd is set. skip_junk is called from
89 here; skip_junk leaves hqx7_first and hqx7_last set.
92 int hqx_open( hqxfile, flags, fh, options )
100 fprintf( stderr, "megatron: entering hqx_open\n" );
102 if ( flags == O_RDONLY ) {
105 rawhex = fopen( "rawhex.unhex", "w" );
106 expandhex = fopen( "expandhex.unhex", "w" );
111 if ( strcmp( hqxfile, STDIN ) == 0 ) {
112 hqx.filed = fileno( stdin );
113 } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
118 if ( skip_junk( FIRST ) == 0 ) {
119 if ( hqx_header_read( fh ) == 0 ) {
123 pos = lseek( hqx.filed, 0, L_INCR );
124 fprintf( stderr, "megatron: current position is %ld\n", pos );
130 fprintf( stderr, "%s\n", hqxfile );
133 maxlen = sizeof( hqx.path ) -1;
134 strncpy( hqx.path, fh->name, maxlen );
135 strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
136 strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
137 if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
141 if ( hqx_header_write( fh ) != 0 ) {
143 fprintf( stderr, "%s\n", hqx.path );
151 * hqx_close must be called before a second file can be opened using
152 * hqx_open. Upon successful completion, a value of 0 is returned.
153 * Otherwise, a value of -1 is returned.
156 int hqx_close( keepflag )
159 if ( keepflag == KEEP ) {
160 return( close( hqx.filed ));
161 } else if ( keepflag == TRASH ) {
162 if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
170 * hqx_read is called until it returns zero for each fork. when it is
171 * and finds that there is zero left to give, it reads in and compares
172 * the crc with the calculated one, and returns zero if all is well.
173 * it returns negative is the crc was bad or if has been called too many
174 * times for the same fork. hqx_read must be called enough times to
175 * return zero and no more than that.
178 int hqx_read( fork, buffer, length )
190 pos = lseek( hqx.filed, 0, L_INCR );
191 fprintf( stderr, "hqx_read: current position is %ld\n", pos );
193 fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
194 fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
197 if ( hqx.forklen[ fork ] < 0 ) {
198 fprintf( stderr, "This should never happen, dude!\n" );
199 return( hqx.forklen[ fork ] );
202 if ( hqx.forklen[ fork ] == 0 ) {
203 cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
204 if ( cc == sizeof( storedcrc )) {
205 storedcrc = ntohs ( storedcrc );
207 fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
208 fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
210 if ( storedcrc == hqx.forkcrc[ fork ] ) {
213 fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
219 if ( hqx.forklen[ fork ] < length ) {
220 readlen = hqx.forklen[ fork ];
225 fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
228 cc = hqx_7tobin( buffer, readlen );
230 hqx.forkcrc[ fork ] =
231 updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
232 hqx.forklen[ fork ] -= cc;
235 fprintf( stderr, "hqx_read: chars read is %d\n", cc );
241 * hqx_header_read is called by hqx_open, and before any information can
242 * read from the hqx_header substruct. it must be called before any
243 * of the bytes of the other two forks can be read, as well.
244 * returns a negative number if it was unable to pull enough information
245 * to fill the hqx_header fields.
248 int hqx_header_read( fh )
251 char *headerbuf, *headerptr;
252 u_int32_t time_seconds;
259 headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
262 mask = htons( 0xfcee );
265 if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
266 fprintf( stderr, "Premature end of file :" );
269 hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen,
273 write( headerfork, &namelen, sizeof( namelen ));
277 (char *)malloc( (unsigned int)( namelen + BHH_HEADSIZ ))) == 0 ) {
280 if ( hqx_7tobin( headerbuf, ( namelen + BHH_HEADSIZ )) == 0 ) {
282 fprintf( stderr, "Premature end of file :" );
285 headerptr = headerbuf;
286 hqx.headercrc = updcrc( hqx.headercrc,
287 (u_char *)headerbuf, ( namelen + BHH_HEADSIZ - BHH_CRCSIZ ));
290 write( headerfork, headerbuf, ( namelen + BHH_HEADSIZ ));
294 * stuff from the hqx file header
297 memcpy( fh->name, headerptr, (int)namelen );
298 headerptr += namelen;
299 headerptr += BVERSION;
300 memcpy(&fh->finder_info, headerptr, TCSIZ );
302 memcpy(&fh->finder_info.fdFlags, headerptr, FLAGSIZ );
303 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
304 headerptr += BHH_FLAGSIZ;
305 memcpy(&fh->forklen[ DATA ], headerptr, BHH_DATASIZ );
306 hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
307 headerptr += BHH_DATASIZ;
308 memcpy( &fh->forklen[ RESOURCE ], headerptr, BHH_RESSIZ );
309 hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
310 headerptr += BHH_RESSIZ;
311 memcpy(&header_crc, headerptr, BHH_CRCSIZ );
312 headerptr += BHH_CRCSIZ;
313 header_crc = ntohs( header_crc );
316 * stuff that should be zero'ed out
319 fh->comment[0] = '\0';
320 fh->finder_info.fdLocation = 0;
321 fh->finder_info.fdFldr = 0;
327 fprintf( stderr, "Values read by hqx_header_read\n" );
328 fprintf( stderr, "name length\t\t%d\n", namelen );
329 fprintf( stderr, "file name\t\t%s\n", fh->name );
330 fprintf( stderr, "get info comment\t%s\n", fh->comment );
331 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
332 &fh->finder_info.fdType );
333 fprintf( stderr, "creator\t\t\t%.*s\n",
334 sizeof( fh->finder_info.fdCreator ),
335 &fh->finder_info.fdCreator );
336 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
337 flags = ntohs( flags );
338 fprintf( stderr, "flags\t\t\t%x\n", flags );
339 fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
340 fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
341 fprintf( stderr, "header_crc\t\t%x\n", header_crc );
342 fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
343 fprintf( stderr, "\n" );
348 * create and modify times are figured from right now
351 time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
352 memcpy( &fh->create_date, &time_seconds,
353 sizeof( fh->create_date ));
354 memcpy( &fh->mod_date, &time_seconds,
355 sizeof( fh->mod_date ));
356 fh->backup_date = AD_DATE_START;
359 * stuff that should be zero'ed out
362 fh->comment[0] = '\0';
363 memset( &fh->finder_info.fdLocation, 0,
364 sizeof( fh->finder_info.fdLocation ));
365 memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
367 hqx.forkcrc[ DATA ] = 0;
368 hqx.forkcrc[ RESOURCE ] = 0;
371 if ( header_crc != hqx.headercrc ) {
372 fprintf( stderr, "Bad Header crc, dude :" );
382 int hqx_header_write( fh )
389 * hqx7_fill is called from skip_junk and hqx_7tobin. it pulls from the
390 * binhqx file into the hqx7 buffer. returns number of bytes read
391 * or a zero for end of file.
392 * it sets the pointers to the hqx7 buffer up to point to the valid data.
395 int hqx7_fill( hqx7_ptr )
401 cs = hqx7_ptr - hqx7_buf;
402 if ( cs >= sizeof( hqx7_buf )) return( -1 );
403 hqx7_first = hqx7_ptr;
404 cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
409 hqx7_last = ( hqx7_first + cc );
414 char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
415 0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
417 Input characters are translated to a number between 0 and 63 by direct
418 array lookup. 0xFF signals a bad character. 0xFE is signals a legal
419 character that should be skipped, namely '\n', '\r'. 0xFD signals ':'.
420 0xFC signals a whitespace character.
423 u_char hqxlookup[] = {
424 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
425 0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
426 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
427 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
428 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
429 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
430 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
431 0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
432 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
433 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
434 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
435 0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
436 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
437 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
438 0x3D, 0x3E, 0x3F, 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,
455 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
459 * skip_junk is called from hqx_open. it skips over junk in the file until
460 * it comes to a line containing a valid first line of binhqx encoded file.
461 * returns a 0 for success, negative if it never finds good data.
462 * pass a FIRST when looking for the first valid binhex line, a value of
463 * OTHER when looking for any subsequent line.
466 int skip_junk( line )
475 if ( line == FIRST ) {
476 if ( hqx7_fill( hqx7_buf ) <= 0 ) {
477 fprintf( stderr, "Premature end of file :" );
482 while ( found == NOWAY ) {
483 if ( line == FIRST ) {
484 if ( *hqx7_first == ':' ) {
488 while (( stopflag == NOWAY ) &&
489 ( nc < ( hqx7_last - hqx7_first ))) {
490 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
495 stopflag = SURETHANG;
502 if (( nc > 30 ) && ( nc < 64 ) &&
503 (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
508 if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
512 while (( stopflag == NOWAY ) &&
513 ( nc < ( hqx7_last - hqx7_first ))) {
514 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
517 if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
523 stopflag = SURETHANG;
533 } else if (( nc > 30 ) && ( c == 0xFE )) {
541 if (( hqx7_last - hqx7_first ) == nc ) {
542 if ( line == FIRST ) {
544 } else *hqx7_buf = '\n';
545 memcpy(hqx7_buf + 1, hqx7_first, nc );
546 hqx7_first = hqx7_buf + ( ++nc );
547 if ( hqx7_fill( hqx7_first ) <= 0 ) {
548 fprintf( stderr, "Premature end of file :" );
551 hqx7_first = hqx7_buf;
559 * hqx_7tobin is used to read the data, converted to binary. It is
560 * called by hqx_header_read to get the header information, and must be
561 * called to get the data for each fork, and the crc data for each
562 * fork. it has the same basic calling structure as unix read. the
563 * number of valid bytes read is returned. It does buffering so as to
564 * return the requested length of data every time, unless the end of
568 int hqx_7tobin( outbuf, datalen )
572 static u_char hqx8[3];
574 static u_char prev_hqx8;
575 static u_char prev_out;
576 static u_char prev_hqx7;
584 fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
585 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
588 if ( first_flag == 0 ) {
598 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
602 out_last = out_first + datalen;
604 while (( out_first < out_last ) && ( eofflag == 0 )) {
606 if ( hqx7_first == hqx7_last ) {
607 if ( hqx7_fill( hqx7_buf ) == 0 ) {
615 while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
616 hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
617 switch ( hqx7[ hqx7i ] ) {
619 if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
626 while ( hqx7i < 4 ) {
631 prev_hqx7 = hqx7[ hqx7i ];
632 if ( skip_junk( OTHER ) < 0 ) {
633 fprintf( stderr, "\n" );
635 while ( hqx7i < 4 ) {
636 hqx7[ hqx7i++ ] = 0; }
640 prev_hqx7 = hqx7[ hqx7i++ ];
647 hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
648 hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
649 hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
654 while (( hqx8i < 3 ) && ( out_first < out_last )) {
657 putc( hqx8i, rawhex );
658 putc( hqx8[ hqx8i ], rawhex );
661 if ( prev_hqx8 == RUNCHAR ) {
662 if ( hqx8[ hqx8i ] == 0 ) {
663 *out_first = prev_hqx8;
665 putc( *out_first, expandhex );
667 prev_out = prev_hqx8;
670 while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
671 *out_first = prev_out;
673 putc( *out_first, expandhex );
678 if ( hqx8[ hqx8i ] < 2 ) {
679 prev_hqx8 = hqx8[ hqx8i ];
685 prev_hqx8 = hqx8[ hqx8i ];
686 if ( prev_hqx8 != RUNCHAR ) {
687 *out_first = prev_hqx8;
689 putc( *out_first, expandhex );
691 prev_out = prev_hqx8;
699 return( out_first - outbuf );