2 * $Id: hqx.c,v 1.18 2010-01-27 21:27:53 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
70 FILE *rawhex, *expandhex;
71 #endif /* HEXOUTPUT */
73 static struct hqx_file_data {
74 u_int32_t forklen[ NUMFORKS ];
75 u_short forkcrc[ NUMFORKS ];
76 char path[ MAXPATHLEN + 1];
81 extern char *forkname[];
82 static u_char hqx7_buf[8192];
83 static u_char *hqx7_first;
84 static u_char *hqx7_last;
85 static int first_flag;
88 hqx_open must be called first. pass it a filename that is supposed
89 to contain a binhqx file. an hqx struct will be allocated and
90 somewhat initialized; hqx_fd is set. skip_junk is called from
91 here; skip_junk leaves hqx7_first and hqx7_last set.
94 int hqx_open(char *hqxfile, int flags, struct FHeader *fh, int options)
99 fprintf( stderr, "megatron: entering hqx_open\n" );
101 select_charset( options);
102 if ( flags == O_RDONLY ) {
105 rawhex = fopen( "rawhex.unhex", "w" );
106 expandhex = fopen( "expandhex.unhex", "w" );
107 #endif /* HEXOUTPUT */
111 if ( strcmp( hqxfile, STDIN ) == 0 ) {
112 hqx.filed = fileno( stdin );
113 } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
118 if ( skip_junk( FIRST ) == 0 ) {
119 if ( hqx_header_read( fh ) == 0 ) {
123 pos = lseek( hqx.filed, 0, SEEK_CUR );
124 fprintf( stderr, "megatron: current position is %ld\n", pos );
130 fprintf( stderr, "%s\n", hqxfile );
133 maxlen = sizeof( hqx.path ) -1;
134 strncpy( hqx.path, fh->name, maxlen );
135 strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
136 strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
137 if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
141 if ( hqx_header_write( fh ) != 0 ) {
143 fprintf( stderr, "%s\n", hqx.path );
151 * hqx_close must be called before a second file can be opened using
152 * hqx_open. Upon successful completion, a value of 0 is returned.
153 * Otherwise, a value of -1 is returned.
156 int hqx_close(int keepflag)
158 if ( keepflag == KEEP ) {
159 return( close( hqx.filed ));
160 } else if ( keepflag == TRASH ) {
161 if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
169 * hqx_read is called until it returns zero for each fork. when it is
170 * and finds that there is zero left to give, it reads in and compares
171 * the crc with the calculated one, and returns zero if all is well.
172 * it returns negative is the crc was bad or if has been called too many
173 * times for the same fork. hqx_read must be called enough times to
174 * return zero and no more than that.
177 ssize_t hqx_read(int fork, char *buffer, size_t length)
186 pos = lseek( hqx.filed, 0, SEEK_CUR );
187 fprintf( stderr, "hqx_read: current position is %ld\n", pos );
189 fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
190 fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
191 #endif /* DEBUG >= 3 */
193 if (hqx.forklen[fork] > 0x7FFFFFFF) {
194 fprintf(stderr, "This should never happen, dude!, fork length == %u\n", hqx.forklen[fork]);
198 if ( hqx.forklen[ fork ] == 0 ) {
199 cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
200 if ( cc == sizeof( storedcrc )) {
201 storedcrc = ntohs ( storedcrc );
203 fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
204 fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
205 #endif /* DEBUG >= 4 */
206 if ( storedcrc == hqx.forkcrc[ fork ] ) {
209 fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
215 if ( hqx.forklen[ fork ] < length ) {
216 readlen = hqx.forklen[ fork ];
221 fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
222 #endif /* DEBUG >= 3 */
224 cc = hqx_7tobin( buffer, readlen );
226 hqx.forkcrc[ fork ] =
227 updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
228 hqx.forklen[ fork ] -= cc;
231 fprintf( stderr, "hqx_read: chars read is %d\n", cc );
232 #endif /* DEBUG >= 3 */
237 * hqx_header_read is called by hqx_open, and before any information can
238 * read from the hqx_header substruct. it must be called before any
239 * of the bytes of the other two forks can be read, as well.
240 * returns a negative number if it was unable to pull enough information
241 * to fill the hqx_header fields.
244 int hqx_header_read(struct FHeader *fh)
246 char *headerbuf, *headerptr;
247 u_int32_t time_seconds;
254 headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
255 #endif /* HEXOUTPUT */
257 mask = htons( 0xfcee );
260 if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
261 fprintf( stderr, "Premature end of file :" );
264 hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen,
268 write( headerfork, &namelen, sizeof( namelen ));
269 #endif /* HEXOUTPUT */
272 (char *)malloc( (unsigned int)( namelen + BHH_HEADSIZ ))) == NULL ) {
275 if ( hqx_7tobin( headerbuf, ( namelen + BHH_HEADSIZ )) == 0 ) {
277 fprintf( stderr, "Premature end of file :" );
280 headerptr = headerbuf;
281 hqx.headercrc = updcrc( hqx.headercrc,
282 (u_char *)headerbuf, ( namelen + BHH_HEADSIZ - BHH_CRCSIZ ));
285 write( headerfork, headerbuf, ( namelen + BHH_HEADSIZ ));
286 #endif /* HEXOUTPUT */
289 * stuff from the hqx file header
292 memcpy( fh->name, headerptr, (int)namelen );
293 headerptr += namelen;
294 headerptr += BHH_VERSION;
295 memcpy(&fh->finder_info, headerptr, BHH_TCSIZ );
296 headerptr += BHH_TCSIZ;
297 memcpy(&fh->finder_info.fdFlags, headerptr, BHH_FLAGSIZ );
298 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
299 headerptr += BHH_FLAGSIZ;
300 memcpy(&fh->forklen[ DATA ], headerptr, BHH_DATASIZ );
301 hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
302 headerptr += BHH_DATASIZ;
303 memcpy( &fh->forklen[ RESOURCE ], headerptr, BHH_RESSIZ );
304 hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
305 headerptr += BHH_RESSIZ;
306 memcpy(&header_crc, headerptr, BHH_CRCSIZ );
307 headerptr += BHH_CRCSIZ;
308 header_crc = ntohs( header_crc );
311 * stuff that should be zero'ed out
314 fh->comment[0] = '\0';
315 fh->finder_info.fdLocation = 0;
316 fh->finder_info.fdFldr = 0;
322 fprintf( stderr, "Values read by hqx_header_read\n" );
323 fprintf( stderr, "name length\t\t%d\n", namelen );
324 fprintf( stderr, "file name\t\t%s\n", fh->name );
325 fprintf( stderr, "get info comment\t%s\n", fh->comment );
326 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
327 &fh->finder_info.fdType );
328 fprintf( stderr, "creator\t\t\t%.*s\n",
329 sizeof( fh->finder_info.fdCreator ),
330 &fh->finder_info.fdCreator );
331 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
332 flags = ntohs( flags );
333 fprintf( stderr, "flags\t\t\t%x\n", flags );
334 fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
335 fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
336 fprintf( stderr, "header_crc\t\t%x\n", header_crc );
337 fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
338 fprintf( stderr, "\n" );
340 #endif /* DEBUG >= 5 */
343 * create and modify times are figured from right now
346 time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
347 memcpy( &fh->create_date, &time_seconds,
348 sizeof( fh->create_date ));
349 memcpy( &fh->mod_date, &time_seconds,
350 sizeof( fh->mod_date ));
351 fh->backup_date = AD_DATE_START;
354 * stuff that should be zero'ed out
357 fh->comment[0] = '\0';
358 memset( &fh->finder_info.fdLocation, 0,
359 sizeof( fh->finder_info.fdLocation ));
360 memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
362 hqx.forkcrc[ DATA ] = 0;
363 hqx.forkcrc[ RESOURCE ] = 0;
366 if ( header_crc != hqx.headercrc ) {
367 fprintf( stderr, "Bad Header crc, dude :" );
377 int hqx_header_write(struct FHeader *fh _U_)
383 * hqx7_fill is called from skip_junk and hqx_7tobin. it pulls from the
384 * binhqx file into the hqx7 buffer. returns number of bytes read
385 * or a zero for end of file.
386 * it sets the pointers to the hqx7 buffer up to point to the valid data.
389 ssize_t hqx7_fill(u_char *hqx7_ptr)
394 cs = hqx7_ptr - hqx7_buf;
395 if ( cs >= sizeof( hqx7_buf )) return( -1 );
396 hqx7_first = hqx7_ptr;
397 cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
402 hqx7_last = ( hqx7_first + cc );
407 char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
408 0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
410 Input characters are translated to a number between 0 and 63 by direct
411 array lookup. 0xFF signals a bad character. 0xFE is signals a legal
412 character that should be skipped, namely '\n', '\r'. 0xFD signals ':'.
413 0xFC signals a whitespace character.
416 static const u_char hqxlookup[] = {
417 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
418 0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
419 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
420 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
421 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
422 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
423 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
424 0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
425 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
426 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
427 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
428 0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
429 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
430 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
431 0x3D, 0x3E, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
432 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
433 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
434 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
435 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
436 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
437 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
438 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
439 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
440 0xFF, 0xFF, 0xFF, 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,
452 * skip_junk is called from hqx_open. it skips over junk in the file until
453 * it comes to a line containing a valid first line of binhqx encoded file.
454 * returns a 0 for success, negative if it never finds good data.
455 * pass a FIRST when looking for the first valid binhex line, a value of
456 * OTHER when looking for any subsequent line.
459 int skip_junk(int line)
467 if ( line == FIRST ) {
468 if ( hqx7_fill( hqx7_buf ) <= 0 ) {
469 fprintf( stderr, "Premature end of file :" );
474 while ( found == NOWAY ) {
475 if ( line == FIRST ) {
476 if ( *hqx7_first == ':' ) {
480 while (( stopflag == NOWAY ) &&
481 ( nc < ( hqx7_last - hqx7_first ))) {
482 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
487 stopflag = SURETHANG;
494 if (( nc > 30 ) && ( nc < 64 ) &&
495 (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
500 if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
504 while (( stopflag == NOWAY ) &&
505 ( nc < ( hqx7_last - hqx7_first ))) {
506 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
509 if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
515 stopflag = SURETHANG;
525 } else if (( nc > 30 ) && ( c == 0xFE )) {
533 if (( hqx7_last - hqx7_first ) == nc ) {
534 if ( line == FIRST ) {
536 } else *hqx7_buf = '\n';
537 memcpy(hqx7_buf + 1, hqx7_first, nc );
538 hqx7_first = hqx7_buf + ( ++nc );
539 if ( hqx7_fill( hqx7_first ) <= 0 ) {
540 fprintf( stderr, "Premature end of file :" );
543 hqx7_first = hqx7_buf;
551 * hqx_7tobin is used to read the data, converted to binary. It is
552 * called by hqx_header_read to get the header information, and must be
553 * called to get the data for each fork, and the crc data for each
554 * fork. it has the same basic calling structure as unix read. the
555 * number of valid bytes read is returned. It does buffering so as to
556 * return the requested length of data every time, unless the end of
560 size_t hqx_7tobin( char *outbuf, size_t datalen)
562 static u_char hqx8[3];
564 static u_char prev_hqx8;
565 static u_char prev_out;
566 static u_char prev_hqx7;
574 fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
575 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
578 if ( first_flag == 0 ) {
588 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
592 out_last = out_first + datalen;
594 while (( out_first < out_last ) && ( eofflag == 0 )) {
596 if ( hqx7_first == hqx7_last ) {
597 if ( hqx7_fill( hqx7_buf ) == 0 ) {
605 while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
606 hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
607 switch ( hqx7[ hqx7i ] ) {
609 if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
616 while ( hqx7i < 4 ) {
621 prev_hqx7 = hqx7[ hqx7i ];
622 if ( skip_junk( OTHER ) < 0 ) {
623 fprintf( stderr, "\n" );
625 while ( hqx7i < 4 ) {
626 hqx7[ hqx7i++ ] = 0; }
630 prev_hqx7 = hqx7[ hqx7i++ ];
637 hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
638 hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
639 hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
644 while (( hqx8i < 3 ) && ( out_first < out_last )) {
647 putc( hqx8i, rawhex );
648 putc( hqx8[ hqx8i ], rawhex );
649 #endif /* HEXOUTPUT */
651 if ( prev_hqx8 == RUNCHAR ) {
652 if ( hqx8[ hqx8i ] == 0 ) {
653 *out_first = prev_hqx8;
655 putc( *out_first, expandhex );
656 #endif /* HEXOUTPUT */
657 prev_out = prev_hqx8;
660 while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
661 *out_first = prev_out;
663 putc( *out_first, expandhex );
664 #endif /* HEXOUTPUT */
668 if ( hqx8[ hqx8i ] < 2 ) {
669 prev_hqx8 = hqx8[ hqx8i ];
675 prev_hqx8 = hqx8[ hqx8i ];
676 if ( prev_hqx8 != RUNCHAR ) {
677 *out_first = prev_hqx8;
679 putc( *out_first, expandhex );
680 #endif /* HEXOUTPUT */
681 prev_out = prev_hqx8;
689 return( out_first - outbuf );