2 * $Id: hqx.c,v 1.7 2001-06-10 20:12:29 srittau 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>
34 /* String used to indicate standard input instead of a disk
35 file. Should be a string not normally used for a file
46 /* Looking for the first or any other line of a binhex file
51 /* This is the binhex run length encoding character
55 /* These are field sizes in bytes of various pieces of the
64 #define BHH_HEADSIZ 21
68 /* Forward declarations.
70 int skip_junk(int line);
71 int hqx_close(int keepflag);
72 int hqx_header_read(struct FHeader *fh);
73 int hqx_header_write(struct FHeader *fh);
74 int hqx_7tobin(char *outbuf, int datalen);
75 int hqx7_fill(u_char *hqx7_ptr);
78 FILE *rawhex, *expandhex;
81 struct hqx_file_data {
82 u_int32_t forklen[ NUMFORKS ];
83 u_short forkcrc[ NUMFORKS ];
84 char path[ MAXPATHLEN + 1];
89 extern char *forkname[];
90 u_char hqx7_buf[8192];
96 hqx_open must be called first. pass it a filename that is supposed
97 to contain a binhqx file. an hqx struct will be allocated and
98 somewhat initialized; hqx_fd is set. skip_junk is called from
99 here; skip_junk leaves hqx7_first and hqx7_last set.
102 int hqx_open( hqxfile, flags, fh, options )
110 fprintf( stderr, "megatron: entering hqx_open\n" );
112 if ( flags == O_RDONLY ) {
115 rawhex = fopen( "rawhex.unhex", "w" );
116 expandhex = fopen( "expandhex.unhex", "w" );
121 if ( strcmp( hqxfile, STDIN ) == 0 ) {
122 hqx.filed = fileno( stdin );
123 } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
128 if ( skip_junk( FIRST ) == 0 ) {
129 if ( hqx_header_read( fh ) == 0 ) {
133 pos = lseek( hqx.filed, 0, L_INCR );
134 fprintf( stderr, "megatron: current position is %ld\n", pos );
140 fprintf( stderr, "%s\n", hqxfile );
143 maxlen = sizeof( hqx.path ) -1;
144 strncpy( hqx.path, fh->name, maxlen );
145 strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
146 strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
147 if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
151 if ( hqx_header_write( fh ) != 0 ) {
153 fprintf( stderr, "%s\n", hqx.path );
161 * hqx_close must be called before a second file can be opened using
162 * hqx_open. Upon successful completion, a value of 0 is returned.
163 * Otherwise, a value of -1 is returned.
166 int hqx_close( keepflag )
169 if ( keepflag == KEEP ) {
170 return( close( hqx.filed ));
171 } else if ( keepflag == TRASH ) {
172 if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
180 * hqx_read is called until it returns zero for each fork. when it is
181 * and finds that there is zero left to give, it reads in and compares
182 * the crc with the calculated one, and returns zero if all is well.
183 * it returns negative is the crc was bad or if has been called too many
184 * times for the same fork. hqx_read must be called enough times to
185 * return zero and no more than that.
188 int hqx_read( fork, buffer, length )
200 pos = lseek( hqx.filed, 0, L_INCR );
201 fprintf( stderr, "hqx_read: current position is %ld\n", pos );
203 fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
204 fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
207 if ( hqx.forklen[ fork ] < 0 ) {
208 fprintf( stderr, "This should never happen, dude!\n" );
209 return( hqx.forklen[ fork ] );
212 if ( hqx.forklen[ fork ] == 0 ) {
213 cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
214 if ( cc == sizeof( storedcrc )) {
215 storedcrc = ntohs ( storedcrc );
217 fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
218 fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
220 if ( storedcrc == hqx.forkcrc[ fork ] ) {
223 fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
229 if ( hqx.forklen[ fork ] < length ) {
230 readlen = hqx.forklen[ fork ];
235 fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
238 cc = hqx_7tobin( buffer, readlen );
240 hqx.forkcrc[ fork ] =
241 updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
242 hqx.forklen[ fork ] -= cc;
245 fprintf( stderr, "hqx_read: chars read is %d\n", cc );
251 * hqx_header_read is called by hqx_open, and before any information can
252 * read from the hqx_header substruct. it must be called before any
253 * of the bytes of the other two forks can be read, as well.
254 * returns a negative number if it was unable to pull enough information
255 * to fill the hqx_header fields.
258 int hqx_header_read( fh )
261 char *headerbuf, *headerptr;
262 u_int32_t time_seconds;
269 headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
272 mask = htons( 0xfcee );
275 if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
276 fprintf( stderr, "Premature end of file :" );
279 hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen,
283 write( headerfork, &namelen, sizeof( namelen ));
287 (char *)malloc( (unsigned int)( namelen + BHH_HEADSIZ ))) == 0 ) {
290 if ( hqx_7tobin( headerbuf, ( namelen + BHH_HEADSIZ )) == 0 ) {
292 fprintf( stderr, "Premature end of file :" );
295 headerptr = headerbuf;
296 hqx.headercrc = updcrc( hqx.headercrc,
297 (u_char *)headerbuf, ( namelen + BHH_HEADSIZ - BHH_CRCSIZ ));
300 write( headerfork, headerbuf, ( namelen + BHH_HEADSIZ ));
304 * stuff from the hqx file header
307 memcpy( fh->name, headerptr, (int)namelen );
308 headerptr += namelen;
309 headerptr += BHH_VERSION;
310 memcpy(&fh->finder_info, headerptr, BHH_TCSIZ );
311 headerptr += BHH_TCSIZ;
312 memcpy(&fh->finder_info.fdFlags, headerptr, BHH_FLAGSIZ );
313 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
314 headerptr += BHH_FLAGSIZ;
315 memcpy(&fh->forklen[ DATA ], headerptr, BHH_DATASIZ );
316 hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
317 headerptr += BHH_DATASIZ;
318 memcpy( &fh->forklen[ RESOURCE ], headerptr, BHH_RESSIZ );
319 hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
320 headerptr += BHH_RESSIZ;
321 memcpy(&header_crc, headerptr, BHH_CRCSIZ );
322 headerptr += BHH_CRCSIZ;
323 header_crc = ntohs( header_crc );
326 * stuff that should be zero'ed out
329 fh->comment[0] = '\0';
330 fh->finder_info.fdLocation = 0;
331 fh->finder_info.fdFldr = 0;
337 fprintf( stderr, "Values read by hqx_header_read\n" );
338 fprintf( stderr, "name length\t\t%d\n", namelen );
339 fprintf( stderr, "file name\t\t%s\n", fh->name );
340 fprintf( stderr, "get info comment\t%s\n", fh->comment );
341 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
342 &fh->finder_info.fdType );
343 fprintf( stderr, "creator\t\t\t%.*s\n",
344 sizeof( fh->finder_info.fdCreator ),
345 &fh->finder_info.fdCreator );
346 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
347 flags = ntohs( flags );
348 fprintf( stderr, "flags\t\t\t%x\n", flags );
349 fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
350 fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
351 fprintf( stderr, "header_crc\t\t%x\n", header_crc );
352 fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
353 fprintf( stderr, "\n" );
358 * create and modify times are figured from right now
361 time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
362 memcpy( &fh->create_date, &time_seconds,
363 sizeof( fh->create_date ));
364 memcpy( &fh->mod_date, &time_seconds,
365 sizeof( fh->mod_date ));
366 fh->backup_date = AD_DATE_START;
369 * stuff that should be zero'ed out
372 fh->comment[0] = '\0';
373 memset( &fh->finder_info.fdLocation, 0,
374 sizeof( fh->finder_info.fdLocation ));
375 memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
377 hqx.forkcrc[ DATA ] = 0;
378 hqx.forkcrc[ RESOURCE ] = 0;
381 if ( header_crc != hqx.headercrc ) {
382 fprintf( stderr, "Bad Header crc, dude :" );
392 int hqx_header_write( fh )
399 * hqx7_fill is called from skip_junk and hqx_7tobin. it pulls from the
400 * binhqx file into the hqx7 buffer. returns number of bytes read
401 * or a zero for end of file.
402 * it sets the pointers to the hqx7 buffer up to point to the valid data.
405 int hqx7_fill( hqx7_ptr )
411 cs = hqx7_ptr - hqx7_buf;
412 if ( cs >= sizeof( hqx7_buf )) return( -1 );
413 hqx7_first = hqx7_ptr;
414 cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
419 hqx7_last = ( hqx7_first + cc );
424 char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
425 0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
427 Input characters are translated to a number between 0 and 63 by direct
428 array lookup. 0xFF signals a bad character. 0xFE is signals a legal
429 character that should be skipped, namely '\n', '\r'. 0xFD signals ':'.
430 0xFC signals a whitespace character.
433 u_char hqxlookup[] = {
434 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
435 0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
436 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
437 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
438 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
439 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
440 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
441 0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
442 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
443 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
444 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
445 0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
446 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
447 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
448 0x3D, 0x3E, 0x3F, 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,
456 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
457 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
458 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
459 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
460 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
461 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
462 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
463 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
464 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
465 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
469 * skip_junk is called from hqx_open. it skips over junk in the file until
470 * it comes to a line containing a valid first line of binhqx encoded file.
471 * returns a 0 for success, negative if it never finds good data.
472 * pass a FIRST when looking for the first valid binhex line, a value of
473 * OTHER when looking for any subsequent line.
476 int skip_junk( line )
485 if ( line == FIRST ) {
486 if ( hqx7_fill( hqx7_buf ) <= 0 ) {
487 fprintf( stderr, "Premature end of file :" );
492 while ( found == NOWAY ) {
493 if ( line == FIRST ) {
494 if ( *hqx7_first == ':' ) {
498 while (( stopflag == NOWAY ) &&
499 ( nc < ( hqx7_last - hqx7_first ))) {
500 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
505 stopflag = SURETHANG;
512 if (( nc > 30 ) && ( nc < 64 ) &&
513 (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
518 if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
522 while (( stopflag == NOWAY ) &&
523 ( nc < ( hqx7_last - hqx7_first ))) {
524 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
527 if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
533 stopflag = SURETHANG;
543 } else if (( nc > 30 ) && ( c == 0xFE )) {
551 if (( hqx7_last - hqx7_first ) == nc ) {
552 if ( line == FIRST ) {
554 } else *hqx7_buf = '\n';
555 memcpy(hqx7_buf + 1, hqx7_first, nc );
556 hqx7_first = hqx7_buf + ( ++nc );
557 if ( hqx7_fill( hqx7_first ) <= 0 ) {
558 fprintf( stderr, "Premature end of file :" );
561 hqx7_first = hqx7_buf;
569 * hqx_7tobin is used to read the data, converted to binary. It is
570 * called by hqx_header_read to get the header information, and must be
571 * called to get the data for each fork, and the crc data for each
572 * fork. it has the same basic calling structure as unix read. the
573 * number of valid bytes read is returned. It does buffering so as to
574 * return the requested length of data every time, unless the end of
578 int hqx_7tobin( outbuf, datalen )
582 static u_char hqx8[3];
584 static u_char prev_hqx8;
585 static u_char prev_out;
586 static u_char prev_hqx7;
594 fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
595 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
598 if ( first_flag == 0 ) {
608 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
612 out_last = out_first + datalen;
614 while (( out_first < out_last ) && ( eofflag == 0 )) {
616 if ( hqx7_first == hqx7_last ) {
617 if ( hqx7_fill( hqx7_buf ) == 0 ) {
625 while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
626 hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
627 switch ( hqx7[ hqx7i ] ) {
629 if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
636 while ( hqx7i < 4 ) {
641 prev_hqx7 = hqx7[ hqx7i ];
642 if ( skip_junk( OTHER ) < 0 ) {
643 fprintf( stderr, "\n" );
645 while ( hqx7i < 4 ) {
646 hqx7[ hqx7i++ ] = 0; }
650 prev_hqx7 = hqx7[ hqx7i++ ];
657 hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
658 hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
659 hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
664 while (( hqx8i < 3 ) && ( out_first < out_last )) {
667 putc( hqx8i, rawhex );
668 putc( hqx8[ hqx8i ], rawhex );
671 if ( prev_hqx8 == RUNCHAR ) {
672 if ( hqx8[ hqx8i ] == 0 ) {
673 *out_first = prev_hqx8;
675 putc( *out_first, expandhex );
677 prev_out = prev_hqx8;
680 while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
681 *out_first = prev_out;
683 putc( *out_first, expandhex );
688 if ( hqx8[ hqx8i ] < 2 ) {
689 prev_hqx8 = hqx8[ hqx8i ];
695 prev_hqx8 = hqx8[ hqx8i ];
696 if ( prev_hqx8 != RUNCHAR ) {
697 *out_first = prev_hqx8;
699 putc( *out_first, expandhex );
701 prev_out = prev_hqx8;
709 return( out_first - outbuf );