]> arthur.barton.de Git - netatalk.git/blob - contrib/shell_utils/apple_dump.in
apple_dump: use open2(), from Oichinokata
[netatalk.git] / contrib / shell_utils / apple_dump.in
1 #!@PERL@
2 #
3 # AppleSingle/AppleDouble dump
4 #
5 # (c) 2009-2012 by HAT <hat@fa2.so-net.ne.jp>
6 #
7 #  This program is free software; you can redistribute it and/or modify
8 #  it under the terms of the GNU General Public License as published by
9 #  the Free Software Foundation; either version 2 of the License, or
10 #  (at your option) any later version.
11 #
12 #  This program is distributed in the hope that it will be useful,
13 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #  GNU General Public License for more details.
16 #
17
18 #
19 # References:
20 #
21 # Applesingle and AppleDouble format internals (version 1)
22 # http://users.phg-online.de/tk/netatalk/doc/Apple/v1/
23 #
24 # AppleSingle/AppleDouble Formats for Foreign Files Developer's Note (version2)
25 # http://users.phg-online.de/tk/netatalk/doc/Apple/v2/AppleSingle_AppleDouble.pdf
26 #
27 # Inside Macintosh: Macintosh Toolbox Essentials /
28 # Chapter 7 - Finder Interface / Finder Interface Reference
29 # http://developer.apple.com/legacy/mac/library/documentation/mac/toolbox/Toolbox-463.html
30 #
31 # Finder Interface Reference
32 # http://developer.apple.com/legacy/mac/library/documentation/Carbon/Reference/Finder_Interface/Reference/reference.html
33 #
34 # Technical Note TN1150  HFS Plus Volume Format
35 # http://developer.apple.com/mac/library/technotes/tn/tn1150.html#FinderInfo
36 #
37 # CarbonHeaders source
38 # http://www.opensource.apple.com/source/CarbonHeaders/CarbonHeaders-8A428/Finder.h
39 # http://www.opensource.apple.com/source/CarbonHeaders/CarbonHeaders-9A581/Finder.h
40 #
41 # Xcode 3.2.1
42 # /usr/bin/SetFile
43 # /usr/bin/GetFileInfo
44 #
45 # Mac OS X 10.6.2 kernel source
46 # http://www.opensource.apple.com/source/xnu/xnu-1486.2.11/bsd/vfs/vfs_xattr.c
47 #
48
49 use File::Basename;
50 use File::Spec;
51 use File::Temp qw /tempfile/;
52 use bigint; # require perl >= 5.8
53 use IPC::Open2 qw /open2/;
54
55 # check command for extended attributes -----------------------------------
56
57 if (     0 == system("which getfattr > /dev/null 2>&1")) {
58     $eacommand = 1;
59 } elsif (0 == system("which attr > /dev/null 2>&1")) {
60     $eacommand = 2;
61 } elsif (0 == system("which runat > /dev/null 2>&1")) {
62     $eacommand = 3;
63 } elsif (0 == system("which getextattr > /dev/null 2>&1")) {
64     $eacommand = 4;
65 } else {
66     $eacommand = 0;
67 }
68
69 #printf ( "eacommand = %d\n", $eacommand );   # debug
70
71 # parse command line -----------------------------------------------
72
73 $stdinputmode = 0;
74 $eaoption = 0;
75 $finderinfo = 0;              #  0: unknown   1: file   2: directory
76 while ($arg = shift @ARGV)
77 {
78     if  ($arg =~ /^(-h|-help|--help)$/ ) {
79         printf ("usage: %s [-a] [FILE|DIR]\n"         ,basename($0));
80         printf (" or:   %s -e FILE|DIR\n"             ,basename($0));
81         printf (" or:   %s -f [FILE]\n"               ,basename($0));
82         printf (" or:   %s -d [FILE]\n"               ,basename($0));
83         printf (" or:   %s -h|-help|--help\n"         ,basename($0));
84         printf (" or:   %s -v|-version|--version\n"   ,basename($0));
85         printf ("Dump AppleSingle/AppleDouble format data.\n");
86         printf ("With no FILE|DIR, or when FILE|DIR is -, read standard input.\n");
87         printf ("\n");
88         printf ("  -a (default)     Dump a AppleSingle/AppleDouble data for FILE or DIR\n");
89         printf ("                   automatically.\n");
90         printf ("                   If FILE is not AppleSingle/AppleDouble format,\n");
91         printf ("                   look for extended attribute, .AppleDouble/FILE and ._FILE.\n");
92         printf ("                   If DIR, look for extended attribute,\n");
93         printf ("                   DIR/.AppleDouble/.Parent and ._DIR.\n");
94         printf ("  -e               Dump extended attribute of FILE or DIR\n");
95         printf ("  -f               Dump FILE. Assume FinderInfo to be FileInfo.\n");
96         printf ("  -d               Dump FILE. Assume FinderInfo to be DirInfo.\n");
97         printf ("  -h,-help,--help  Display this help and exit\n");
98         printf ("  -v,-version,--version  Show version and exit\n");
99         printf ("\n");
100         printf ("There is no way to detect whether FinderInfo is FileInfo or DirInfo.\n");
101         printf ("By default, %s examins whether file or directory,\n"   ,basename($0));
102         printf ("a parent directory is .AppleDouble, filename is ._*, filename is .Parent,\n");
103         printf ("and so on.\n");
104         printf ("If setting option -e, -f or -d, %s assume FinderInfo and doesn't look for\n");
105         printf ("another file.\n");
106         exit 1;
107     } elsif ($arg =~ /^(-v|-version|--version)$/ ) {
108         printf ("%s \(Netatalk @NETATALK_VERSION@\)\n", basename($0));
109         exit 1;
110     } elsif ($arg eq "-a") {
111         $finderinfo = 0;
112     } elsif ($arg eq "-e") {
113         if ($eacommand == 0) {
114             printf (STDERR "%s: unsupported option -e\n", basename($0));
115             printf (STDERR "because neither getfattr, attr, runat nor getextattr is found.\n");
116             exit 1;
117         }
118         $eaoption = 1;
119     } elsif ($arg eq "-f") {
120         $finderinfo = 1;
121     } elsif ($arg eq "-d") {
122         $finderinfo = 2;
123     } elsif ($arg eq "-") {
124         $stdinputmode = 1;
125     } elsif ($arg =~ /^-/) {
126         printf (STDERR "%s: invalid option %s\n", basename($0), $arg);
127         printf (STDERR "Try \`%s\ -h' for more information.\n", basename($0));
128         exit 1;
129     } else {
130         $afile = $arg;
131     }
132 }
133
134 if (!($afile)) {
135     $stdinputmode = 1;
136 } elsif (!( -e $afile)) {
137     printf (STDERR "\"%s\" is not found.\n", $afile);
138     exit 1;
139 }
140
141 # detect FinderInfo, and search AppleSingle/AppleDouble file --------------
142
143 $abspath = File::Spec->rel2abs($afile);
144 ($basename, $path, $ext) = fileparse($abspath);
145
146 if ( $stdinputmode == 1) {
147     ($eatempfh, $openfile) = tempfile(UNLINK => 1);
148     system("cat - > $openfile");
149     close($eatempfh);
150     $openmessage = "Dumping Standard Input...\n";
151 } elsif ( $eaoption == 1 ) {
152     if ( -f $afile ) {
153         $finderinfo = 1;
154     } elsif ( -d $afile ) {
155         $finderinfo = 2;
156     } else {
157         printf (STDERR "unknown error: %s\n", $afile);
158         exit 1;
159     }
160     if ( 0 == checkea($afile) ) {
161         printf (STDERR "\"%s\"'s extended attribute is not found\n",  $afile);
162         exit 1;
163     }
164     $openfile = eaopenfile($afile);
165     $openmessage = "Dumping \"$afile\"'s extended attribute...\n";
166 } elsif ( $finderinfo != 0 ) {
167     $openfile = $afile;
168     $openmessage = "Dumping \"$openfile\"...\n";
169 } elsif ( -f $afile ) {
170     if ( $basename eq ".Parent") {
171         $finderinfo = 2;
172     } elsif ( $path =~ /\/.AppleDouble\/$/ ) {
173         $finderinfo = 1;
174     } elsif ( $basename =~ /^._/ ) {
175         if ( -f $path.substr($basename, 2) ) {
176             $finderinfo = 1;
177         } elsif ( -d $path.substr($basename, 2) ) {
178             $finderinfo = 2;
179         }
180     }
181     if (!open(INFILE, "<$afile")) {
182         printf (STDERR "cannot open %s\n",  $afile);
183         exit 1;
184     }
185     read(INFILE,$buf,4);
186     $val = unpack("N", $buf );
187     close(INFILE);
188     if ($val == 0x00051600 || $val == 0x00051607) {
189         $openfile = $afile;
190         $openmessage = "Dumping \"$openfile\"...\n";
191     } else {
192         printf ("\"%s\" is not AppleSingle/AppleDouble format.\n", $afile);
193         $finderinfo = 1;
194         $adcount = 0;
195         $netatalkfile = $path.".AppleDouble/".$basename;
196         $osxfile = $path."._".$basename;
197
198         if ( 1 == checkea($afile) ) {
199             printf ("\"%s\"\'s extended attribute is found.\n", $afile);
200             $adcount++;
201             $openfile = eaopenfile($afile);
202             $openmessage = "Dumping \"$afile\"'s extended attribute...\n";
203         }
204         if ( -e $netatalkfile ) {
205             printf ("\"%s\" is found.\n", $netatalkfile);
206             $adcount++;
207             $openfile = $netatalkfile;
208             $openmessage = "Dumping \"$openfile\"...\n";
209         }
210         if ( -e $osxfile ) {
211             printf ("\"%s\" is found.\n", $osxfile);
212             $adcount++;
213             $openfile = $osxfile;
214             $openmessage = "Dumping \"$openfile\"...\n";
215         }
216         if ( $adcount == 0 ) {
217             printf ("AppleSingle/AppleDouble data is not found.\n");
218             exit 1;
219         }
220         if ( $adcount != 1 ) {
221             printf ("Specify any one.\n");
222             exit 1;
223         }
224     }
225 } elsif ( -d $afile) {
226     printf ("\"%s\" is a directory.\n", $afile);
227     $finderinfo = 2;
228     $adcount = 0;
229     $netatalkfile = $path.$basename."/.AppleDouble/.Parent";
230     $osxfile = $path."._".$basename;
231
232     if ( 1 == checkea($afile) ) {
233         printf ("\"%s\"\'s extended attribute is found.\n", $afile);
234         $adcount++;
235         $openfile = eaopenfile($afile);
236         $openmessage = "Dumping \"$afile\"'s extended attribute...\n";
237     }
238     if ( -e $netatalkfile ) {
239         printf ("\"%s\" is found.\n", $netatalkfile);
240         $adcount++;
241         $openfile= $netatalkfile;
242         $openmessage = "Dumping \"$openfile\"...\n";
243     }
244     if ( -e $osxfile ) {
245         printf ("\"%s\" is found.\n", $osxfile);
246         $adcount++;
247         $openfile = $osxfile;
248         $openmessage = "Dumping \"$openfile\"...\n";
249     }
250     if ( $adcount == 0 ) {
251         printf ("AppleSingle/AppleDouble data is not found.\n");
252         exit 1;
253     }
254     if ( $adcount != 1 ) {
255         printf ("Specify any one.\n");
256         exit 1;
257     }
258 } else {
259     printf (STDERR "unknown error: %s\n", $afile);
260     exit 1;
261 }
262
263 if (!open(INFILE, "<$openfile")) {
264     printf (STDERR "cannot open %s\n",  $openfile);
265     exit 1;
266 }
267
268 printf ($openmessage);
269
270 #Dump --------------------------------------------------------
271
272 # Magic Number -----------------------------------------------
273
274 print "-------------------------------------------------------------------------------\n";
275
276 read(INFILE,$buf,4);
277 $val = unpack("N", $buf );
278 printf("MagicNumber: %08X", $val);
279 if    ( $val == 0x00051600 ) {
280     printf("                                        : AppleSingle");
281 }
282 elsif ( $val == 0x00051607 ) {
283     printf("                                        : AppleDouble");
284 }
285 else                         {
286     printf("                                        : Unknown"    );
287 }
288 print "\n";
289
290 # Version Number ---------------------------------------------
291
292 read(INFILE,$buf,4);
293 $version = unpack("N", $buf );
294 printf("Version    : %08X", $version);
295 if ( $version == 0x00010000 ) {
296     printf("                                        : Version 1");
297 } elsif ( $version == 0x00020000 ) {
298     printf("                                        : Version 2");
299 } else {
300     printf("                                        : Unknown"  );
301 }
302 print "\n";
303
304 # v1:Home file system / v2:Filler ----------------------------
305
306 read(INFILE,$buf,16);
307 if ( $version == 0x00010000 ) {
308     print "HomeFileSys:";
309 } else {
310     print "Filler     :";
311 }
312 hexdump($buf, 16, 16, " ");
313
314 # Number of entities -----------------------------------------
315
316 read(INFILE,$buf,2);
317 $entnum = unpack("n", $buf );
318 printf("Num. of ent: %04X    ", $entnum);
319 printf("                                        : %d", $entnum);
320 print "\n";
321
322 # data -------------------------------------------------------
323
324 for ( $num = 0 ; $num < $entnum ; $num++) {
325
326     seek(INFILE, ($num * 12 + 26), 0);
327
328 #    Entry ---------------------------------------------------
329
330     read(INFILE,$buf,4);
331     $entid = unpack("N", $buf );
332     print "\n-------------------------------------------------------------------------------\n";
333     printf("Entry ID   : %08X", $entid);
334     if    ( $entid ==  1 )         { printf(" : Data Fork"); }
335     elsif ( $entid ==  2 )         { printf(" : Resource Fork"); }
336     elsif ( $entid ==  3 )         { printf(" : Real Name"); }
337     elsif ( $entid ==  4 )         { printf(" : Comment"); }
338     elsif ( $entid ==  5 )         { printf(" : Icon, B&W"); }
339     elsif ( $entid ==  6 )         { printf(" : Icon Color"); }
340     elsif ( $entid ==  7 )         { printf(" : File Info"); }
341     elsif ( $entid ==  8 )         { printf(" : File Dates Info"); }
342     elsif ( $entid ==  9 )         { printf(" : Finder Info"); }
343     elsif ( $entid == 10 )         { printf(" : Macintosh File Info"); }
344     elsif ( $entid == 11 )         { printf(" : ProDOS File Info"); }
345     elsif ( $entid == 12 )         { printf(" : MS-DOS File Info"); }
346     elsif ( $entid == 13 )         { printf(" : Short Name"); }
347     elsif ( $entid == 14 )         { printf(" : AFP File Info"); }
348     elsif ( $entid == 15 )         { printf(" : Directory ID"); }
349     elsif ( $entid == 0x8053567E ) { printf(" : CNID (Netatalk Extended)"); }
350     elsif ( $entid == 0x8053594E ) { printf(" : DB stamp (Netatalk Extended)"); }
351     elsif ( $entid == 0x80444556 ) { printf(" : dev (Netatalk Extended)"); }
352     elsif ( $entid == 0x80494E4F ) { printf(" : inode (Netatalk Extended)"); }
353     else                           { printf(" : Unknown"); }
354     print "\n";
355
356 #    Offset -------------------------------------------------
357
358     read(INFILE,$buf,4);
359     $ofst = unpack("N", $buf );
360     printf("Offset     : %08X", $ofst);
361     printf(" : %d ", $ofst);
362
363 #    Length -------------------------------------------------
364
365     read(INFILE,$buf,4);
366     $len = unpack("N", $buf );
367     printf("\nLength     : %08X", $len);
368     printf(" : %d", $len);
369     $quo = $len >> 4;
370     $rem = $len & 0xF;
371     print "\n";
372
373 #     Dump for each Entry ID --------------------------------
374
375 #    if ( $entid ==  1 ) { ; } # Data Fork
376 #    if ( $entid ==  2 ) { ; } # Resource Fork
377 #    if ( $entid ==  3 ) { ; } # Real Name
378 #    if ( $entid ==  4 ) { ; } # Comment
379 #    if ( $entid ==  5 ) { ; } # Icon, B&W
380 #    if ( $entid ==  6 ) { ; } # Icon Color
381 #    if ( $entid ==  7 ) { ; } # File Info
382     if ( $entid ==  8 ) { filedatesdump($ofst,$len); }
383     elsif ( $entid ==  9 ) { finderinfodump($ofst,$len); }
384 #    if ( $entid == 10 ) { ; } # Macintosh File Info
385 #    if ( $entid == 11 ) { ; } # ProDOS File Info
386 #    if ( $entid == 12 ) { ; } # MS-DOS File Info
387 #    if ( $entid == 13 ) { ; } # Short Name
388 #    if ( $entid == 14 ) { ; } # AFP File Info 
389     elsif ( $entid == 15 ) { print "\n"; bedump($ofst,$len); } # Directory ID
390     elsif ( $entid == 0x8053567E  ) { print "\n"; bedump($ofst,$len); ledump($ofst,$len); } # CNID (Netatalk Extended)
391     elsif ( $entid == 0x8053594E  ) { print "\n"; bedump($ofst,$len); ledump($ofst,$len); } # DB stamp (Netatalk Extended)
392     elsif ( $entid == 0x80444556  ) { print "\n"; bedump($ofst,$len); ledump($ofst,$len); } # dev (Netatalk Extended)
393     elsif ( $entid == 0x80494E4F  ) { print "\n"; bedump($ofst,$len); ledump($ofst,$len); } # inode (Netatalk Extended)
394
395 #    RAW Dump ---------------------------------------------------
396
397     if ( ($quo > 0) || ($rem > 0)) {
398         print "\n";
399         print "-RAW DUMP--:  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F : (ASCII)\n";
400     }
401
402     seek(INFILE, $ofst, 0);
403     rawdump($quo, $rem);
404
405 }
406
407 close(INFILE);
408 exit 0;
409
410 #sub -----------------------------------------------------------
411
412 sub filedatesdump {
413     my ($ofst, $len) = @_;
414     my ($datedata);
415     my ($i);
416     my ($datestr);
417
418     @datetype =('create    ', 'modify    ', 'backup    ', 'access    ');
419
420     seek(INFILE, $ofst, 0);
421
422     print "\n";
423     printf ("-DATE------:          : (GMT)                    : (Local)\n");
424
425     for ( $i = 0 ; $i < 4 ; $i++) {
426         read(INFILE,$buf,4);
427         $datedata = unpack("N", $buf );
428         if ($datedata < 0x80000000) {
429             $datestr = gmtime( $datedata + 946684800)
430                 ." : "
431                 .localtime( $datedata + 946684800);
432         } elsif ($datedata == 0x80000000) {
433             $datestr = "Unknown or Initial";
434         } else {
435             $datestr = gmtime( $datedata - 3348282496)
436                 ." : "
437                 .localtime( $datedata - 3348282496);
438         }
439         printf ("%s : %08X : %s\n",$datetype[$i], $datedata, $datestr);
440     }
441 }
442
443 sub finderinfodump {
444     my ($ofst, $len) = @_;
445
446     seek(INFILE, $ofst, 0);
447
448     if ($finderinfo == 0) {
449         print "\n";
450         print "-NOTE------: cannot detect whether FInfo or DInfo. assume FInfo.\n";
451     }
452
453     if ($finderinfo == 0 || $finderinfo == 1) {
454         filefinderinfodump();
455     } elsif ($finderinfo == 2) {
456         dirfinderinfodump();
457     } else {
458         print STDERR "unknown FinderInfo type\n"
459     }
460
461     if ($len > 32) { eadump(); }
462 }
463
464 sub filefinderinfodump {
465
466     print "\n";
467     print "-FInfo-----:\n";
468
469     read(INFILE,$buf,4);
470     print "Type       : ";
471     hexdump($buf, 4, 4, "");
472
473     read(INFILE,$buf,4);
474     print "Creator    : ";
475     hexdump($buf, 4, 4, "");
476
477     flagsdump();
478
479     read(INFILE,$buf,2);
480     $val = unpack("n", $buf );
481     printf("Location v : %04X", $val);
482     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
483
484     read(INFILE,$buf,2);
485     $val = unpack("n", $buf );
486     printf("Location h : %04X", $val);
487     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
488
489     read(INFILE,$buf,2);
490     print "Fldr       : ";
491     hexdump($buf, 2, 4, "");
492
493     print "\n";
494     print "-FXInfo----:\n";
495
496     read(INFILE,$buf,2);
497     $val = unpack("n", $buf );
498     printf("Rsvd|IconID: %04X", $val);
499     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
500
501     read(INFILE,$buf,2);
502     print "Rsvd       : ";
503     hexdump($buf, 2, 4, "");
504     read(INFILE,$buf,2);
505     print "Rsvd       : ";
506     hexdump($buf, 2, 4, "");
507     read(INFILE,$buf,2);
508     print "Rsvd       : ";
509     hexdump($buf, 2, 4, "");
510
511     xflagsdump();
512
513     read(INFILE,$buf,2);
514     $val = unpack("n", $buf );
515     printf("Rsvd|commnt: %04X", $val);
516     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
517
518     read(INFILE,$buf,4);
519     $val = unpack("N", $buf );
520     printf("PutAway    : %08X", $val);
521     printf(" : %d\n", $val>0x7FFFFFFF?$val-0x100000000:$val); # Why SInt32?
522
523 }
524
525 sub dirfinderinfodump {
526
527     print "\n";
528     print "-DInfo-----:\n";
529
530     read(INFILE,$buf,2);
531     $val = unpack("n", $buf );
532     printf("Rect top   : %04X", $val);
533     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
534
535     read(INFILE,$buf,2);
536     $val = unpack("n", $buf );
537     printf("Rect left  : %04X", $val);
538     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
539
540     read(INFILE,$buf,2);
541     $val = unpack("n", $buf );
542     printf("Rect bottom: %04X", $val);
543     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
544
545     read(INFILE,$buf,2);
546     $val = unpack("n", $buf );
547     printf("Rect right : %04X", $val);
548     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
549
550     flagsdump();
551
552     read(INFILE,$buf,2);
553     $val = unpack("n", $buf );
554     printf("Location v : %04X", $val);
555     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
556
557     read(INFILE,$buf,2);
558     $val = unpack("n", $buf );
559     printf("Location h : %04X", $val);
560     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
561
562     read(INFILE,$buf,2);
563     print "View       : ";
564     hexdump($buf, 2, 4, "");
565
566     print "\n";
567     print "-DXInfo----:\n";
568
569     read(INFILE,$buf,2);
570     $val = unpack("n", $buf );
571     printf("Scroll v   : %04X", $val);
572     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
573
574     read(INFILE,$buf,2);
575     $val = unpack("n", $buf );
576     printf("Scroll h   : %04X", $val);
577     printf("     : %d\n", $val>0x7FFF?$val-0x10000:$val);
578
579     read(INFILE,$buf,4);
580     $val = unpack("N", $buf );
581     printf("Rsvd|OpnChn: %08X", $val);
582     printf(" : %d\n", $val>0x7FFFFFFF?$val-0x100000000:$val); # Why SInt32?
583
584     xflagsdump();
585
586     read(INFILE,$buf,2);
587     print "Comment    : ";
588     hexdump($buf, 2, 4, "");
589
590     read(INFILE,$buf,4);
591     $val = unpack("N", $buf );
592     printf("PutAway    : %08X", $val);
593     printf(" : %d\n", $val>0x7FFFFFFF?$val-0x100000000:$val); # Why SInt32?
594
595 }
596 sub flagsdump {
597
598     @colortype =('none', 'gray', 'green', 'purple', 'blue', 'yellow', 'red', 'orange');
599
600     read(INFILE,$buf,2);
601     $flags = unpack("n", $buf );
602     printf ("isAlias    : %d\n", ($flags >> 15) & 1);
603     printf ("Invisible  : %d\n", ($flags >> 14) & 1);
604     printf ("hasBundle  : %d\n", ($flags >> 13) & 1);
605     printf ("nameLocked : %d\n", ($flags >> 12) & 1);
606     printf ("Stationery : %d\n", ($flags >> 11) & 1);
607     printf ("CustomIcon : %d\n", ($flags >> 10) & 1);
608     printf ("Reserved   : %d\n", ($flags >>  9) & 1);
609     printf ("Inited     : %d\n", ($flags >>  8) & 1);
610     printf ("NoINITS    : %d\n", ($flags >>  7) & 1);
611     printf ("Shared     : %d\n", ($flags >>  6) & 1);
612     printf ("SwitchLaunc: %d\n", ($flags >>  5) & 1);
613     printf ("Hidden Ext : %d\n", ($flags >>  4) & 1);
614     printf ("color      : %d%d%d      : %s\n", ($flags >>  3) & 1,
615             ($flags >>  2) & 1,
616             ($flags >>  1) & 1,
617             @colortype[($flags & 0xE)>>1]);
618     printf ("isOnDesk   : %d\n", ($flags >>  0) & 1);
619
620 }
621
622 sub xflagsdump {
623
624     read(INFILE,$buf,2);
625     $flags = unpack("n", $buf );
626
627     if (($flags >> 15) == 1) {
628         print "Script     : ";
629         hexdump($buf, 1, 4, "");
630     } else {
631         printf ("AreInvalid : %d\n", ($flags >> 15) & 1);
632         printf ("unknown bit: %d\n", ($flags >> 14) & 1);
633         printf ("unknown bit: %d\n", ($flags >> 13) & 1);
634         printf ("unknown bit: %d\n", ($flags >> 12) & 1);
635         printf ("unknown bit: %d\n", ($flags >> 11) & 1);
636         printf ("unknown bit: %d\n", ($flags >> 10) & 1);
637         printf ("unknown bit: %d\n", ($flags >>  9) & 1);
638     }
639
640     printf ("CustomBadge: %d\n", ($flags >>  8) & 1);
641     printf ("ObjctIsBusy: %d\n", ($flags >>  7) & 1);
642     printf ("unknown bit: %d\n", ($flags >>  6) & 1);
643     printf ("unknown bit: %d\n", ($flags >>  5) & 1);
644     printf ("unknown bit: %d\n", ($flags >>  4) & 1);
645     printf ("unknown bit: %d\n", ($flags >>  3) & 1);
646     printf ("RoutingInfo: %d\n", ($flags >>  2) & 1);
647     printf ("unknown bit: %d\n", ($flags >>  1) & 1);
648     printf ("unknown bit: %d\n", ($flags >>  0) & 1);
649
650 }
651
652 sub eadump {
653
654     print "\n";
655     print "-EA--------:\n";
656
657     read(INFILE,$buf,2);
658     print "pad        : ";
659     hexdump($buf, 2, 4, "");
660
661     read(INFILE,$buf,4);
662     print "magic      : ";
663     hexdump($buf, 4, 4, "");
664
665     read(INFILE,$buf,4);
666     $ea_debug_tag = unpack("N", $buf );
667     printf("debug_tag  : %08X", $ea_debug_tag);
668     printf(" : %d\n", $ea_debug_tag);
669
670     read(INFILE,$buf,4);
671     $ea_total_size = unpack("N", $buf );
672     printf("total_size : %08X", $ea_total_size);
673     printf(" : %d\n", $ea_total_size);
674
675     read(INFILE,$buf,4);
676     $ea_data_start = unpack("N", $buf );
677     printf("data_start : %08X", $ea_data_start);
678     printf(" : %d\n", $ea_data_start);
679
680     read(INFILE,$buf,4);
681     $ea_data_length = unpack("N", $buf );
682     printf("data_length: %08X", $ea_data_length);
683     printf(" : %d\n", $ea_data_length);
684
685     read(INFILE,$buf,4);
686     print "reserved[0]: ";
687     hexdump($buf, 4, 4, "");
688
689     read(INFILE,$buf,4);
690     print "reserved[1]: ";
691     hexdump($buf, 4, 4, "");
692
693     read(INFILE,$buf,4);
694     print "reserved[2]: ";
695     hexdump($buf, 4, 4, "");
696
697     read(INFILE,$buf,2);
698     print "flags      : ";
699     hexdump($buf, 2, 4, "");
700
701     read(INFILE,$buf,2);
702     $ea_num_attrs = unpack("n", $buf );
703     printf("num_attrs  : %04X", $ea_num_attrs);
704     printf("     : %d\n", $ea_num_attrs);
705
706     $pos = tell(INFILE);
707
708     for ($i = 0 ; $i < $ea_num_attrs ; $i++) {
709
710         $pos = (($pos & 0x3) == 0) ? ($pos) : ((($pos >> 2) + 1) << 2);
711         seek(INFILE, $pos, 0);
712
713         print "-EA ENTRY--:\n";
714
715         read(INFILE,$buf,4);
716         $ea_offset = unpack("N", $buf );
717         printf("offset     : %08X", $ea_offset);
718         printf(" : %d\n", $ea_offset);
719
720         read(INFILE,$buf,4);
721         $ea_length = unpack("N", $buf );
722         printf("length     : %08X", $ea_length);
723         printf(" : %d\n", $ea_length);
724
725         read(INFILE,$buf,2);
726         print "flags      : ";
727         hexdump($buf, 2, 4, "");
728
729         read(INFILE,$buf,1);
730         $ea_namelen = unpack("C", $buf );
731         printf("namelen    : %02X", $ea_namelen);
732         printf("       : %d\n", $ea_namelen);
733
734         $ea_namequo = $ea_namelen >> 4;
735         $ea_namerem = $ea_namelen & 0xF;
736         print "-EA NAME---:  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F : (ASCII)\n";
737         rawdump($ea_namequo, $ea_namerem);
738
739         $pos = tell(INFILE);
740
741         seek(INFILE, $ea_offset, 0);
742         $ea_quo = $ea_length >> 4;
743         $ea_rem = $ea_length & 0xF;
744         print "-EA VALUE--:  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F : (ASCII)\n";
745         rawdump($ea_quo, $ea_rem);
746     }
747 }
748
749 sub bedump  {
750     my ($ofst, $len) = @_;
751     my ($i);
752     my ($value);
753
754     seek(INFILE, $ofst, 0);
755
756     printf("%2dbit-BE   : ", $len * 8 );
757
758     $value = 0;
759     for ( $i=0 ; $i < $len ; $i++ ) {
760         read(INFILE,$buf,1);
761         $bytedata[$i] = unpack("C", $buf );
762           $value += $bytedata[$i] << (($len - $i -1) * 8) ;
763     }
764
765     for ( $i=0 ; $i < $len ; $i++ ) {
766         printf("%02X", $bytedata[$i]);
767     }
768
769     printf(" : %s", $value);
770     print "\n";
771 }
772
773 sub ledump  {
774     my ($ofst, $len) = @_;
775     my ($i);
776     my ($value);
777
778     seek(INFILE, $ofst, 0);
779
780     printf("%2dbit-LE   : ", $len * 8 );
781
782     $value = 0;
783     for ( $i=0 ; $i < $len ; $i++ ) {
784         read(INFILE,$buf,1);
785         $bytedata[$len - $i - 1] = unpack("C", $buf );
786           $value += $bytedata[$len - $i -1] << ($i * 8) ;
787     }
788
789     for ( $i=0 ; $i < $len ; $i++ ) {
790         printf("%02X", $bytedata[$i]);
791     }
792
793     printf(" : %s", $value);
794     print "\n";
795 }
796
797 sub rawdump {
798     my ($quo, $rem) = @_;
799     my ($addrs, $line, $buf);
800
801     $addrs = 0;
802     for ( $line = 0 ; $line < $quo ; $line++) {
803         read(INFILE, $buf, 16);
804         printf ( "%08X   :", $addrs);
805         hexdump($buf, 16, 16, " ");
806         $addrs = $addrs + 0x10;
807     }
808     if ( $rem != 0 ) {
809         read(INFILE, $buf, $rem);
810         printf ( "%08X   :", $addrs);
811         hexdump($buf, $rem, 16, " ");
812     }
813 }
814
815 sub hexdump {
816     my ($buf, $len, $col, $delimit) = @_;
817     my ($i);
818
819     $hexstr = "";
820     $ascstr = "";
821
822     for ( $i=0 ; $i < $len ; $i++ ) {
823         $val = substr($buf, $i, 1);
824         $ascval = ord($val);
825         $hexstr .= sprintf("%s%02X", $delimit, $ascval);
826
827         if (($ascval < 32) || (  $ascval > 126 )) {
828             $val = ".";
829         }
830         $ascstr .= $val;
831     }
832     for ( ; $i < $col ; $i++) {
833         $hexstr .= "  ".$delimit;
834         $ascstr .= " ";
835     }
836
837     printf("%s : %s", $hexstr,$ascstr);
838
839     print "\n";
840 }
841
842 sub checkea {
843     my ($file) = @_;
844
845     if ( $eacommand == 1 ) {
846         open2(\*EALIST, \*EAIN, 'getfattr', $file) or die $@;
847         while(<EALIST>) {
848             if ( $_ eq "user.org.netatalk.Metadata\n" ) {
849                 close (EALIST, EAIN);
850                 return 1;
851             }
852         }
853         close (EALIST, EAIN);
854         return 0;
855     } elsif ( $eacommand == 2 ) {
856         open2(\*EALIST, \*EAIN, 'attr', '-q', '-l', $file) or die $@;
857         while(<EALIST>) {
858             if ( $_ eq "org.netatalk.Metadata\n" ) {
859                 close (EALIST, EAIN);
860                 return 1;
861             }
862         }
863         close (EALIST, EAIN);
864         return 0;
865     } elsif ( $eacommand == 3 ) {
866         open2(\*EALIST, \*EAIN, 'runat', $file, 'ls', '-1') or die $@;
867         while(<EALIST>) {
868             if ( $_ eq "org.netatalk.Metadata\n" ) {
869                 close (EALIST, EAIN);
870                 return 1;
871             }
872         }
873         close (EALIST, EAIN);
874         return 0;
875     } elsif ( $eacommand == 4 ) {
876         open2(\*EALIST, \*EAIN, 'lsextattr', '-q', 'user', $file) or die $@;
877         while(<EALIST>) {
878             $_ = "\t".$_;
879             if ( $_ =~ /\torg\.netatalk\.Metadata[\n\t]/ ) {
880                 close (EALIST, EAIN);
881                 return 1;
882             }
883         }
884         close (EALIST, EAIN);
885         return 0;
886     } else {
887         return 0;
888     }
889 }
890
891 sub eaopenfile {
892     my ($file) = @_;
893     my @eacommands = ();
894
895     if ( $eacommand == 1 ) {
896         @eacommands = ('getfattr', '--only-values', '-n', 'user.org.netatalk.Metadata', $file);
897     } elsif ( $eacommand == 2 ) {
898         @eacommands = ('attr', '-q', '-g', 'org.netatalk.Metadata', $file);
899     } elsif ( $eacommand == 3 ) {
900         @eacommands = ('runat', $file, 'cat', 'org.netatalk.Metadata',);
901     } elsif ( $eacommand == 4 ) {
902         @eacommands = ('getextattr', '-q', 'user', 'org.netatalk.Metadata', $file);
903     } else {
904         return "";
905     }
906
907     my ($eatempfh, $eatempfile) = tempfile(UNLINK => 1);
908     open2(my $ealist, my $eain, @eacommands) or die $@;
909     print $eatempfh $_ while(<$ealist>);
910     close($ealist, $eain);
911     close($eatempfh);
912     return $eatempfile;
913 }
914
915 #EOF