]> arthur.barton.de Git - netatalk.git/blob - bin/megatron/macbin.c
massive commenting/autoconf changes
[netatalk.git] / bin / megatron / macbin.c
1 /*
2  * $Id: macbin.c,v 1.8 2001-06-29 14:14:46 rufustfirefly Exp $
3  */
4
5 #ifdef HAVE_CONFIG_H
6 #include "config.h"
7 #endif /* HAVE_CONFIG_H */
8
9 #include <sys/types.h>
10 #include <sys/uio.h>
11 #include <sys/time.h>
12 #include <sys/param.h>
13 #ifdef HAVE_FCNTL_H
14 #include <fcntl.h>
15 #endif /* HAVE_FCNTL_H */
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif /* HAVE_UNISTD_H */
19 #include <string.h>
20 #include <strings.h>
21 #include <syslog.h>
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <time.h>
25
26 #include <atalk/adouble.h>
27 #include <netatalk/endian.h>
28 #include "megatron.h"
29 #include "macbin.h"
30
31 /* This allows megatron to generate .bin files that won't choke other
32    well-known converter apps. It also makes sure that checksums
33    always match. (RLB) */
34 #define MACBINARY_PLAY_NICE_WITH_OTHERS
35
36 /*      String used to indicate standard input instead of a disk
37         file.  Should be a string not normally used for a file
38  */
39 #ifndef STDIN
40 #       define  STDIN   "-"
41 #endif /* STDIN */
42
43 /*      Yes and no
44  */
45 #define NOWAY           0
46 #define SURETHANG       1
47
48 /*      Size of a macbinary file header
49  */
50 #define HEADBUFSIZ      128
51
52 /*      Both input and output routines use this struct and the
53         following globals; therefore this module can only be used
54         for one of the two functions at a time.
55  */
56 struct bin_file_data {
57     u_int32_t           forklen[ NUMFORKS ];
58     char                path[ MAXPATHLEN + 1];
59     int                 filed;
60     u_short             headercrc;
61     time_t              gmtoff; /* to convert from/to localtime */
62 }               bin;
63
64 extern char     *forkname[];
65 u_char          head_buf[HEADBUFSIZ];
66
67 /* 
68  * bin_open must be called first.  pass it a filename that is supposed
69  * to contain a macbinary file.  an bin struct will be allocated and
70  * somewhat initialized; bin_filed is set.
71  */
72
73 int bin_open( binfile, flags, fh, options )
74     char                *binfile;
75     int                 flags, options;
76     struct FHeader      *fh;
77 {
78     int                 maxlen;
79     int                 rc;
80     time_t              t;
81     struct tm           *tp;
82
83 #if DEBUG
84     fprintf( stderr, "entering bin_open\n" );
85 #endif /* DEBUG */
86
87     /* call localtime so that we get the timezone offset */
88     bin.gmtoff = 0;
89 #ifndef NO_STRUCT_TM_GMTOFF
90     time(&t);
91     tp = localtime(&t);
92     if (tp)
93         bin.gmtoff = tp->tm_gmtoff;
94 #endif /* ! NO_STRUCT_TM_GMTOFF */
95
96     if ( flags == O_RDONLY ) { /* input */
97         if ( strcmp( binfile, STDIN ) == 0 ) {
98             bin.filed = fileno( stdin );
99         } else if (( bin.filed = open( binfile, flags )) < 0 ) {
100             perror( binfile );
101             return( -1 );
102         }
103 #if DEBUG
104         fprintf( stderr, "opened %s for read\n", binfile );
105 #endif /* DEBUG */
106         if ((( rc = test_header() ) > 0 ) && 
107                 ( bin_header_read( fh, rc ) == 0 )) {
108             return( 0 );
109         }
110         fprintf( stderr, "%s is not a macbinary file.\n", binfile );
111         return( -1 );
112     } else { /* output */
113         if (options & OPTION_STDOUT) 
114           bin.filed = fileno(stdout);
115         else {
116           maxlen = sizeof( bin.path ) - 1;
117 #if DEBUG
118           fprintf( stderr, "sizeof bin.path\t\t\t%d\n", sizeof( bin.path ));
119           fprintf( stderr, "maxlen \t\t\t\t%d\n", maxlen );
120 #endif /* DEBUG */
121           strncpy( bin.path, fh->name, maxlen );
122           strncpy( bin.path, mtoupath( bin.path ), maxlen );
123           strncat( bin.path, ".bin", maxlen - strlen( bin.path ));
124           if (( bin.filed = open( bin.path, flags, 0666 )) < 0 ) {
125             perror( bin.path );
126             return( -1 );
127           }
128 #if DEBUG
129           fprintf( stderr, "opened %s for write\n", 
130                    (options & OPTION_STDOUT) ? "(stdout)" : bin.path );
131 #endif /* DEBUG */
132         }
133
134         if ( bin_header_write( fh ) != 0 ) {
135             bin_close( TRASH );
136             fprintf( stderr, "%s\n", bin.path );
137             return( -1 );
138         }
139         return( 0 );
140     }
141 }
142
143 /* 
144  * bin_close must be called before a second file can be opened using
145  * bin_open.  Upon successful completion, a value of 0 is returned.  
146  * Otherwise, a value of -1 is returned.
147  */
148
149 int bin_close( keepflag )
150     int                 keepflag;
151 {
152 #if DEBUG
153     fprintf( stderr, "entering bin_close\n" );
154 #endif /* DEBUG */
155     if ( keepflag == KEEP ) {
156         return( close( bin.filed ));
157     } else if ( keepflag == TRASH ) {
158         if (( strcmp( bin.path, STDIN ) != 0 ) && 
159                 ( unlink( bin.path ) < 0 )) {
160             perror ( bin.path );
161         }
162         return( 0 );
163     } else return( -1 );
164 }
165
166 /*
167  * bin_read is called until it returns zero for each fork.  when it is
168  * and finds that there is zero left to give, it seeks to the position
169  * of the next fork (if there is one ).
170  * bin_read must be called enough times to
171  * return zero and no more than that.
172  */
173
174 int bin_read( fork, buffer, length )
175     int                 fork;
176     char                *buffer;
177     int                 length;
178 {
179     char                *buf_ptr;
180     int                 readlen;
181     int                 cc = 1;
182     off_t               pos;
183
184 #if DEBUG >= 3
185     fprintf( stderr, "bin_read: fork is %s\n", forkname[ fork ] );
186     fprintf( stderr, "bin_read: remaining length is %d\n", bin.forklen[fork] );
187 #endif /* DEBUG >= 3 */
188
189     if ( bin.forklen[ fork ] < 0 ) {
190         fprintf( stderr, "This should never happen, dude!\n" );
191         return( bin.forklen[ fork ] );
192     }
193
194     if ( bin.forklen[ fork ] == 0 ) {
195         if ( fork == DATA ) {
196             pos = lseek( bin.filed, 0, SEEK_CUR );
197 #if DEBUG
198             fprintf( stderr, "current position is %ld\n", pos );
199 #endif /* DEBUG */
200             pos %= HEADBUFSIZ;
201             if (pos != 0) {
202               pos = lseek( bin.filed, HEADBUFSIZ - pos, SEEK_CUR );
203             }
204 #if DEBUG
205             fprintf( stderr, "current position is %ld\n", pos );
206 #endif /* DEBUG */
207         }
208         return( 0 );
209     }
210
211     if ( bin.forklen[ fork ] < length ) {
212         readlen = bin.forklen[ fork ];
213     } else {
214         readlen = length;
215     }
216 #if DEBUG >= 3
217     fprintf( stderr, "bin_read: readlen is %d\n", readlen );
218     fprintf( stderr, "bin_read: cc is %d\n", cc );
219 #endif /* DEBUG >= 3 */
220
221     buf_ptr = buffer;
222     while (( readlen > 0 ) && ( cc > 0 )) {
223         if (( cc = read( bin.filed, buf_ptr, readlen )) > 0 ) {
224 #if DEBUG >= 3
225             fprintf( stderr, "bin_read: cc is %d\n", cc );
226 #endif /* DEBUG >= 3 */
227             readlen -= cc;
228             buf_ptr += cc;
229         }
230     }
231     if ( cc >= 0 ) {
232         cc = buf_ptr - buffer;
233         bin.forklen[ fork ] -= cc;
234     }
235
236 #if DEBUG >= 3
237     fprintf( stderr, "bin_read: chars read is %d\n", cc );
238 #endif /* DEBUG >= 3 */
239     return( cc );
240 }
241
242 /*
243  * bin_write 
244  */
245
246 int bin_write( fork, buffer, length )
247     int                 fork;
248     char                *buffer;
249     int                 length;
250 {
251     char                *buf_ptr;
252     int                 writelen;
253     int                 cc = 0;
254     off_t               pos;
255     u_char              padchar = 0x7f;
256                 /* Not sure why, but it seems this must be 0x7f to match
257                    other converters, not 0. (RLB) */
258
259 #if DEBUG >= 3
260     fprintf( stderr, "bin_write: fork is %s\n", forkname[ fork ] );
261     fprintf( stderr, "bin_write: remaining length is %d\n", bin.forklen[fork] );
262 #endif /* DEBUG >= 3 */
263
264     if (( fork == RESOURCE ) && ( bin.forklen[ DATA ] != 0 )) {
265         fprintf( stderr, "Forklength error.\n" );
266         return( -1 );
267     }
268
269     buf_ptr = (char *)buffer;
270     if ( bin.forklen[ fork ] >= length ) {
271         writelen = length;
272     } else {
273         fprintf( stderr, "Forklength error.\n" );
274         return( -1 );
275     }
276
277 #if DEBUG >= 3
278     fprintf( stderr, "bin_write: write length is %d\n", writelen );
279 #endif /* DEBUG >= 3 */
280
281     while (( writelen > 0 ) && ( cc >= 0 )) {
282         cc = write( bin.filed, buf_ptr, writelen );
283         buf_ptr += cc;
284         writelen -= cc;
285     }
286     if ( cc < 0 ) {
287         perror( "Couldn't write to macbinary file:" );
288         return( cc );
289     }
290     bin.forklen[ fork ] -= length;
291
292     if ( bin.forklen[ fork ] < 0 ) {
293         fprintf( stderr, "This should never happen, dude!\n" );
294         return( bin.forklen[ fork ] );
295     }
296
297 /*
298  * add the padding at end of data and resource forks
299  */
300
301     if ( bin.forklen[ fork ] == 0 ) {
302         pos = lseek( bin.filed, 0, SEEK_CUR );
303 #if DEBUG
304         fprintf( stderr, "current position is %ld\n", pos );
305 #endif /* DEBUG */
306         pos %= HEADBUFSIZ;
307         if (pos != 0) { /* pad only if we need to */
308           pos = lseek( bin.filed, HEADBUFSIZ - pos - 1, SEEK_CUR );
309           if ( write( bin.filed, &padchar, 1 ) != 1 ) {
310             perror( "Couldn't write to macbinary file:" );
311             return( -1 );
312           }
313         }
314 #if DEBUG
315           fprintf( stderr, "current position is %ld\n", pos );
316 #endif /* DEBUG */
317     }
318
319 #if DEBUG
320         fprintf( stderr, "\n" );
321 #endif /* DEBUG */
322
323     return( length );
324 }
325
326 /* 
327  * bin_header_read is called by bin_open, and before any information can
328  * read from the fh substruct.  it must be called before any
329  * of the bytes of the other two forks can be read, as well.
330  */
331
332 int bin_header_read( fh, revision )
333     struct FHeader      *fh;
334     int                 revision;
335 {
336     u_short             mask;
337
338 /*
339  * Set the appropriate finder flags mask for the type of macbinary
340  * file it is, and copy the extra macbinary II stuff from the header.
341  * If it is not a macbinary file revision of I or II, then return
342  * negative.
343  */
344
345     switch ( revision ) {
346         case 3:
347         case 2 :
348             mask = htons( 0xfcee );
349             memcpy(&fh->finder_info.fdFlags + 1, head_buf + 101,1 );
350             break;
351         case 1 :
352             mask = htons( 0xfc00 );
353             break;
354         default :
355             return( -1 );
356             break;
357     }
358
359 /*
360  * Go through and copy all the stuff you can get from the 
361  * MacBinary header into the fh struct.  What fun!
362  */
363
364     memcpy(fh->name, head_buf +  2, head_buf[ 1 ] );
365     memcpy(&fh->create_date, head_buf +  91, 4 );
366     fh->create_date = MAC_DATE_TO_UNIX(fh->create_date) - bin.gmtoff;
367     fh->create_date = AD_DATE_FROM_UNIX(fh->create_date);
368     memcpy( &fh->mod_date, head_buf +  95, 4 );
369     fh->mod_date = MAC_DATE_TO_UNIX(fh->mod_date) - bin.gmtoff;
370     fh->mod_date = AD_DATE_FROM_UNIX(fh->mod_date);
371     fh->backup_date = AD_DATE_START;
372     memcpy( &fh->finder_info, head_buf +  65, 8 );
373
374 #ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */
375     memcpy( &fh->finder_info.fdFlags, head_buf + 73, 1 );
376     fh->finder_info.fdFlags &= mask;
377 #else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
378         memcpy( &fh->finder_info.fdFlags, head_buf + 73, 2 );
379 #endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
380
381     memcpy(&fh->finder_info.fdLocation, head_buf + 75, 4 );
382     memcpy(&fh->finder_info.fdFldr, head_buf +  79, 2 );
383     memcpy(&fh->forklen[ DATA ],  head_buf + 83, 4 );
384     bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
385     memcpy(&fh->forklen[ RESOURCE ],  head_buf +  87, 4 );
386     bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
387     fh->comment[0] = '\0';
388
389     if (revision == 3) {
390       fh->finder_xinfo.fdScript = *(head_buf + 106);
391       fh->finder_xinfo.fdXFlags = *(head_buf + 107);
392     }
393
394 #if DEBUG >= 5
395     {
396         short           flags;
397         long            flags_long;
398
399         fprintf( stderr, "Values read by bin_header_read\n" );
400         fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
401         fprintf( stderr, "file name\t\t%s\n", fh->name );
402         fprintf( stderr, "get info comment\t%s\n", fh->comment );
403         fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
404                 &fh->finder_info.fdType );
405         fprintf( stderr, "creator\t\t\t%.*s\n", 
406                 sizeof( fh->finder_info.fdCreator ), 
407                 &fh->finder_info.fdCreator );
408         memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
409         flags = ntohs( flags );
410         fprintf( stderr, "flags\t\t\t%x\n", flags );
411
412         /* Show fdLocation too (RLB) */
413         memcpy( &flags_long, &fh->finder_info.fdLocation,
414                 sizeof( flags_long ));
415         flags_long = ntohl( flags_long );
416         fprintf( stderr, "location flags\t\t%lx\n", flags_long );
417
418         fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
419         fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
420         fprintf( stderr, "\n" );
421     }
422 #endif /* DEBUG >= 5 */
423
424     return( 0 );
425 }
426
427 /* 
428  * bin_header_write is called by bin_open, and relies on information
429  * from the fh substruct.  it must be called before any
430  * of the bytes of the other two forks can be written, as well.
431  * bin_header_write and bin_header_read are opposites.
432  */
433
434 int bin_header_write( fh )
435     struct FHeader      *fh;
436 {
437     char                *write_ptr;
438     u_int32_t           t;
439     int                 wc;
440     int                 wr;
441
442     memset(head_buf, 0, sizeof( head_buf ));
443     head_buf[ 1 ] = (u_char)strlen( fh->name );
444     memcpy( head_buf + 2, fh->name, head_buf[ 1 ] );
445     memcpy( head_buf + 65, &fh->finder_info, 8 );
446
447 #ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */
448     memcpy( head_buf + 73, &fh->finder_info.fdFlags, 1 );
449 #else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
450     memcpy( head_buf + 73, &fh->finder_info.fdFlags, 2 );
451 #endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
452
453     memcpy( head_buf + 75, &fh->finder_info.fdLocation, 4 );
454     memcpy( head_buf + 79, &fh->finder_info.fdFldr, 2 );
455     memcpy( head_buf + 83, &fh->forklen[ DATA ], 4 );
456     memcpy( head_buf + 87, &fh->forklen[ RESOURCE ], 4 );
457     t = AD_DATE_TO_UNIX(fh->create_date) + bin.gmtoff;
458     t = MAC_DATE_FROM_UNIX(t);
459     memcpy( head_buf + 91, &t, sizeof(t) );
460     t = AD_DATE_TO_UNIX(fh->mod_date) + bin.gmtoff;
461     t = MAC_DATE_FROM_UNIX(t);
462     memcpy( head_buf + 95, &t, sizeof(t) );
463     memcpy( head_buf + 101, &fh->finder_info.fdFlags + 1, 1);
464
465     /* macbinary III */
466     memcpy( head_buf + 102, "mBIN", 4);
467     *(head_buf + 106) = fh->finder_xinfo.fdScript;
468     *(head_buf + 107) = fh->finder_xinfo.fdXFlags;
469     head_buf[ 122 ] = 130;
470
471     head_buf[ 123 ] = 129;
472
473     bin.headercrc = htons( updcrc( (u_short) 0, head_buf, 124 ));
474     memcpy(head_buf + 124, &bin.headercrc, sizeof( bin.headercrc ));
475
476     bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
477     bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
478
479 #if DEBUG >= 5
480     {
481         short   flags;
482         long    flags_long;
483
484         fprintf( stderr, "Values written by bin_header_write\n" );
485         fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
486         fprintf( stderr, "file name\t\t%s\n", (char *)&head_buf[ 2 ] );
487         fprintf( stderr, "type\t\t\t%.4s\n", (char *)&head_buf[ 65 ] );
488         fprintf( stderr, "creator\t\t\t%.4s\n", (char *)&head_buf[ 69 ] );
489
490         memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
491         flags = ntohs( flags );
492         fprintf( stderr, "flags\t\t\t%x\n", flags );
493
494         /* Show fdLocation too (RLB) */
495         memcpy( &flags_long, &fh->finder_info.fdLocation,
496                 sizeof( flags_long ));
497         flags_long = ntohl( flags_long );
498         fprintf( stderr, "location flags\t\t%ldx\n", flags_long );
499
500         fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
501         fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
502         fprintf( stderr, "\n" );
503     }
504 #endif /* DEBUG >= 5 */
505
506     write_ptr = (char *)head_buf;
507     wc = sizeof( head_buf );
508     wr = 0;
509     while (( wc > 0 ) && ( wr >= 0 )) {
510         wr = write( bin.filed, write_ptr, wc );
511         write_ptr += wr;
512         wc -= wr;
513     }
514     if ( wr < 0 ) {
515         perror( "Couldn't write macbinary header:" );
516         return( wr );
517     }
518
519     return( 0 );
520 }
521
522 /*
523  * test_header is called from bin_open.  it checks certain values of
524  * the first 128 bytes, determines if the file is a MacBinary,
525  * MacBinary II, MacBinary III, or non-MacBinary file, and returns a
526  * one, two, three or negative one to indicate the file type.
527  *
528  * If the signature at 102 is equal to "mBIN," then it's a MacBinary
529  * III file. Bytes 0 and 74 must be zero for the file to be any type
530  * of MacBinary.  If the crc of bytes 0 through 123 equals the value
531  * at offset 124 then it is a MacBinary II.  If not, then if byte 82
532  * is zero, byte 2 is a valid value for a mac filename length (between
533  * one and sixty-three), and bytes 101 through 125 are all zero, then
534  * the file is a MacBinary. 
535  *
536  * NOTE: apple's MacBinary II files have a non-zero value at byte 74.
537  * so, the check for byte 74 isn't very useful.
538  */
539
540 int test_header(void)
541 {
542     const char          zeros[25] = "";
543     u_int32_t           cc;
544     u_short             header_crc;
545     u_char              namelen;
546
547 #if DEBUG
548     fprintf( stderr, "entering test_header\n" );
549 #endif /* DEBUG */
550
551     cc = read( bin.filed, (char *)head_buf, sizeof( head_buf ));
552     if ( cc < sizeof( head_buf )) {
553         perror( "Premature end of file :" );
554         return( -1 );
555     }
556
557 #if DEBUG
558     fprintf( stderr, "was able to read HEADBUFSIZ bytes\n" );
559 #endif /* DEBUG */
560
561     /* check for macbinary III header */
562     if (memcmp(head_buf + 102, "mBIN", 4) == 0)
563         return 3;
564
565     /* check for macbinary II even if only one of the bytes is zero */
566     if (( head_buf[ 0 ] == 0 ) || ( head_buf[ 74 ] == 0 )) {
567 #if DEBUG
568       fprintf( stderr, "byte 0 and 74 are both zero\n" );
569 #endif /* DEBUG */
570       bin.headercrc = updcrc( (u_short) 0, head_buf, 124 );
571       memcpy(&header_crc, head_buf + 124, sizeof( header_crc ));
572       header_crc = ntohs( header_crc );
573       if ( header_crc == bin.headercrc ) {
574         return( 2 );
575       }
576
577 #if DEBUG
578       fprintf( stderr, "header crc didn't pan out\n" );
579 #endif /* DEBUG */
580     }
581
582     /* now see if we have a macbinary file. */
583     if ( head_buf[ 82 ] != 0 ) {
584         return( -1 );
585     }
586     memcpy( &namelen, head_buf + 1, sizeof( namelen ));
587 #if DEBUG
588     fprintf( stderr, "name length is %d\n", namelen );
589 #endif /* DEBUG */
590     if (( namelen < 1 ) || ( namelen > 63 )) {
591         return( -1 );
592     }
593
594     /* bytes 101 - 125 should be zero */
595     if (memcmp(head_buf + 101, zeros, sizeof(zeros)) != 0)
596         return -1;
597
598     /* macbinary forks aren't larger than 0x7FFFFF */
599     memcpy(&cc, head_buf + 83, sizeof(cc));
600     cc = ntohl(cc);
601     if (cc > 0x7FFFFF)
602         return -1;
603     memcpy(&cc, head_buf + 87, sizeof(cc));
604     cc = ntohl(cc);
605     if (cc > 0x7FFFFF)
606         return -1;
607
608
609 #if DEBUG
610     fprintf( stderr, "byte 82 is zero and name length is cool\n" );
611 #endif /* DEBUG */
612
613     return( 1 );
614 }