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 #include <netinet/in.h>
25 #include <atalk/adouble.h>
26 #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
68 FILE *rawhex, *expandhex;
69 #endif /* HEXOUTPUT */
71 static struct hqx_file_data {
72 u_int32_t forklen[ NUMFORKS ];
73 u_short forkcrc[ NUMFORKS ];
74 char path[ MAXPATHLEN + 1];
79 extern char *forkname[];
80 static u_char hqx7_buf[8192];
81 static u_char *hqx7_first;
82 static u_char *hqx7_last;
83 static int first_flag;
86 hqx_open must be called first. pass it a filename that is supposed
87 to contain a binhqx file. an hqx struct will be allocated and
88 somewhat initialized; hqx_fd is set. skip_junk is called from
89 here; skip_junk leaves hqx7_first and hqx7_last set.
92 int hqx_open(char *hqxfile, int flags, struct FHeader *fh, int options)
97 fprintf( stderr, "megatron: entering hqx_open\n" );
99 select_charset( options);
100 if ( flags == O_RDONLY ) {
103 rawhex = fopen( "rawhex.unhex", "w" );
104 expandhex = fopen( "expandhex.unhex", "w" );
105 #endif /* HEXOUTPUT */
109 if ( strcmp( hqxfile, STDIN ) == 0 ) {
110 hqx.filed = fileno( stdin );
111 } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
116 if ( skip_junk( FIRST ) == 0 ) {
117 if ( hqx_header_read( fh ) == 0 ) {
121 pos = lseek( hqx.filed, 0, SEEK_CUR );
122 fprintf( stderr, "megatron: current position is %ld\n", pos );
128 fprintf( stderr, "%s\n", hqxfile );
131 maxlen = sizeof( hqx.path ) -1;
132 strncpy( hqx.path, fh->name, maxlen );
133 strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
134 strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
135 if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
139 if ( hqx_header_write( fh ) != 0 ) {
141 fprintf( stderr, "%s\n", hqx.path );
149 * hqx_close must be called before a second file can be opened using
150 * hqx_open. Upon successful completion, a value of 0 is returned.
151 * Otherwise, a value of -1 is returned.
154 int hqx_close(int keepflag)
156 if ( keepflag == KEEP ) {
157 return( close( hqx.filed ));
158 } else if ( keepflag == TRASH ) {
159 if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
167 * hqx_read is called until it returns zero for each fork. when it is
168 * and finds that there is zero left to give, it reads in and compares
169 * the crc with the calculated one, and returns zero if all is well.
170 * it returns negative is the crc was bad or if has been called too many
171 * times for the same fork. hqx_read must be called enough times to
172 * return zero and no more than that.
175 ssize_t hqx_read(int fork, char *buffer, size_t length)
184 pos = lseek( hqx.filed, 0, SEEK_CUR );
185 fprintf( stderr, "hqx_read: current position is %ld\n", pos );
187 fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
188 fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
189 #endif /* DEBUG >= 3 */
191 if (hqx.forklen[fork] > 0x7FFFFFFF) {
192 fprintf(stderr, "This should never happen, dude!, fork length == %u\n", hqx.forklen[fork]);
196 if ( hqx.forklen[ fork ] == 0 ) {
197 cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
198 if ( cc == sizeof( storedcrc )) {
199 storedcrc = ntohs ( storedcrc );
201 fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
202 fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
203 #endif /* DEBUG >= 4 */
204 if ( storedcrc == hqx.forkcrc[ fork ] ) {
207 fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n",
213 if ( hqx.forklen[ fork ] < length ) {
214 readlen = hqx.forklen[ fork ];
219 fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
220 #endif /* DEBUG >= 3 */
222 cc = hqx_7tobin( buffer, readlen );
224 hqx.forkcrc[ fork ] =
225 updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
226 hqx.forklen[ fork ] -= cc;
229 fprintf( stderr, "hqx_read: chars read is %d\n", cc );
230 #endif /* DEBUG >= 3 */
235 * hqx_header_read is called by hqx_open, and before any information can
236 * read from the hqx_header substruct. it must be called before any
237 * of the bytes of the other two forks can be read, as well.
238 * returns a negative number if it was unable to pull enough information
239 * to fill the hqx_header fields.
242 int hqx_header_read(struct FHeader *fh)
244 char *headerbuf, *headerptr;
245 u_int32_t time_seconds;
252 headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
253 #endif /* HEXOUTPUT */
255 mask = htons( 0xfcee );
258 if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
259 fprintf( stderr, "Premature end of file :" );
262 hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen,
266 write( headerfork, &namelen, sizeof( namelen ));
267 #endif /* HEXOUTPUT */
270 (char *)malloc( (unsigned int)( namelen + BHH_HEADSIZ ))) == NULL ) {
273 if ( hqx_7tobin( headerbuf, ( namelen + BHH_HEADSIZ )) == 0 ) {
275 fprintf( stderr, "Premature end of file :" );
278 headerptr = headerbuf;
279 hqx.headercrc = updcrc( hqx.headercrc,
280 (u_char *)headerbuf, ( namelen + BHH_HEADSIZ - BHH_CRCSIZ ));
283 write( headerfork, headerbuf, ( namelen + BHH_HEADSIZ ));
284 #endif /* HEXOUTPUT */
287 * stuff from the hqx file header
290 memcpy( fh->name, headerptr, (int)namelen );
291 headerptr += namelen;
292 headerptr += BHH_VERSION;
293 memcpy(&fh->finder_info, headerptr, BHH_TCSIZ );
294 headerptr += BHH_TCSIZ;
295 memcpy(&fh->finder_info.fdFlags, headerptr, BHH_FLAGSIZ );
296 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
297 headerptr += BHH_FLAGSIZ;
298 memcpy(&fh->forklen[ DATA ], headerptr, BHH_DATASIZ );
299 hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
300 headerptr += BHH_DATASIZ;
301 memcpy( &fh->forklen[ RESOURCE ], headerptr, BHH_RESSIZ );
302 hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
303 headerptr += BHH_RESSIZ;
304 memcpy(&header_crc, headerptr, BHH_CRCSIZ );
305 headerptr += BHH_CRCSIZ;
306 header_crc = ntohs( header_crc );
309 * stuff that should be zero'ed out
312 fh->comment[0] = '\0';
313 fh->finder_info.fdLocation = 0;
314 fh->finder_info.fdFldr = 0;
320 fprintf( stderr, "Values read by hqx_header_read\n" );
321 fprintf( stderr, "name length\t\t%d\n", namelen );
322 fprintf( stderr, "file name\t\t%s\n", fh->name );
323 fprintf( stderr, "get info comment\t%s\n", fh->comment );
324 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
325 &fh->finder_info.fdType );
326 fprintf( stderr, "creator\t\t\t%.*s\n",
327 sizeof( fh->finder_info.fdCreator ),
328 &fh->finder_info.fdCreator );
329 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
330 flags = ntohs( flags );
331 fprintf( stderr, "flags\t\t\t%x\n", flags );
332 fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
333 fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
334 fprintf( stderr, "header_crc\t\t%x\n", header_crc );
335 fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
336 fprintf( stderr, "\n" );
338 #endif /* DEBUG >= 5 */
341 * create and modify times are figured from right now
344 time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
345 memcpy( &fh->create_date, &time_seconds,
346 sizeof( fh->create_date ));
347 memcpy( &fh->mod_date, &time_seconds,
348 sizeof( fh->mod_date ));
349 fh->backup_date = AD_DATE_START;
352 * stuff that should be zero'ed out
355 fh->comment[0] = '\0';
356 memset( &fh->finder_info.fdLocation, 0,
357 sizeof( fh->finder_info.fdLocation ));
358 memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
360 hqx.forkcrc[ DATA ] = 0;
361 hqx.forkcrc[ RESOURCE ] = 0;
364 if ( header_crc != hqx.headercrc ) {
365 fprintf( stderr, "Bad Header crc, dude :" );
375 int hqx_header_write(struct FHeader *fh _U_)
381 * hqx7_fill is called from skip_junk and hqx_7tobin. it pulls from the
382 * binhqx file into the hqx7 buffer. returns number of bytes read
383 * or a zero for end of file.
384 * it sets the pointers to the hqx7 buffer up to point to the valid data.
387 ssize_t hqx7_fill(u_char *hqx7_ptr)
392 cs = hqx7_ptr - hqx7_buf;
393 if ( cs >= sizeof( hqx7_buf )) return( -1 );
394 hqx7_first = hqx7_ptr;
395 cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
400 hqx7_last = ( hqx7_first + cc );
405 char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
406 0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
408 Input characters are translated to a number between 0 and 63 by direct
409 array lookup. 0xFF signals a bad character. 0xFE is signals a legal
410 character that should be skipped, namely '\n', '\r'. 0xFD signals ':'.
411 0xFC signals a whitespace character.
414 static const u_char hqxlookup[] = {
415 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
416 0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
417 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
418 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
419 0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
420 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
421 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
422 0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
423 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
424 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
425 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
426 0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
427 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
428 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
429 0x3D, 0x3E, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
430 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
431 0xFF, 0xFF, 0xFF, 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,
450 * skip_junk is called from hqx_open. it skips over junk in the file until
451 * it comes to a line containing a valid first line of binhqx encoded file.
452 * returns a 0 for success, negative if it never finds good data.
453 * pass a FIRST when looking for the first valid binhex line, a value of
454 * OTHER when looking for any subsequent line.
457 int skip_junk(int line)
465 if ( line == FIRST ) {
466 if ( hqx7_fill( hqx7_buf ) <= 0 ) {
467 fprintf( stderr, "Premature end of file :" );
472 while ( found == NOWAY ) {
473 if ( line == FIRST ) {
474 if ( *hqx7_first == ':' ) {
478 while (( stopflag == NOWAY ) &&
479 ( nc < ( hqx7_last - hqx7_first ))) {
480 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
485 stopflag = SURETHANG;
492 if (( nc > 30 ) && ( nc < 64 ) &&
493 (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
498 if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
502 while (( stopflag == NOWAY ) &&
503 ( nc < ( hqx7_last - hqx7_first ))) {
504 switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
507 if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
513 stopflag = SURETHANG;
523 } else if (( nc > 30 ) && ( c == 0xFE )) {
531 if (( hqx7_last - hqx7_first ) == nc ) {
532 if ( line == FIRST ) {
534 } else *hqx7_buf = '\n';
535 memcpy(hqx7_buf + 1, hqx7_first, nc );
536 hqx7_first = hqx7_buf + ( ++nc );
537 if ( hqx7_fill( hqx7_first ) <= 0 ) {
538 fprintf( stderr, "Premature end of file :" );
541 hqx7_first = hqx7_buf;
549 * hqx_7tobin is used to read the data, converted to binary. It is
550 * called by hqx_header_read to get the header information, and must be
551 * called to get the data for each fork, and the crc data for each
552 * fork. it has the same basic calling structure as unix read. the
553 * number of valid bytes read is returned. It does buffering so as to
554 * return the requested length of data every time, unless the end of
558 size_t hqx_7tobin( char *outbuf, size_t datalen)
560 static u_char hqx8[3];
562 static u_char prev_hqx8;
563 static u_char prev_out;
564 static u_char prev_hqx7;
572 fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
573 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
576 if ( first_flag == 0 ) {
586 fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
590 out_last = out_first + datalen;
592 while (( out_first < out_last ) && ( eofflag == 0 )) {
594 if ( hqx7_first == hqx7_last ) {
595 if ( hqx7_fill( hqx7_buf ) == 0 ) {
603 while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
604 hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
605 switch ( hqx7[ hqx7i ] ) {
607 if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
614 while ( hqx7i < 4 ) {
619 prev_hqx7 = hqx7[ hqx7i ];
620 if ( skip_junk( OTHER ) < 0 ) {
621 fprintf( stderr, "\n" );
623 while ( hqx7i < 4 ) {
624 hqx7[ hqx7i++ ] = 0; }
628 prev_hqx7 = hqx7[ hqx7i++ ];
635 hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
636 hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
637 hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
642 while (( hqx8i < 3 ) && ( out_first < out_last )) {
645 putc( hqx8i, rawhex );
646 putc( hqx8[ hqx8i ], rawhex );
647 #endif /* HEXOUTPUT */
649 if ( prev_hqx8 == RUNCHAR ) {
650 if ( hqx8[ hqx8i ] == 0 ) {
651 *out_first = prev_hqx8;
653 putc( *out_first, expandhex );
654 #endif /* HEXOUTPUT */
655 prev_out = prev_hqx8;
658 while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
659 *out_first = prev_out;
661 putc( *out_first, expandhex );
662 #endif /* HEXOUTPUT */
666 if ( hqx8[ hqx8i ] < 2 ) {
667 prev_hqx8 = hqx8[ hqx8i ];
673 prev_hqx8 = hqx8[ hqx8i ];
674 if ( prev_hqx8 != RUNCHAR ) {
675 *out_first = prev_hqx8;
677 putc( *out_first, expandhex );
678 #endif /* HEXOUTPUT */
679 prev_out = prev_hqx8;
687 return( out_first - outbuf );