2
* $Id: macbin.c,v 1.10 2002/02/16 17:12:53 srittau Exp $
7
#endif /* HAVE_CONFIG_H */
12
#include <sys/param.h>
15
#endif /* HAVE_FCNTL_H */
18
#endif /* HAVE_UNISTD_H */
25
#include <atalk/adouble.h>
26
#include <netatalk/endian.h>
30
/* This allows megatron to generate .bin files that won't choke other
31
well-known converter apps. It also makes sure that checksums
32
always match. (RLB) */
33
#define MACBINARY_PLAY_NICE_WITH_OTHERS
35
/* String used to indicate standard input instead of a disk
36
file. Should be a string not normally used for a file
47
/* Size of a macbinary file header
49
#define HEADBUFSIZ 128
51
/* Both input and output routines use this struct and the
52
following globals; therefore this module can only be used
53
for one of the two functions at a time.
55
struct bin_file_data {
56
u_int32_t forklen[ NUMFORKS ];
57
char path[ MAXPATHLEN + 1];
60
time_t gmtoff; /* to convert from/to localtime */
63
extern char *forkname[];
64
u_char head_buf[HEADBUFSIZ];
67
* bin_open must be called first. pass it a filename that is supposed
68
* to contain a macbinary file. an bin struct will be allocated and
69
* somewhat initialized; bin_filed is set.
72
int bin_open( binfile, flags, fh, options )
83
fprintf( stderr, "entering bin_open\n" );
86
/* call localtime so that we get the timezone offset */
88
#ifndef NO_STRUCT_TM_GMTOFF
92
bin.gmtoff = tp->tm_gmtoff;
93
#endif /* ! NO_STRUCT_TM_GMTOFF */
95
if ( flags == O_RDONLY ) { /* input */
96
if ( strcmp( binfile, STDIN ) == 0 ) {
97
bin.filed = fileno( stdin );
98
} else if (( bin.filed = open( binfile, flags )) < 0 ) {
103
fprintf( stderr, "opened %s for read\n", binfile );
105
if ((( rc = test_header() ) > 0 ) &&
106
( bin_header_read( fh, rc ) == 0 )) {
109
fprintf( stderr, "%s is not a macbinary file.\n", binfile );
111
} else { /* output */
112
if (options & OPTION_STDOUT)
113
bin.filed = fileno(stdout);
115
maxlen = sizeof( bin.path ) - 1;
117
fprintf( stderr, "sizeof bin.path\t\t\t%d\n", sizeof( bin.path ));
118
fprintf( stderr, "maxlen \t\t\t\t%d\n", maxlen );
120
strncpy( bin.path, fh->name, maxlen );
121
strncpy( bin.path, mtoupath( bin.path ), maxlen );
122
strncat( bin.path, ".bin", maxlen - strlen( bin.path ));
123
if (( bin.filed = open( bin.path, flags, 0666 )) < 0 ) {
128
fprintf( stderr, "opened %s for write\n",
129
(options & OPTION_STDOUT) ? "(stdout)" : bin.path );
133
if ( bin_header_write( fh ) != 0 ) {
135
fprintf( stderr, "%s\n", bin.path );
143
* bin_close must be called before a second file can be opened using
144
* bin_open. Upon successful completion, a value of 0 is returned.
145
* Otherwise, a value of -1 is returned.
148
int bin_close( keepflag )
152
fprintf( stderr, "entering bin_close\n" );
154
if ( keepflag == KEEP ) {
155
return( close( bin.filed ));
156
} else if ( keepflag == TRASH ) {
157
if (( strcmp( bin.path, STDIN ) != 0 ) &&
158
( unlink( bin.path ) < 0 )) {
166
* bin_read is called until it returns zero for each fork. when it is
167
* and finds that there is zero left to give, it seeks to the position
168
* of the next fork (if there is one ).
169
* bin_read must be called enough times to
170
* return zero and no more than that.
173
int bin_read( fork, buffer, length )
184
fprintf( stderr, "bin_read: fork is %s\n", forkname[ fork ] );
185
fprintf( stderr, "bin_read: remaining length is %d\n", bin.forklen[fork] );
186
#endif /* DEBUG >= 3 */
188
if ( bin.forklen[ fork ] < 0 ) {
189
fprintf( stderr, "This should never happen, dude!\n" );
190
return( bin.forklen[ fork ] );
193
if ( bin.forklen[ fork ] == 0 ) {
194
if ( fork == DATA ) {
195
pos = lseek( bin.filed, 0, SEEK_CUR );
197
fprintf( stderr, "current position is %ld\n", pos );
201
pos = lseek( bin.filed, HEADBUFSIZ - pos, SEEK_CUR );
204
fprintf( stderr, "current position is %ld\n", pos );
210
if ( bin.forklen[ fork ] < length ) {
211
readlen = bin.forklen[ fork ];
216
fprintf( stderr, "bin_read: readlen is %d\n", readlen );
217
fprintf( stderr, "bin_read: cc is %d\n", cc );
218
#endif /* DEBUG >= 3 */
221
while (( readlen > 0 ) && ( cc > 0 )) {
222
if (( cc = read( bin.filed, buf_ptr, readlen )) > 0 ) {
224
fprintf( stderr, "bin_read: cc is %d\n", cc );
225
#endif /* DEBUG >= 3 */
231
cc = buf_ptr - buffer;
232
bin.forklen[ fork ] -= cc;
236
fprintf( stderr, "bin_read: chars read is %d\n", cc );
237
#endif /* DEBUG >= 3 */
245
int bin_write( fork, buffer, length )
254
u_char padchar = 0x7f;
255
/* Not sure why, but it seems this must be 0x7f to match
256
other converters, not 0. (RLB) */
259
fprintf( stderr, "bin_write: fork is %s\n", forkname[ fork ] );
260
fprintf( stderr, "bin_write: remaining length is %d\n", bin.forklen[fork] );
261
#endif /* DEBUG >= 3 */
263
if (( fork == RESOURCE ) && ( bin.forklen[ DATA ] != 0 )) {
264
fprintf( stderr, "Forklength error.\n" );
268
buf_ptr = (char *)buffer;
269
if ( bin.forklen[ fork ] >= length ) {
272
fprintf( stderr, "Forklength error.\n" );
277
fprintf( stderr, "bin_write: write length is %d\n", writelen );
278
#endif /* DEBUG >= 3 */
280
while (( writelen > 0 ) && ( cc >= 0 )) {
281
cc = write( bin.filed, buf_ptr, writelen );
286
perror( "Couldn't write to macbinary file:" );
289
bin.forklen[ fork ] -= length;
291
if ( bin.forklen[ fork ] < 0 ) {
292
fprintf( stderr, "This should never happen, dude!\n" );
293
return( bin.forklen[ fork ] );
297
* add the padding at end of data and resource forks
300
if ( bin.forklen[ fork ] == 0 ) {
301
pos = lseek( bin.filed, 0, SEEK_CUR );
303
fprintf( stderr, "current position is %ld\n", pos );
306
if (pos != 0) { /* pad only if we need to */
307
pos = lseek( bin.filed, HEADBUFSIZ - pos - 1, SEEK_CUR );
308
if ( write( bin.filed, &padchar, 1 ) != 1 ) {
309
perror( "Couldn't write to macbinary file:" );
314
fprintf( stderr, "current position is %ld\n", pos );
319
fprintf( stderr, "\n" );
326
* bin_header_read is called by bin_open, and before any information can
327
* read from the fh substruct. it must be called before any
328
* of the bytes of the other two forks can be read, as well.
331
int bin_header_read( fh, revision )
338
* Set the appropriate finder flags mask for the type of macbinary
339
* file it is, and copy the extra macbinary II stuff from the header.
340
* If it is not a macbinary file revision of I or II, then return
344
switch ( revision ) {
347
mask = htons( 0xfcee );
348
memcpy(&fh->finder_info.fdFlags + 1, head_buf + 101,1 );
351
mask = htons( 0xfc00 );
359
* Go through and copy all the stuff you can get from the
360
* MacBinary header into the fh struct. What fun!
363
memcpy(fh->name, head_buf + 2, head_buf[ 1 ] );
364
memcpy(&fh->create_date, head_buf + 91, 4 );
365
fh->create_date = MAC_DATE_TO_UNIX(fh->create_date) - bin.gmtoff;
366
fh->create_date = AD_DATE_FROM_UNIX(fh->create_date);
367
memcpy( &fh->mod_date, head_buf + 95, 4 );
368
fh->mod_date = MAC_DATE_TO_UNIX(fh->mod_date) - bin.gmtoff;
369
fh->mod_date = AD_DATE_FROM_UNIX(fh->mod_date);
370
fh->backup_date = AD_DATE_START;
371
memcpy( &fh->finder_info, head_buf + 65, 8 );
373
#ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */
374
memcpy( &fh->finder_info.fdFlags, head_buf + 73, 1 );
375
fh->finder_info.fdFlags &= mask;
376
#else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
377
memcpy( &fh->finder_info.fdFlags, head_buf + 73, 2 );
378
#endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
380
memcpy(&fh->finder_info.fdLocation, head_buf + 75, 4 );
381
memcpy(&fh->finder_info.fdFldr, head_buf + 79, 2 );
382
memcpy(&fh->forklen[ DATA ], head_buf + 83, 4 );
383
bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
384
memcpy(&fh->forklen[ RESOURCE ], head_buf + 87, 4 );
385
bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
386
fh->comment[0] = '\0';
389
fh->finder_xinfo.fdScript = *(head_buf + 106);
390
fh->finder_xinfo.fdXFlags = *(head_buf + 107);
398
fprintf( stderr, "Values read by bin_header_read\n" );
399
fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
400
fprintf( stderr, "file name\t\t%s\n", fh->name );
401
fprintf( stderr, "get info comment\t%s\n", fh->comment );
402
fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
403
&fh->finder_info.fdType );
404
fprintf( stderr, "creator\t\t\t%.*s\n",
405
sizeof( fh->finder_info.fdCreator ),
406
&fh->finder_info.fdCreator );
407
memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
408
flags = ntohs( flags );
409
fprintf( stderr, "flags\t\t\t%x\n", flags );
411
/* Show fdLocation too (RLB) */
412
memcpy( &flags_long, &fh->finder_info.fdLocation,
413
sizeof( flags_long ));
414
flags_long = ntohl( flags_long );
415
fprintf( stderr, "location flags\t\t%lx\n", flags_long );
417
fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
418
fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
419
fprintf( stderr, "\n" );
421
#endif /* DEBUG >= 5 */
427
* bin_header_write is called by bin_open, and relies on information
428
* from the fh substruct. it must be called before any
429
* of the bytes of the other two forks can be written, as well.
430
* bin_header_write and bin_header_read are opposites.
433
int bin_header_write( fh )
441
memset(head_buf, 0, sizeof( head_buf ));
442
head_buf[ 1 ] = (u_char)strlen( fh->name );
443
memcpy( head_buf + 2, fh->name, head_buf[ 1 ] );
444
memcpy( head_buf + 65, &fh->finder_info, 8 );
446
#ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */
447
memcpy( head_buf + 73, &fh->finder_info.fdFlags, 1 );
448
#else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
449
memcpy( head_buf + 73, &fh->finder_info.fdFlags, 2 );
450
#endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
452
memcpy( head_buf + 75, &fh->finder_info.fdLocation, 4 );
453
memcpy( head_buf + 79, &fh->finder_info.fdFldr, 2 );
454
memcpy( head_buf + 83, &fh->forklen[ DATA ], 4 );
455
memcpy( head_buf + 87, &fh->forklen[ RESOURCE ], 4 );
456
t = AD_DATE_TO_UNIX(fh->create_date) + bin.gmtoff;
457
t = MAC_DATE_FROM_UNIX(t);
458
memcpy( head_buf + 91, &t, sizeof(t) );
459
t = AD_DATE_TO_UNIX(fh->mod_date) + bin.gmtoff;
460
t = MAC_DATE_FROM_UNIX(t);
461
memcpy( head_buf + 95, &t, sizeof(t) );
462
memcpy( head_buf + 101, &fh->finder_info.fdFlags + 1, 1);
465
memcpy( head_buf + 102, "mBIN", 4);
466
*(head_buf + 106) = fh->finder_xinfo.fdScript;
467
*(head_buf + 107) = fh->finder_xinfo.fdXFlags;
468
head_buf[ 122 ] = 130;
470
head_buf[ 123 ] = 129;
472
bin.headercrc = htons( updcrc( (u_short) 0, head_buf, 124 ));
473
memcpy(head_buf + 124, &bin.headercrc, sizeof( bin.headercrc ));
475
bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
476
bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
483
fprintf( stderr, "Values written by bin_header_write\n" );
484
fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
485
fprintf( stderr, "file name\t\t%s\n", (char *)&head_buf[ 2 ] );
486
fprintf( stderr, "type\t\t\t%.4s\n", (char *)&head_buf[ 65 ] );
487
fprintf( stderr, "creator\t\t\t%.4s\n", (char *)&head_buf[ 69 ] );
489
memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
490
flags = ntohs( flags );
491
fprintf( stderr, "flags\t\t\t%x\n", flags );
493
/* Show fdLocation too (RLB) */
494
memcpy( &flags_long, &fh->finder_info.fdLocation,
495
sizeof( flags_long ));
496
flags_long = ntohl( flags_long );
497
fprintf( stderr, "location flags\t\t%ldx\n", flags_long );
499
fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
500
fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
501
fprintf( stderr, "\n" );
503
#endif /* DEBUG >= 5 */
505
write_ptr = (char *)head_buf;
506
wc = sizeof( head_buf );
508
while (( wc > 0 ) && ( wr >= 0 )) {
509
wr = write( bin.filed, write_ptr, wc );
514
perror( "Couldn't write macbinary header:" );
522
* test_header is called from bin_open. it checks certain values of
523
* the first 128 bytes, determines if the file is a MacBinary,
524
* MacBinary II, MacBinary III, or non-MacBinary file, and returns a
525
* one, two, three or negative one to indicate the file type.
527
* If the signature at 102 is equal to "mBIN," then it's a MacBinary
528
* III file. Bytes 0 and 74 must be zero for the file to be any type
529
* of MacBinary. If the crc of bytes 0 through 123 equals the value
530
* at offset 124 then it is a MacBinary II. If not, then if byte 82
531
* is zero, byte 2 is a valid value for a mac filename length (between
532
* one and sixty-three), and bytes 101 through 125 are all zero, then
533
* the file is a MacBinary.
535
* NOTE: apple's MacBinary II files have a non-zero value at byte 74.
536
* so, the check for byte 74 isn't very useful.
539
int test_header(void)
541
const char zeros[25] = "";
547
fprintf( stderr, "entering test_header\n" );
550
cc = read( bin.filed, (char *)head_buf, sizeof( head_buf ));
551
if ( cc < sizeof( head_buf )) {
552
perror( "Premature end of file :" );
557
fprintf( stderr, "was able to read HEADBUFSIZ bytes\n" );
560
/* check for macbinary III header */
561
if (memcmp(head_buf + 102, "mBIN", 4) == 0)
564
/* check for macbinary II even if only one of the bytes is zero */
565
if (( head_buf[ 0 ] == 0 ) || ( head_buf[ 74 ] == 0 )) {
567
fprintf( stderr, "byte 0 and 74 are both zero\n" );
569
bin.headercrc = updcrc( (u_short) 0, head_buf, 124 );
570
memcpy(&header_crc, head_buf + 124, sizeof( header_crc ));
571
header_crc = ntohs( header_crc );
572
if ( header_crc == bin.headercrc ) {
577
fprintf( stderr, "header crc didn't pan out\n" );
581
/* now see if we have a macbinary file. */
582
if ( head_buf[ 82 ] != 0 ) {
585
memcpy( &namelen, head_buf + 1, sizeof( namelen ));
587
fprintf( stderr, "name length is %d\n", namelen );
589
if (( namelen < 1 ) || ( namelen > 63 )) {
593
/* bytes 101 - 125 should be zero */
594
if (memcmp(head_buf + 101, zeros, sizeof(zeros)) != 0)
597
/* macbinary forks aren't larger than 0x7FFFFF */
598
memcpy(&cc, head_buf + 83, sizeof(cc));
602
memcpy(&cc, head_buf + 87, sizeof(cc));
609
fprintf( stderr, "byte 82 is zero and name length is cool\n" );