2 * $Id: hqx.c,v 1.8 2001-06-29 14:14:46 rufustfirefly Exp $
7 #endif /* HAVE_CONFIG_H */
10 #include <sys/types.h>
13 #include <sys/param.h>
16 # include <machine/endian.h>
17 #else /* BSD >= 199006 */
18 # include <netinet/in.h>
19 #endif /* BSD >= 199006 */
24 #endif /* HAVE_FCNTL_H */
29 #include <atalk/adouble.h>
30 #include <netatalk/endian.h>
36 /* String used to indicate standard input instead of a disk
37 file. Should be a string not normally used for a file
48 /* Looking for the first or any other line of a binhex file
53 /* This is the binhex run length encoding character
57 /* These are field sizes in bytes of various pieces of the
66 #define BHH_HEADSIZ 21
70 /* Forward declarations.
72 int skip_junk(int line);
73 int hqx_close(int keepflag);
74 int hqx_header_read(struct FHeader *fh);
75 int hqx_header_write(struct FHeader *fh);
76 int hqx_7tobin(char *outbuf, int datalen);
77 int hqx7_fill(u_char *hqx7_ptr);
80 FILE *rawhex, *expandhex;
81 #endif /* HEXOUTPUT */
83 struct hqx_file_data {
84 u_int32_t forklen[ NUMFORKS ];
85 u_short forkcrc[ NUMFORKS ];
86 char path[ MAXPATHLEN + 1];
91 extern char *forkname[];
92 u_char hqx7_buf[8192];
98 hqx_open must be called first. pass it a filename that is supposed
99 to contain a binhqx file. an hqx struct will be allocated and
100 somewhat initialized; hqx_fd is set. skip_junk is called from
101 here; skip_junk leaves hqx7_first and hqx7_last set.
104 int hqx_open( hqxfile, flags, fh, options )
112 fprintf( stderr, "megatron: entering hqx_open\n" );
114 if ( flags == O_RDONLY ) {
117 rawhex = fopen( "rawhex.unhex", "w" );
118 expandhex = fopen( "expandhex.unhex", "w" );
119 #endif /* HEXOUTPUT */
123 if ( strcmp( hqxfile, STDIN ) == 0 ) {
124 hqx.filed = fileno( stdin );
125 } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
130 if ( skip_junk( FIRST ) == 0 ) {
131 if ( hqx_header_read( fh ) == 0 ) {
135 pos = lseek( hqx.filed, 0, L_INCR );
136 fprintf( stderr, "megatron: current position is %ld\n", pos );
142 fprintf( stderr, "%s\n", hqxfile );
145 maxlen = sizeof( hqx.path ) -1;
146 strncpy( hqx.path, fh->name, maxlen );
147 strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
148 strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
149 if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
153 if ( hqx_header_write( fh ) != 0 ) {
155 fprintf( stderr, "%s\n", hqx.path );
163 * hqx_close must be called before a second file can be opened using
164 * hqx_open. Upon successful completion, a value of 0 is returned.
165 * Otherwise, a value of -1 is returned.
168 int hqx_close( keepflag )
171 if ( keepflag == KEEP ) {
172 return( close( hqx.filed ));
173 } else if ( keepflag == TRASH ) {
174 if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
182 * hqx_read is called until it returns zero for each fork. when it is
183 * and finds that there is zero left to give, it reads in and compares
184 * the crc with the calculated one, and returns zero if all is well.
185 * it returns negative is the crc was bad or if has been called too many
186 * times for the same fork. hqx_read must be called enough times to
187 * return zero and no more than that.
190 int hqx_read( fork, buffer, length )
202 pos = lseek( hqx.filed, 0, L_INCR );
203 fprintf( stderr, "hqx_read: current position is %ld\n", pos );
205 fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
206 fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
207 #endif /* DEBUG >= 3 */
209 if ( hqx.forklen[ fork ] < 0 ) {
210 fprintf( stderr, "This should never happen, dude!\n" );
211 return( hqx.forklen[ fork ] );
214 if ( hqx.forklen[ fork ] == 0 ) {
215 cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
216 if ( cc == sizeof( storedcrc )) {
217 storedcrc = ntohs ( storedcrc );
219 fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
220 fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
221 #endif /* DEBUG >= 4 */
222 if ( storedcrc == hqx.forkcrc[ fork ] ) {
225 fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
231 if ( hqx.forklen[ fork ] < length ) {
232 readlen = hqx.forklen[ fork ];
237 fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
238 #endif /* DEBUG >= 3 */
240 cc = hqx_7tobin( buffer, readlen );
242 hqx.forkcrc[ fork ] =
243 updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
244 hqx.forklen[ fork ] -= cc;
247 fprintf( stderr, "hqx_read: chars read is %d\n", cc );
248 #endif /* DEBUG >= 3 */
253 * hqx_header_read is called by hqx_open, and before any information can
254 * read from the hqx_header substruct. it must be called before any
255 * of the bytes of the other two forks can be read, as well.
256 * returns a negative number if it was unable to pull enough information
257 * to fill the hqx_header fields.
260 int hqx_header_read( fh )
263 char *headerbuf, *headerptr;
264 u_int32_t time_seconds;
271 headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
272 #endif /* HEXOUTPUT */
274 mask = htons( 0xfcee );
277 if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
278 fprintf( stderr, "Premature end of file :" );
281 hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen,
285 write( headerfork, &namelen, sizeof( namelen ));
286 #endif /* HEXOUTPUT */
289 (char *)malloc( (unsigned int)( namelen + BHH_HEADSIZ ))) == 0 ) {
292 if ( hqx_7tobin( headerbuf, ( namelen + BHH_HEADSIZ )) == 0 ) {
294 fprintf( stderr, "Premature end of file :" );
297 headerptr = headerbuf;
298 hqx.headercrc = updcrc( hqx.headercrc,
299 (u_char *)headerbuf, ( namelen + BHH_HEADSIZ - BHH_CRCSIZ ));
302 write( headerfork, headerbuf, ( namelen + BHH_HEADSIZ ));
303 #endif /* HEXOUTPUT */
306 * stuff from the hqx file header
309 memcpy( fh->name, headerptr, (int)namelen );
310 headerptr += namelen;
311 headerptr += BHH_VERSION;
312 memcpy(&fh->finder_info, headerptr, BHH_TCSIZ );
313 headerptr += BHH_TCSIZ;
314 memcpy(&fh->finder_info.fdFlags, headerptr, BHH_FLAGSIZ );
315 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
316 headerptr += BHH_FLAGSIZ;
317 memcpy(&fh->forklen[ DATA ], headerptr, BHH_DATASIZ );
318 hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
319 headerptr += BHH_DATASIZ;
320 memcpy( &fh->forklen[ RESOURCE ], headerptr, BHH_RESSIZ );
321 hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
322 headerptr += BHH_RESSIZ;
323 memcpy(&header_crc, headerptr, BHH_CRCSIZ );
324 headerptr += BHH_CRCSIZ;
325 header_crc = ntohs( header_crc );
328 * stuff that should be zero'ed out
331 fh->comment[0] = '\0';
332 fh->finder_info.fdLocation = 0;
333 fh->finder_info.fdFldr = 0;
339 fprintf( stderr, "Values read by hqx_header_read\n" );
340 fprintf( stderr, "name length\t\t%d\n", namelen );
341 fprintf( stderr, "file name\t\t%s\n", fh->name );
342 fprintf( stderr, "get info comment\t%s\n", fh->comment );
343 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
344 &fh->finder_info.fdType );
345 fprintf( stderr, "creator\t\t\t%.*s\n",
346 sizeof( fh->finder_info.fdCreator ),
347 &fh->finder_info.fdCreator );
348 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
349 flags = ntohs( flags );
350 fprintf( stderr, "flags\t\t\t%x\n", flags );
351 fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
352 fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
353 fprintf( stderr, "header_crc\t\t%x\n", header_crc );
354 fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
355 fprintf( stderr, "\n" );
357 #endif /* DEBUG >= 5 */
360 * create and modify times are figured from right now
363 time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
364 memcpy( &fh->create_date, &time_seconds,
365 sizeof( fh->create_date ));
366 memcpy( &fh->mod_date, &time_seconds,
367 sizeof( fh->mod_date ));
368 fh->backup_date = AD_DATE_START;
371 * stuff that should be zero'ed out
374 fh->comment[0] = '\0';
375 memset( &fh->finder_info.fdLocation, 0,
376 sizeof( fh->finder_info.fdLocation ));
377 memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
379 hqx.forkcrc[ DATA ] = 0;
380 hqx.forkcrc[ RESOURCE ] = 0;
383 if ( header_crc != hqx.headercrc ) {
384 fprintf( stderr, "Bad Header crc, dude :" );
394 int hqx_header_write( fh )
401 * hqx7_fill is called from skip_junk and hqx_7tobin. it pulls from the
402 * binhqx file into the hqx7 buffer. returns number of bytes read
403 * or a zero for end of file.
404 * it sets the pointers to the hqx7 buffer up to point to the valid data.
407 int hqx7_fill( hqx7_ptr )
413 cs = hqx7_ptr - hqx7_buf;
414 if ( cs >= sizeof( hqx7_buf )) return( -1 );
415 hqx7_first = hqx7_ptr;
416 cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
421 hqx7_last = ( hqx7_first + cc );
426 char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
427 0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
429 Input characters are translated to a number between 0 and 63 by direct
430 array lookup. 0xFF signals a bad character. 0xFE is signals a legal
431 character that should be skipped, namely '\n', '\r'. 0xFD signals ':'.
432 0xFC signals a whitespace character.
435 u_char hqxlookup[] = {
436 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
437 0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
438 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
439 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
440 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
441 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
442 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
443 0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
444 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
445 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
446 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
447 0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
448 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
449 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
450 0x3D, 0x3E, 0x3F, 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,
466 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
467 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
471 * skip_junk is called from hqx_open. it skips over junk in the file until
472 * it comes to a line containing a valid first line of binhqx encoded file.
473 * returns a 0 for success, negative if it never finds good data.
474 * pass a FIRST when looking for the first valid binhex line, a value of
475 * OTHER when looking for any subsequent line.
478 int skip_junk( line )
487 if ( line == FIRST ) {
488 if ( hqx7_fill( hqx7_buf ) <= 0 ) {
489 fprintf( stderr, "Premature end of file :" );
494 while ( found == NOWAY ) {
495 if ( line == FIRST ) {
496 if ( *hqx7_first == ':' ) {
500 while (( stopflag == NOWAY ) &&
501 ( nc < ( hqx7_last - hqx7_first ))) {
502 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
507 stopflag = SURETHANG;
514 if (( nc > 30 ) && ( nc < 64 ) &&
515 (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
520 if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
524 while (( stopflag == NOWAY ) &&
525 ( nc < ( hqx7_last - hqx7_first ))) {
526 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
529 if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
535 stopflag = SURETHANG;
545 } else if (( nc > 30 ) && ( c == 0xFE )) {
553 if (( hqx7_last - hqx7_first ) == nc ) {
554 if ( line == FIRST ) {
556 } else *hqx7_buf = '\n';
557 memcpy(hqx7_buf + 1, hqx7_first, nc );
558 hqx7_first = hqx7_buf + ( ++nc );
559 if ( hqx7_fill( hqx7_first ) <= 0 ) {
560 fprintf( stderr, "Premature end of file :" );
563 hqx7_first = hqx7_buf;
571 * hqx_7tobin is used to read the data, converted to binary. It is
572 * called by hqx_header_read to get the header information, and must be
573 * called to get the data for each fork, and the crc data for each
574 * fork. it has the same basic calling structure as unix read. the
575 * number of valid bytes read is returned. It does buffering so as to
576 * return the requested length of data every time, unless the end of
580 int hqx_7tobin( outbuf, datalen )
584 static u_char hqx8[3];
586 static u_char prev_hqx8;
587 static u_char prev_out;
588 static u_char prev_hqx7;
596 fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
597 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
600 if ( first_flag == 0 ) {
610 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
614 out_last = out_first + datalen;
616 while (( out_first < out_last ) && ( eofflag == 0 )) {
618 if ( hqx7_first == hqx7_last ) {
619 if ( hqx7_fill( hqx7_buf ) == 0 ) {
627 while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
628 hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
629 switch ( hqx7[ hqx7i ] ) {
631 if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
638 while ( hqx7i < 4 ) {
643 prev_hqx7 = hqx7[ hqx7i ];
644 if ( skip_junk( OTHER ) < 0 ) {
645 fprintf( stderr, "\n" );
647 while ( hqx7i < 4 ) {
648 hqx7[ hqx7i++ ] = 0; }
652 prev_hqx7 = hqx7[ hqx7i++ ];
659 hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
660 hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
661 hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
666 while (( hqx8i < 3 ) && ( out_first < out_last )) {
669 putc( hqx8i, rawhex );
670 putc( hqx8[ hqx8i ], rawhex );
671 #endif /* HEXOUTPUT */
673 if ( prev_hqx8 == RUNCHAR ) {
674 if ( hqx8[ hqx8i ] == 0 ) {
675 *out_first = prev_hqx8;
677 putc( *out_first, expandhex );
678 #endif /* HEXOUTPUT */
679 prev_out = prev_hqx8;
682 while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
683 *out_first = prev_out;
685 putc( *out_first, expandhex );
686 #endif /* HEXOUTPUT */
690 if ( hqx8[ hqx8i ] < 2 ) {
691 prev_hqx8 = hqx8[ hqx8i ];
697 prev_hqx8 = hqx8[ hqx8i ];
698 if ( prev_hqx8 != RUNCHAR ) {
699 *out_first = prev_hqx8;
701 putc( *out_first, expandhex );
702 #endif /* HEXOUTPUT */
703 prev_out = prev_hqx8;
711 return( out_first - outbuf );