2 * $Id: hqx.c,v 1.17 2009-10-14 02:24:04 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>
37 /* String used to indicate standard input instead of a disk
38 file. Should be a string not normally used for a file
49 /* Looking for the first or any other line of a binhex file
54 /* This is the binhex run length encoding character
58 /* These are field sizes in bytes of various pieces of the
67 #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 static 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 static u_char hqx7_buf[8192];
92 static u_char *hqx7_first;
93 static u_char *hqx7_last;
94 static int first_flag;
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(char *hqxfile, int flags, struct FHeader *fh, int options)
108 fprintf( stderr, "megatron: entering hqx_open\n" );
110 select_charset( options);
111 if ( flags == O_RDONLY ) {
114 rawhex = fopen( "rawhex.unhex", "w" );
115 expandhex = fopen( "expandhex.unhex", "w" );
116 #endif /* HEXOUTPUT */
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, SEEK_CUR );
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(int keepflag)
167 if ( keepflag == KEEP ) {
168 return( close( hqx.filed ));
169 } else if ( keepflag == TRASH ) {
170 if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
178 * hqx_read is called until it returns zero for each fork. when it is
179 * and finds that there is zero left to give, it reads in and compares
180 * the crc with the calculated one, and returns zero if all is well.
181 * it returns negative is the crc was bad or if has been called too many
182 * times for the same fork. hqx_read must be called enough times to
183 * return zero and no more than that.
186 int hqx_read(int fork, char *buffer, int length)
195 pos = lseek( hqx.filed, 0, SEEK_CUR );
196 fprintf( stderr, "hqx_read: current position is %ld\n", pos );
198 fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
199 fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
200 #endif /* DEBUG >= 3 */
202 if (hqx.forklen[fork] > length) {
203 fprintf(stderr, "This should never happen, dude! length %d, fork length == %u\n", length, hqx.forklen[fork]);
204 return hqx.forklen[fork];
207 if ( hqx.forklen[ fork ] == 0 ) {
208 cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
209 if ( cc == sizeof( storedcrc )) {
210 storedcrc = ntohs ( storedcrc );
212 fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
213 fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
214 #endif /* DEBUG >= 4 */
215 if ( storedcrc == hqx.forkcrc[ fork ] ) {
218 fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
224 if ( hqx.forklen[ fork ] < length ) {
225 readlen = hqx.forklen[ fork ];
230 fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
231 #endif /* DEBUG >= 3 */
233 cc = hqx_7tobin( buffer, readlen );
235 hqx.forkcrc[ fork ] =
236 updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
237 hqx.forklen[ fork ] -= cc;
240 fprintf( stderr, "hqx_read: chars read is %d\n", cc );
241 #endif /* DEBUG >= 3 */
246 * hqx_header_read is called by hqx_open, and before any information can
247 * read from the hqx_header substruct. it must be called before any
248 * of the bytes of the other two forks can be read, as well.
249 * returns a negative number if it was unable to pull enough information
250 * to fill the hqx_header fields.
253 int hqx_header_read(struct FHeader *fh)
255 char *headerbuf, *headerptr;
256 u_int32_t time_seconds;
263 headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
264 #endif /* HEXOUTPUT */
266 mask = htons( 0xfcee );
269 if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
270 fprintf( stderr, "Premature end of file :" );
273 hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen,
277 write( headerfork, &namelen, sizeof( namelen ));
278 #endif /* HEXOUTPUT */
281 (char *)malloc( (unsigned int)( namelen + BHH_HEADSIZ ))) == NULL ) {
284 if ( hqx_7tobin( headerbuf, ( namelen + BHH_HEADSIZ )) == 0 ) {
286 fprintf( stderr, "Premature end of file :" );
289 headerptr = headerbuf;
290 hqx.headercrc = updcrc( hqx.headercrc,
291 (u_char *)headerbuf, ( namelen + BHH_HEADSIZ - BHH_CRCSIZ ));
294 write( headerfork, headerbuf, ( namelen + BHH_HEADSIZ ));
295 #endif /* HEXOUTPUT */
298 * stuff from the hqx file header
301 memcpy( fh->name, headerptr, (int)namelen );
302 headerptr += namelen;
303 headerptr += BHH_VERSION;
304 memcpy(&fh->finder_info, headerptr, BHH_TCSIZ );
305 headerptr += BHH_TCSIZ;
306 memcpy(&fh->finder_info.fdFlags, headerptr, BHH_FLAGSIZ );
307 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
308 headerptr += BHH_FLAGSIZ;
309 memcpy(&fh->forklen[ DATA ], headerptr, BHH_DATASIZ );
310 hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
311 headerptr += BHH_DATASIZ;
312 memcpy( &fh->forklen[ RESOURCE ], headerptr, BHH_RESSIZ );
313 hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
314 headerptr += BHH_RESSIZ;
315 memcpy(&header_crc, headerptr, BHH_CRCSIZ );
316 headerptr += BHH_CRCSIZ;
317 header_crc = ntohs( header_crc );
320 * stuff that should be zero'ed out
323 fh->comment[0] = '\0';
324 fh->finder_info.fdLocation = 0;
325 fh->finder_info.fdFldr = 0;
331 fprintf( stderr, "Values read by hqx_header_read\n" );
332 fprintf( stderr, "name length\t\t%d\n", namelen );
333 fprintf( stderr, "file name\t\t%s\n", fh->name );
334 fprintf( stderr, "get info comment\t%s\n", fh->comment );
335 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
336 &fh->finder_info.fdType );
337 fprintf( stderr, "creator\t\t\t%.*s\n",
338 sizeof( fh->finder_info.fdCreator ),
339 &fh->finder_info.fdCreator );
340 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
341 flags = ntohs( flags );
342 fprintf( stderr, "flags\t\t\t%x\n", flags );
343 fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
344 fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
345 fprintf( stderr, "header_crc\t\t%x\n", header_crc );
346 fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
347 fprintf( stderr, "\n" );
349 #endif /* DEBUG >= 5 */
352 * create and modify times are figured from right now
355 time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
356 memcpy( &fh->create_date, &time_seconds,
357 sizeof( fh->create_date ));
358 memcpy( &fh->mod_date, &time_seconds,
359 sizeof( fh->mod_date ));
360 fh->backup_date = AD_DATE_START;
363 * stuff that should be zero'ed out
366 fh->comment[0] = '\0';
367 memset( &fh->finder_info.fdLocation, 0,
368 sizeof( fh->finder_info.fdLocation ));
369 memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
371 hqx.forkcrc[ DATA ] = 0;
372 hqx.forkcrc[ RESOURCE ] = 0;
375 if ( header_crc != hqx.headercrc ) {
376 fprintf( stderr, "Bad Header crc, dude :" );
386 int hqx_header_write(struct FHeader *fh _U_)
392 * hqx7_fill is called from skip_junk and hqx_7tobin. it pulls from the
393 * binhqx file into the hqx7 buffer. returns number of bytes read
394 * or a zero for end of file.
395 * it sets the pointers to the hqx7 buffer up to point to the valid data.
398 int hqx7_fill(u_char *hqx7_ptr)
403 cs = hqx7_ptr - hqx7_buf;
404 if ( cs >= sizeof( hqx7_buf )) return( -1 );
405 hqx7_first = hqx7_ptr;
406 cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
411 hqx7_last = ( hqx7_first + cc );
416 char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
417 0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
419 Input characters are translated to a number between 0 and 63 by direct
420 array lookup. 0xFF signals a bad character. 0xFE is signals a legal
421 character that should be skipped, namely '\n', '\r'. 0xFD signals ':'.
422 0xFC signals a whitespace character.
425 static const u_char hqxlookup[] = {
426 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
427 0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
428 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
429 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
430 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
431 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
432 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
433 0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
434 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
435 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
436 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
437 0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
438 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
439 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
440 0x3D, 0x3E, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
441 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
442 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
443 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
444 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
445 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
446 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
447 0xFF, 0xFF, 0xFF, 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,
461 * skip_junk is called from hqx_open. it skips over junk in the file until
462 * it comes to a line containing a valid first line of binhqx encoded file.
463 * returns a 0 for success, negative if it never finds good data.
464 * pass a FIRST when looking for the first valid binhex line, a value of
465 * OTHER when looking for any subsequent line.
468 int skip_junk(int line)
476 if ( line == FIRST ) {
477 if ( hqx7_fill( hqx7_buf ) <= 0 ) {
478 fprintf( stderr, "Premature end of file :" );
483 while ( found == NOWAY ) {
484 if ( line == FIRST ) {
485 if ( *hqx7_first == ':' ) {
489 while (( stopflag == NOWAY ) &&
490 ( nc < ( hqx7_last - hqx7_first ))) {
491 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
496 stopflag = SURETHANG;
503 if (( nc > 30 ) && ( nc < 64 ) &&
504 (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
509 if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
513 while (( stopflag == NOWAY ) &&
514 ( nc < ( hqx7_last - hqx7_first ))) {
515 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
518 if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
524 stopflag = SURETHANG;
534 } else if (( nc > 30 ) && ( c == 0xFE )) {
542 if (( hqx7_last - hqx7_first ) == nc ) {
543 if ( line == FIRST ) {
545 } else *hqx7_buf = '\n';
546 memcpy(hqx7_buf + 1, hqx7_first, nc );
547 hqx7_first = hqx7_buf + ( ++nc );
548 if ( hqx7_fill( hqx7_first ) <= 0 ) {
549 fprintf( stderr, "Premature end of file :" );
552 hqx7_first = hqx7_buf;
560 * hqx_7tobin is used to read the data, converted to binary. It is
561 * called by hqx_header_read to get the header information, and must be
562 * called to get the data for each fork, and the crc data for each
563 * fork. it has the same basic calling structure as unix read. the
564 * number of valid bytes read is returned. It does buffering so as to
565 * return the requested length of data every time, unless the end of
569 int hqx_7tobin( char *outbuf, int datalen)
571 static u_char hqx8[3];
573 static u_char prev_hqx8;
574 static u_char prev_out;
575 static u_char prev_hqx7;
583 fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
584 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
587 if ( first_flag == 0 ) {
597 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
601 out_last = out_first + datalen;
603 while (( out_first < out_last ) && ( eofflag == 0 )) {
605 if ( hqx7_first == hqx7_last ) {
606 if ( hqx7_fill( hqx7_buf ) == 0 ) {
614 while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
615 hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
616 switch ( hqx7[ hqx7i ] ) {
618 if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
625 while ( hqx7i < 4 ) {
630 prev_hqx7 = hqx7[ hqx7i ];
631 if ( skip_junk( OTHER ) < 0 ) {
632 fprintf( stderr, "\n" );
634 while ( hqx7i < 4 ) {
635 hqx7[ hqx7i++ ] = 0; }
639 prev_hqx7 = hqx7[ hqx7i++ ];
646 hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
647 hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
648 hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
653 while (( hqx8i < 3 ) && ( out_first < out_last )) {
656 putc( hqx8i, rawhex );
657 putc( hqx8[ hqx8i ], rawhex );
658 #endif /* HEXOUTPUT */
660 if ( prev_hqx8 == RUNCHAR ) {
661 if ( hqx8[ hqx8i ] == 0 ) {
662 *out_first = prev_hqx8;
664 putc( *out_first, expandhex );
665 #endif /* HEXOUTPUT */
666 prev_out = prev_hqx8;
669 while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
670 *out_first = prev_out;
672 putc( *out_first, expandhex );
673 #endif /* HEXOUTPUT */
677 if ( hqx8[ hqx8i ] < 2 ) {
678 prev_hqx8 = hqx8[ hqx8i ];
684 prev_hqx8 = hqx8[ hqx8i ];
685 if ( prev_hqx8 != RUNCHAR ) {
686 *out_first = prev_hqx8;
688 putc( *out_first, expandhex );
689 #endif /* HEXOUTPUT */
690 prev_out = prev_hqx8;
698 return( out_first - outbuf );