2 * $Id: hqx.c,v 1.12.4.1 2003-09-03 20:40:50 didg 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>
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 select_charset( options);
115 if ( flags == O_RDONLY ) {
118 rawhex = fopen( "rawhex.unhex", "w" );
119 expandhex = fopen( "expandhex.unhex", "w" );
120 #endif /* HEXOUTPUT */
124 if ( strcmp( hqxfile, STDIN ) == 0 ) {
125 hqx.filed = fileno( stdin );
126 } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
131 if ( skip_junk( FIRST ) == 0 ) {
132 if ( hqx_header_read( fh ) == 0 ) {
136 pos = lseek( hqx.filed, 0, SEEK_CUR );
137 fprintf( stderr, "megatron: current position is %ld\n", pos );
143 fprintf( stderr, "%s\n", hqxfile );
146 maxlen = sizeof( hqx.path ) -1;
147 strncpy( hqx.path, fh->name, maxlen );
148 strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
149 strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
150 if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
154 if ( hqx_header_write( fh ) != 0 ) {
156 fprintf( stderr, "%s\n", hqx.path );
164 * hqx_close must be called before a second file can be opened using
165 * hqx_open. Upon successful completion, a value of 0 is returned.
166 * Otherwise, a value of -1 is returned.
169 int hqx_close( keepflag )
172 if ( keepflag == KEEP ) {
173 return( close( hqx.filed ));
174 } else if ( keepflag == TRASH ) {
175 if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
183 * hqx_read is called until it returns zero for each fork. when it is
184 * and finds that there is zero left to give, it reads in and compares
185 * the crc with the calculated one, and returns zero if all is well.
186 * it returns negative is the crc was bad or if has been called too many
187 * times for the same fork. hqx_read must be called enough times to
188 * return zero and no more than that.
191 int hqx_read( fork, buffer, length )
203 pos = lseek( hqx.filed, 0, SEEK_CUR );
204 fprintf( stderr, "hqx_read: current position is %ld\n", pos );
206 fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
207 fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
208 #endif /* DEBUG >= 3 */
210 if ( hqx.forklen[ fork ] < 0 ) {
211 fprintf( stderr, "This should never happen, dude!\n" );
212 return( hqx.forklen[ fork ] );
215 if ( hqx.forklen[ fork ] == 0 ) {
216 cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
217 if ( cc == sizeof( storedcrc )) {
218 storedcrc = ntohs ( storedcrc );
220 fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
221 fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
222 #endif /* DEBUG >= 4 */
223 if ( storedcrc == hqx.forkcrc[ fork ] ) {
226 fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
232 if ( hqx.forklen[ fork ] < length ) {
233 readlen = hqx.forklen[ fork ];
238 fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
239 #endif /* DEBUG >= 3 */
241 cc = hqx_7tobin( buffer, readlen );
243 hqx.forkcrc[ fork ] =
244 updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
245 hqx.forklen[ fork ] -= cc;
248 fprintf( stderr, "hqx_read: chars read is %d\n", cc );
249 #endif /* DEBUG >= 3 */
254 * hqx_header_read is called by hqx_open, and before any information can
255 * read from the hqx_header substruct. it must be called before any
256 * of the bytes of the other two forks can be read, as well.
257 * returns a negative number if it was unable to pull enough information
258 * to fill the hqx_header fields.
261 int hqx_header_read( fh )
264 char *headerbuf, *headerptr;
265 u_int32_t time_seconds;
272 headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
273 #endif /* HEXOUTPUT */
275 mask = htons( 0xfcee );
278 if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
279 fprintf( stderr, "Premature end of file :" );
282 hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen,
286 write( headerfork, &namelen, sizeof( namelen ));
287 #endif /* HEXOUTPUT */
290 (char *)malloc( (unsigned int)( namelen + BHH_HEADSIZ ))) == 0 ) {
293 if ( hqx_7tobin( headerbuf, ( namelen + BHH_HEADSIZ )) == 0 ) {
295 fprintf( stderr, "Premature end of file :" );
298 headerptr = headerbuf;
299 hqx.headercrc = updcrc( hqx.headercrc,
300 (u_char *)headerbuf, ( namelen + BHH_HEADSIZ - BHH_CRCSIZ ));
303 write( headerfork, headerbuf, ( namelen + BHH_HEADSIZ ));
304 #endif /* HEXOUTPUT */
307 * stuff from the hqx file header
310 memcpy( fh->name, headerptr, (int)namelen );
311 headerptr += namelen;
312 headerptr += BHH_VERSION;
313 memcpy(&fh->finder_info, headerptr, BHH_TCSIZ );
314 headerptr += BHH_TCSIZ;
315 memcpy(&fh->finder_info.fdFlags, headerptr, BHH_FLAGSIZ );
316 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
317 headerptr += BHH_FLAGSIZ;
318 memcpy(&fh->forklen[ DATA ], headerptr, BHH_DATASIZ );
319 hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
320 headerptr += BHH_DATASIZ;
321 memcpy( &fh->forklen[ RESOURCE ], headerptr, BHH_RESSIZ );
322 hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
323 headerptr += BHH_RESSIZ;
324 memcpy(&header_crc, headerptr, BHH_CRCSIZ );
325 headerptr += BHH_CRCSIZ;
326 header_crc = ntohs( header_crc );
329 * stuff that should be zero'ed out
332 fh->comment[0] = '\0';
333 fh->finder_info.fdLocation = 0;
334 fh->finder_info.fdFldr = 0;
340 fprintf( stderr, "Values read by hqx_header_read\n" );
341 fprintf( stderr, "name length\t\t%d\n", namelen );
342 fprintf( stderr, "file name\t\t%s\n", fh->name );
343 fprintf( stderr, "get info comment\t%s\n", fh->comment );
344 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
345 &fh->finder_info.fdType );
346 fprintf( stderr, "creator\t\t\t%.*s\n",
347 sizeof( fh->finder_info.fdCreator ),
348 &fh->finder_info.fdCreator );
349 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
350 flags = ntohs( flags );
351 fprintf( stderr, "flags\t\t\t%x\n", flags );
352 fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
353 fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
354 fprintf( stderr, "header_crc\t\t%x\n", header_crc );
355 fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
356 fprintf( stderr, "\n" );
358 #endif /* DEBUG >= 5 */
361 * create and modify times are figured from right now
364 time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
365 memcpy( &fh->create_date, &time_seconds,
366 sizeof( fh->create_date ));
367 memcpy( &fh->mod_date, &time_seconds,
368 sizeof( fh->mod_date ));
369 fh->backup_date = AD_DATE_START;
372 * stuff that should be zero'ed out
375 fh->comment[0] = '\0';
376 memset( &fh->finder_info.fdLocation, 0,
377 sizeof( fh->finder_info.fdLocation ));
378 memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
380 hqx.forkcrc[ DATA ] = 0;
381 hqx.forkcrc[ RESOURCE ] = 0;
384 if ( header_crc != hqx.headercrc ) {
385 fprintf( stderr, "Bad Header crc, dude :" );
395 int hqx_header_write( fh )
402 * hqx7_fill is called from skip_junk and hqx_7tobin. it pulls from the
403 * binhqx file into the hqx7 buffer. returns number of bytes read
404 * or a zero for end of file.
405 * it sets the pointers to the hqx7 buffer up to point to the valid data.
408 int hqx7_fill( hqx7_ptr )
414 cs = hqx7_ptr - hqx7_buf;
415 if ( cs >= sizeof( hqx7_buf )) return( -1 );
416 hqx7_first = hqx7_ptr;
417 cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
422 hqx7_last = ( hqx7_first + cc );
427 char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
428 0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
430 Input characters are translated to a number between 0 and 63 by direct
431 array lookup. 0xFF signals a bad character. 0xFE is signals a legal
432 character that should be skipped, namely '\n', '\r'. 0xFD signals ':'.
433 0xFC signals a whitespace character.
436 u_char hqxlookup[] = {
437 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
438 0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
439 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
440 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
441 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
442 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
443 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
444 0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
445 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
446 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
447 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
448 0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
449 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
450 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
451 0x3D, 0x3E, 0x3F, 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,
468 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
472 * skip_junk is called from hqx_open. it skips over junk in the file until
473 * it comes to a line containing a valid first line of binhqx encoded file.
474 * returns a 0 for success, negative if it never finds good data.
475 * pass a FIRST when looking for the first valid binhex line, a value of
476 * OTHER when looking for any subsequent line.
479 int skip_junk( line )
488 if ( line == FIRST ) {
489 if ( hqx7_fill( hqx7_buf ) <= 0 ) {
490 fprintf( stderr, "Premature end of file :" );
495 while ( found == NOWAY ) {
496 if ( line == FIRST ) {
497 if ( *hqx7_first == ':' ) {
501 while (( stopflag == NOWAY ) &&
502 ( nc < ( hqx7_last - hqx7_first ))) {
503 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
508 stopflag = SURETHANG;
515 if (( nc > 30 ) && ( nc < 64 ) &&
516 (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
521 if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
525 while (( stopflag == NOWAY ) &&
526 ( nc < ( hqx7_last - hqx7_first ))) {
527 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
530 if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
536 stopflag = SURETHANG;
546 } else if (( nc > 30 ) && ( c == 0xFE )) {
554 if (( hqx7_last - hqx7_first ) == nc ) {
555 if ( line == FIRST ) {
557 } else *hqx7_buf = '\n';
558 memcpy(hqx7_buf + 1, hqx7_first, nc );
559 hqx7_first = hqx7_buf + ( ++nc );
560 if ( hqx7_fill( hqx7_first ) <= 0 ) {
561 fprintf( stderr, "Premature end of file :" );
564 hqx7_first = hqx7_buf;
572 * hqx_7tobin is used to read the data, converted to binary. It is
573 * called by hqx_header_read to get the header information, and must be
574 * called to get the data for each fork, and the crc data for each
575 * fork. it has the same basic calling structure as unix read. the
576 * number of valid bytes read is returned. It does buffering so as to
577 * return the requested length of data every time, unless the end of
581 int hqx_7tobin( outbuf, datalen )
585 static u_char hqx8[3];
587 static u_char prev_hqx8;
588 static u_char prev_out;
589 static u_char prev_hqx7;
597 fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
598 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
601 if ( first_flag == 0 ) {
611 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
615 out_last = out_first + datalen;
617 while (( out_first < out_last ) && ( eofflag == 0 )) {
619 if ( hqx7_first == hqx7_last ) {
620 if ( hqx7_fill( hqx7_buf ) == 0 ) {
628 while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
629 hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
630 switch ( hqx7[ hqx7i ] ) {
632 if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
639 while ( hqx7i < 4 ) {
644 prev_hqx7 = hqx7[ hqx7i ];
645 if ( skip_junk( OTHER ) < 0 ) {
646 fprintf( stderr, "\n" );
648 while ( hqx7i < 4 ) {
649 hqx7[ hqx7i++ ] = 0; }
653 prev_hqx7 = hqx7[ hqx7i++ ];
660 hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
661 hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
662 hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
667 while (( hqx8i < 3 ) && ( out_first < out_last )) {
670 putc( hqx8i, rawhex );
671 putc( hqx8[ hqx8i ], rawhex );
672 #endif /* HEXOUTPUT */
674 if ( prev_hqx8 == RUNCHAR ) {
675 if ( hqx8[ hqx8i ] == 0 ) {
676 *out_first = prev_hqx8;
678 putc( *out_first, expandhex );
679 #endif /* HEXOUTPUT */
680 prev_out = prev_hqx8;
683 while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
684 *out_first = prev_out;
686 putc( *out_first, expandhex );
687 #endif /* HEXOUTPUT */
691 if ( hqx8[ hqx8i ] < 2 ) {
692 prev_hqx8 = hqx8[ hqx8i ];
698 prev_hqx8 = hqx8[ hqx8i ];
699 if ( prev_hqx8 != RUNCHAR ) {
700 *out_first = prev_hqx8;
702 putc( *out_first, expandhex );
703 #endif /* HEXOUTPUT */
704 prev_out = prev_hqx8;
712 return( out_first - outbuf );