2 * $Id: hqx.c,v 1.6 2001-05-01 13:58:43 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>
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
67 /* Forward declarations.
69 int skip_junk(int line);
70 int hqx_close(int keepflag);
71 int hqx_header_read(struct FHeader *fh);
72 int hqx_header_write(struct FHeader *fh);
73 int hqx_7tobin(char *outbuf, int datalen);
74 int hqx7_fill(u_char *hqx7_ptr);
77 FILE *rawhex, *expandhex;
80 struct hqx_file_data {
81 u_int32_t forklen[ NUMFORKS ];
82 u_short forkcrc[ NUMFORKS ];
83 char path[ MAXPATHLEN + 1];
88 extern char *forkname[];
89 u_char hqx7_buf[8192];
95 hqx_open must be called first. pass it a filename that is supposed
96 to contain a binhqx file. an hqx struct will be allocated and
97 somewhat initialized; hqx_fd is set. skip_junk is called from
98 here; skip_junk leaves hqx7_first and hqx7_last set.
101 int hqx_open( hqxfile, flags, fh, options )
109 fprintf( stderr, "megatron: entering hqx_open\n" );
111 if ( flags == O_RDONLY ) {
114 rawhex = fopen( "rawhex.unhex", "w" );
115 expandhex = fopen( "expandhex.unhex", "w" );
120 if ( strcmp( hqxfile, STDIN ) == 0 ) {
121 hqx.filed = fileno( stdin );
122 } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
127 if ( skip_junk( FIRST ) == 0 ) {
128 if ( hqx_header_read( fh ) == 0 ) {
132 pos = lseek( hqx.filed, 0, L_INCR );
133 fprintf( stderr, "megatron: current position is %ld\n", pos );
139 fprintf( stderr, "%s\n", hqxfile );
142 maxlen = sizeof( hqx.path ) -1;
143 strncpy( hqx.path, fh->name, maxlen );
144 strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
145 strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
146 if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
150 if ( hqx_header_write( fh ) != 0 ) {
152 fprintf( stderr, "%s\n", hqx.path );
160 * hqx_close must be called before a second file can be opened using
161 * hqx_open. Upon successful completion, a value of 0 is returned.
162 * Otherwise, a value of -1 is returned.
165 int hqx_close( keepflag )
168 if ( keepflag == KEEP ) {
169 return( close( hqx.filed ));
170 } else if ( keepflag == TRASH ) {
171 if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
179 * hqx_read is called until it returns zero for each fork. when it is
180 * and finds that there is zero left to give, it reads in and compares
181 * the crc with the calculated one, and returns zero if all is well.
182 * it returns negative is the crc was bad or if has been called too many
183 * times for the same fork. hqx_read must be called enough times to
184 * return zero and no more than that.
187 int hqx_read( fork, buffer, length )
199 pos = lseek( hqx.filed, 0, L_INCR );
200 fprintf( stderr, "hqx_read: current position is %ld\n", pos );
202 fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
203 fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
206 if ( hqx.forklen[ fork ] < 0 ) {
207 fprintf( stderr, "This should never happen, dude!\n" );
208 return( hqx.forklen[ fork ] );
211 if ( hqx.forklen[ fork ] == 0 ) {
212 cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
213 if ( cc == sizeof( storedcrc )) {
214 storedcrc = ntohs ( storedcrc );
216 fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
217 fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
219 if ( storedcrc == hqx.forkcrc[ fork ] ) {
222 fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
228 if ( hqx.forklen[ fork ] < length ) {
229 readlen = hqx.forklen[ fork ];
234 fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
237 cc = hqx_7tobin( buffer, readlen );
239 hqx.forkcrc[ fork ] =
240 updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
241 hqx.forklen[ fork ] -= cc;
244 fprintf( stderr, "hqx_read: chars read is %d\n", cc );
250 * hqx_header_read is called by hqx_open, and before any information can
251 * read from the hqx_header substruct. it must be called before any
252 * of the bytes of the other two forks can be read, as well.
253 * returns a negative number if it was unable to pull enough information
254 * to fill the hqx_header fields.
257 int hqx_header_read( fh )
260 char *headerbuf, *headerptr;
261 u_int32_t time_seconds;
268 headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
271 mask = htons( 0xfcee );
274 if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
275 fprintf( stderr, "Premature end of file :" );
278 hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen,
282 write( headerfork, &namelen, sizeof( namelen ));
286 (char *)malloc( (unsigned int)( namelen + BHH_HEADSIZ ))) == 0 ) {
289 if ( hqx_7tobin( headerbuf, ( namelen + BHH_HEADSIZ )) == 0 ) {
291 fprintf( stderr, "Premature end of file :" );
294 headerptr = headerbuf;
295 hqx.headercrc = updcrc( hqx.headercrc,
296 (u_char *)headerbuf, ( namelen + BHH_HEADSIZ - BHH_CRCSIZ ));
299 write( headerfork, headerbuf, ( namelen + BHH_HEADSIZ ));
303 * stuff from the hqx file header
306 memcpy( fh->name, headerptr, (int)namelen );
307 headerptr += namelen;
308 headerptr += BHH_VERSION;
309 memcpy(&fh->finder_info, headerptr, BHH_TCSIZ );
310 headerptr += BHH_TCSIZ;
311 memcpy(&fh->finder_info.fdFlags, headerptr, BHH_FLAGSIZ );
312 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
313 headerptr += BHH_FLAGSIZ;
314 memcpy(&fh->forklen[ DATA ], headerptr, BHH_DATASIZ );
315 hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
316 headerptr += BHH_DATASIZ;
317 memcpy( &fh->forklen[ RESOURCE ], headerptr, BHH_RESSIZ );
318 hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
319 headerptr += BHH_RESSIZ;
320 memcpy(&header_crc, headerptr, BHH_CRCSIZ );
321 headerptr += BHH_CRCSIZ;
322 header_crc = ntohs( header_crc );
325 * stuff that should be zero'ed out
328 fh->comment[0] = '\0';
329 fh->finder_info.fdLocation = 0;
330 fh->finder_info.fdFldr = 0;
336 fprintf( stderr, "Values read by hqx_header_read\n" );
337 fprintf( stderr, "name length\t\t%d\n", namelen );
338 fprintf( stderr, "file name\t\t%s\n", fh->name );
339 fprintf( stderr, "get info comment\t%s\n", fh->comment );
340 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
341 &fh->finder_info.fdType );
342 fprintf( stderr, "creator\t\t\t%.*s\n",
343 sizeof( fh->finder_info.fdCreator ),
344 &fh->finder_info.fdCreator );
345 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
346 flags = ntohs( flags );
347 fprintf( stderr, "flags\t\t\t%x\n", flags );
348 fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
349 fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
350 fprintf( stderr, "header_crc\t\t%x\n", header_crc );
351 fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
352 fprintf( stderr, "\n" );
357 * create and modify times are figured from right now
360 time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
361 memcpy( &fh->create_date, &time_seconds,
362 sizeof( fh->create_date ));
363 memcpy( &fh->mod_date, &time_seconds,
364 sizeof( fh->mod_date ));
365 fh->backup_date = AD_DATE_START;
368 * stuff that should be zero'ed out
371 fh->comment[0] = '\0';
372 memset( &fh->finder_info.fdLocation, 0,
373 sizeof( fh->finder_info.fdLocation ));
374 memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
376 hqx.forkcrc[ DATA ] = 0;
377 hqx.forkcrc[ RESOURCE ] = 0;
380 if ( header_crc != hqx.headercrc ) {
381 fprintf( stderr, "Bad Header crc, dude :" );
391 int hqx_header_write( fh )
398 * hqx7_fill is called from skip_junk and hqx_7tobin. it pulls from the
399 * binhqx file into the hqx7 buffer. returns number of bytes read
400 * or a zero for end of file.
401 * it sets the pointers to the hqx7 buffer up to point to the valid data.
404 int hqx7_fill( hqx7_ptr )
410 cs = hqx7_ptr - hqx7_buf;
411 if ( cs >= sizeof( hqx7_buf )) return( -1 );
412 hqx7_first = hqx7_ptr;
413 cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
418 hqx7_last = ( hqx7_first + cc );
423 char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
424 0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
426 Input characters are translated to a number between 0 and 63 by direct
427 array lookup. 0xFF signals a bad character. 0xFE is signals a legal
428 character that should be skipped, namely '\n', '\r'. 0xFD signals ':'.
429 0xFC signals a whitespace character.
432 u_char hqxlookup[] = {
433 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
434 0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
435 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
436 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
437 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
438 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
439 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
440 0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
441 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
442 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
443 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
444 0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
445 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
446 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
447 0x3D, 0x3E, 0x3F, 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,
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,
468 * skip_junk is called from hqx_open. it skips over junk in the file until
469 * it comes to a line containing a valid first line of binhqx encoded file.
470 * returns a 0 for success, negative if it never finds good data.
471 * pass a FIRST when looking for the first valid binhex line, a value of
472 * OTHER when looking for any subsequent line.
475 int skip_junk( line )
484 if ( line == FIRST ) {
485 if ( hqx7_fill( hqx7_buf ) <= 0 ) {
486 fprintf( stderr, "Premature end of file :" );
491 while ( found == NOWAY ) {
492 if ( line == FIRST ) {
493 if ( *hqx7_first == ':' ) {
497 while (( stopflag == NOWAY ) &&
498 ( nc < ( hqx7_last - hqx7_first ))) {
499 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
504 stopflag = SURETHANG;
511 if (( nc > 30 ) && ( nc < 64 ) &&
512 (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
517 if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
521 while (( stopflag == NOWAY ) &&
522 ( nc < ( hqx7_last - hqx7_first ))) {
523 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
526 if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
532 stopflag = SURETHANG;
542 } else if (( nc > 30 ) && ( c == 0xFE )) {
550 if (( hqx7_last - hqx7_first ) == nc ) {
551 if ( line == FIRST ) {
553 } else *hqx7_buf = '\n';
554 memcpy(hqx7_buf + 1, hqx7_first, nc );
555 hqx7_first = hqx7_buf + ( ++nc );
556 if ( hqx7_fill( hqx7_first ) <= 0 ) {
557 fprintf( stderr, "Premature end of file :" );
560 hqx7_first = hqx7_buf;
568 * hqx_7tobin is used to read the data, converted to binary. It is
569 * called by hqx_header_read to get the header information, and must be
570 * called to get the data for each fork, and the crc data for each
571 * fork. it has the same basic calling structure as unix read. the
572 * number of valid bytes read is returned. It does buffering so as to
573 * return the requested length of data every time, unless the end of
577 int hqx_7tobin( outbuf, datalen )
581 static u_char hqx8[3];
583 static u_char prev_hqx8;
584 static u_char prev_out;
585 static u_char prev_hqx7;
593 fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
594 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
597 if ( first_flag == 0 ) {
607 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
611 out_last = out_first + datalen;
613 while (( out_first < out_last ) && ( eofflag == 0 )) {
615 if ( hqx7_first == hqx7_last ) {
616 if ( hqx7_fill( hqx7_buf ) == 0 ) {
624 while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
625 hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
626 switch ( hqx7[ hqx7i ] ) {
628 if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
635 while ( hqx7i < 4 ) {
640 prev_hqx7 = hqx7[ hqx7i ];
641 if ( skip_junk( OTHER ) < 0 ) {
642 fprintf( stderr, "\n" );
644 while ( hqx7i < 4 ) {
645 hqx7[ hqx7i++ ] = 0; }
649 prev_hqx7 = hqx7[ hqx7i++ ];
656 hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
657 hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
658 hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
663 while (( hqx8i < 3 ) && ( out_first < out_last )) {
666 putc( hqx8i, rawhex );
667 putc( hqx8[ hqx8i ], rawhex );
670 if ( prev_hqx8 == RUNCHAR ) {
671 if ( hqx8[ hqx8i ] == 0 ) {
672 *out_first = prev_hqx8;
674 putc( *out_first, expandhex );
676 prev_out = prev_hqx8;
679 while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
680 *out_first = prev_out;
682 putc( *out_first, expandhex );
687 if ( hqx8[ hqx8i ] < 2 ) {
688 prev_hqx8 = hqx8[ hqx8i ];
694 prev_hqx8 = hqx8[ hqx8i ];
695 if ( prev_hqx8 != RUNCHAR ) {
696 *out_first = prev_hqx8;
698 putc( *out_first, expandhex );
700 prev_out = prev_hqx8;
708 return( out_first - outbuf );