2 * $Id: hqx.c,v 1.8.2.1 2002-03-12 14:12:40 srittau Exp $
7 #endif /* HAVE_CONFIG_H */
12 #include <sys/param.h>
23 #endif /* HAVE_FCNTL_H */
25 #include <netinet/in.h>
27 #include <atalk/adouble.h>
28 #include <netatalk/endian.h>
35 /* String used to indicate standard input instead of a disk
36 file. Should be a string not normally used for a file
47 /* Looking for the first or any other line of a binhex file
52 /* This is the binhex run length encoding character
56 /* These are field sizes in bytes of various pieces of the
65 #define BHH_HEADSIZ 21
69 /* Forward declarations.
71 int skip_junk(int line);
72 int hqx_close(int keepflag);
73 int hqx_header_read(struct FHeader *fh);
74 int hqx_header_write(struct FHeader *fh);
75 int hqx_7tobin(char *outbuf, int datalen);
76 int hqx7_fill(u_char *hqx7_ptr);
79 FILE *rawhex, *expandhex;
80 #endif /* HEXOUTPUT */
82 struct hqx_file_data {
83 u_int32_t forklen[ NUMFORKS ];
84 u_short forkcrc[ NUMFORKS ];
85 char path[ MAXPATHLEN + 1];
90 extern char *forkname[];
91 u_char hqx7_buf[8192];
97 hqx_open must be called first. pass it a filename that is supposed
98 to contain a binhqx file. an hqx struct will be allocated and
99 somewhat initialized; hqx_fd is set. skip_junk is called from
100 here; skip_junk leaves hqx7_first and hqx7_last set.
103 int hqx_open( hqxfile, flags, fh, options )
111 fprintf( stderr, "megatron: entering hqx_open\n" );
113 if ( flags == O_RDONLY ) {
116 rawhex = fopen( "rawhex.unhex", "w" );
117 expandhex = fopen( "expandhex.unhex", "w" );
118 #endif /* HEXOUTPUT */
122 if ( strcmp( hqxfile, STDIN ) == 0 ) {
123 hqx.filed = fileno( stdin );
124 } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
129 if ( skip_junk( FIRST ) == 0 ) {
130 if ( hqx_header_read( fh ) == 0 ) {
134 pos = lseek( hqx.filed, 0, SEEK_CUR );
135 fprintf( stderr, "megatron: current position is %ld\n", pos );
141 fprintf( stderr, "%s\n", hqxfile );
144 maxlen = sizeof( hqx.path ) -1;
145 strncpy( hqx.path, fh->name, maxlen );
146 strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
147 strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
148 if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
152 if ( hqx_header_write( fh ) != 0 ) {
154 fprintf( stderr, "%s\n", hqx.path );
162 * hqx_close must be called before a second file can be opened using
163 * hqx_open. Upon successful completion, a value of 0 is returned.
164 * Otherwise, a value of -1 is returned.
167 int hqx_close( keepflag )
170 if ( keepflag == KEEP ) {
171 return( close( hqx.filed ));
172 } else if ( keepflag == TRASH ) {
173 if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
181 * hqx_read is called until it returns zero for each fork. when it is
182 * and finds that there is zero left to give, it reads in and compares
183 * the crc with the calculated one, and returns zero if all is well.
184 * it returns negative is the crc was bad or if has been called too many
185 * times for the same fork. hqx_read must be called enough times to
186 * return zero and no more than that.
189 int hqx_read( fork, buffer, length )
201 pos = lseek( hqx.filed, 0, SEEK_CUR );
202 fprintf( stderr, "hqx_read: current position is %ld\n", pos );
204 fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
205 fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
206 #endif /* DEBUG >= 3 */
208 if ( hqx.forklen[ fork ] < 0 ) {
209 fprintf( stderr, "This should never happen, dude!\n" );
210 return( hqx.forklen[ fork ] );
213 if ( hqx.forklen[ fork ] == 0 ) {
214 cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
215 if ( cc == sizeof( storedcrc )) {
216 storedcrc = ntohs ( storedcrc );
218 fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
219 fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
220 #endif /* DEBUG >= 4 */
221 if ( storedcrc == hqx.forkcrc[ fork ] ) {
224 fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
230 if ( hqx.forklen[ fork ] < length ) {
231 readlen = hqx.forklen[ fork ];
236 fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
237 #endif /* DEBUG >= 3 */
239 cc = hqx_7tobin( buffer, readlen );
241 hqx.forkcrc[ fork ] =
242 updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
243 hqx.forklen[ fork ] -= cc;
246 fprintf( stderr, "hqx_read: chars read is %d\n", cc );
247 #endif /* DEBUG >= 3 */
252 * hqx_header_read is called by hqx_open, and before any information can
253 * read from the hqx_header substruct. it must be called before any
254 * of the bytes of the other two forks can be read, as well.
255 * returns a negative number if it was unable to pull enough information
256 * to fill the hqx_header fields.
259 int hqx_header_read( fh )
262 char *headerbuf, *headerptr;
263 u_int32_t time_seconds;
270 headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
271 #endif /* HEXOUTPUT */
273 mask = htons( 0xfcee );
276 if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
277 fprintf( stderr, "Premature end of file :" );
280 hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen,
284 write( headerfork, &namelen, sizeof( namelen ));
285 #endif /* HEXOUTPUT */
288 (char *)malloc( (unsigned int)( namelen + BHH_HEADSIZ ))) == 0 ) {
291 if ( hqx_7tobin( headerbuf, ( namelen + BHH_HEADSIZ )) == 0 ) {
293 fprintf( stderr, "Premature end of file :" );
296 headerptr = headerbuf;
297 hqx.headercrc = updcrc( hqx.headercrc,
298 (u_char *)headerbuf, ( namelen + BHH_HEADSIZ - BHH_CRCSIZ ));
301 write( headerfork, headerbuf, ( namelen + BHH_HEADSIZ ));
302 #endif /* HEXOUTPUT */
305 * stuff from the hqx file header
308 memcpy( fh->name, headerptr, (int)namelen );
309 headerptr += namelen;
310 headerptr += BHH_VERSION;
311 memcpy(&fh->finder_info, headerptr, BHH_TCSIZ );
312 headerptr += BHH_TCSIZ;
313 memcpy(&fh->finder_info.fdFlags, headerptr, BHH_FLAGSIZ );
314 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
315 headerptr += BHH_FLAGSIZ;
316 memcpy(&fh->forklen[ DATA ], headerptr, BHH_DATASIZ );
317 hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
318 headerptr += BHH_DATASIZ;
319 memcpy( &fh->forklen[ RESOURCE ], headerptr, BHH_RESSIZ );
320 hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
321 headerptr += BHH_RESSIZ;
322 memcpy(&header_crc, headerptr, BHH_CRCSIZ );
323 headerptr += BHH_CRCSIZ;
324 header_crc = ntohs( header_crc );
327 * stuff that should be zero'ed out
330 fh->comment[0] = '\0';
331 fh->finder_info.fdLocation = 0;
332 fh->finder_info.fdFldr = 0;
338 fprintf( stderr, "Values read by hqx_header_read\n" );
339 fprintf( stderr, "name length\t\t%d\n", namelen );
340 fprintf( stderr, "file name\t\t%s\n", fh->name );
341 fprintf( stderr, "get info comment\t%s\n", fh->comment );
342 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
343 &fh->finder_info.fdType );
344 fprintf( stderr, "creator\t\t\t%.*s\n",
345 sizeof( fh->finder_info.fdCreator ),
346 &fh->finder_info.fdCreator );
347 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
348 flags = ntohs( flags );
349 fprintf( stderr, "flags\t\t\t%x\n", flags );
350 fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
351 fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
352 fprintf( stderr, "header_crc\t\t%x\n", header_crc );
353 fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
354 fprintf( stderr, "\n" );
356 #endif /* DEBUG >= 5 */
359 * create and modify times are figured from right now
362 time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
363 memcpy( &fh->create_date, &time_seconds,
364 sizeof( fh->create_date ));
365 memcpy( &fh->mod_date, &time_seconds,
366 sizeof( fh->mod_date ));
367 fh->backup_date = AD_DATE_START;
370 * stuff that should be zero'ed out
373 fh->comment[0] = '\0';
374 memset( &fh->finder_info.fdLocation, 0,
375 sizeof( fh->finder_info.fdLocation ));
376 memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
378 hqx.forkcrc[ DATA ] = 0;
379 hqx.forkcrc[ RESOURCE ] = 0;
382 if ( header_crc != hqx.headercrc ) {
383 fprintf( stderr, "Bad Header crc, dude :" );
393 int hqx_header_write( fh )
400 * hqx7_fill is called from skip_junk and hqx_7tobin. it pulls from the
401 * binhqx file into the hqx7 buffer. returns number of bytes read
402 * or a zero for end of file.
403 * it sets the pointers to the hqx7 buffer up to point to the valid data.
406 int hqx7_fill( hqx7_ptr )
412 cs = hqx7_ptr - hqx7_buf;
413 if ( cs >= sizeof( hqx7_buf )) return( -1 );
414 hqx7_first = hqx7_ptr;
415 cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
420 hqx7_last = ( hqx7_first + cc );
425 char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
426 0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
428 Input characters are translated to a number between 0 and 63 by direct
429 array lookup. 0xFF signals a bad character. 0xFE is signals a legal
430 character that should be skipped, namely '\n', '\r'. 0xFD signals ':'.
431 0xFC signals a whitespace character.
434 u_char hqxlookup[] = {
435 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
436 0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
437 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
438 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
439 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
440 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
441 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
442 0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
443 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
444 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
445 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
446 0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
447 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
448 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
449 0x3D, 0x3E, 0x3F, 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,
466 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
470 * skip_junk is called from hqx_open. it skips over junk in the file until
471 * it comes to a line containing a valid first line of binhqx encoded file.
472 * returns a 0 for success, negative if it never finds good data.
473 * pass a FIRST when looking for the first valid binhex line, a value of
474 * OTHER when looking for any subsequent line.
477 int skip_junk( line )
486 if ( line == FIRST ) {
487 if ( hqx7_fill( hqx7_buf ) <= 0 ) {
488 fprintf( stderr, "Premature end of file :" );
493 while ( found == NOWAY ) {
494 if ( line == FIRST ) {
495 if ( *hqx7_first == ':' ) {
499 while (( stopflag == NOWAY ) &&
500 ( nc < ( hqx7_last - hqx7_first ))) {
501 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
506 stopflag = SURETHANG;
513 if (( nc > 30 ) && ( nc < 64 ) &&
514 (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
519 if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
523 while (( stopflag == NOWAY ) &&
524 ( nc < ( hqx7_last - hqx7_first ))) {
525 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
528 if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
534 stopflag = SURETHANG;
544 } else if (( nc > 30 ) && ( c == 0xFE )) {
552 if (( hqx7_last - hqx7_first ) == nc ) {
553 if ( line == FIRST ) {
555 } else *hqx7_buf = '\n';
556 memcpy(hqx7_buf + 1, hqx7_first, nc );
557 hqx7_first = hqx7_buf + ( ++nc );
558 if ( hqx7_fill( hqx7_first ) <= 0 ) {
559 fprintf( stderr, "Premature end of file :" );
562 hqx7_first = hqx7_buf;
570 * hqx_7tobin is used to read the data, converted to binary. It is
571 * called by hqx_header_read to get the header information, and must be
572 * called to get the data for each fork, and the crc data for each
573 * fork. it has the same basic calling structure as unix read. the
574 * number of valid bytes read is returned. It does buffering so as to
575 * return the requested length of data every time, unless the end of
579 int hqx_7tobin( outbuf, datalen )
583 static u_char hqx8[3];
585 static u_char prev_hqx8;
586 static u_char prev_out;
587 static u_char prev_hqx7;
595 fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
596 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
599 if ( first_flag == 0 ) {
609 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
613 out_last = out_first + datalen;
615 while (( out_first < out_last ) && ( eofflag == 0 )) {
617 if ( hqx7_first == hqx7_last ) {
618 if ( hqx7_fill( hqx7_buf ) == 0 ) {
626 while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
627 hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
628 switch ( hqx7[ hqx7i ] ) {
630 if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
637 while ( hqx7i < 4 ) {
642 prev_hqx7 = hqx7[ hqx7i ];
643 if ( skip_junk( OTHER ) < 0 ) {
644 fprintf( stderr, "\n" );
646 while ( hqx7i < 4 ) {
647 hqx7[ hqx7i++ ] = 0; }
651 prev_hqx7 = hqx7[ hqx7i++ ];
658 hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
659 hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
660 hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
665 while (( hqx8i < 3 ) && ( out_first < out_last )) {
668 putc( hqx8i, rawhex );
669 putc( hqx8[ hqx8i ], rawhex );
670 #endif /* HEXOUTPUT */
672 if ( prev_hqx8 == RUNCHAR ) {
673 if ( hqx8[ hqx8i ] == 0 ) {
674 *out_first = prev_hqx8;
676 putc( *out_first, expandhex );
677 #endif /* HEXOUTPUT */
678 prev_out = prev_hqx8;
681 while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
682 *out_first = prev_out;
684 putc( *out_first, expandhex );
685 #endif /* HEXOUTPUT */
689 if ( hqx8[ hqx8i ] < 2 ) {
690 prev_hqx8 = hqx8[ hqx8i ];
696 prev_hqx8 = hqx8[ hqx8i ];
697 if ( prev_hqx8 != RUNCHAR ) {
698 *out_first = prev_hqx8;
700 putc( *out_first, expandhex );
701 #endif /* HEXOUTPUT */
702 prev_out = prev_hqx8;
710 return( out_first - outbuf );