]> arthur.barton.de Git - netatalk.git/blob - contrib/shell_utils/asaddump.in
ffbcd8f23a19de0b92a25eefc2348daa96d42954
[netatalk.git] / contrib / shell_utils / asaddump.in
1 #!@PERL@
2 #
3 # AppleSingle/AppleDouble dump
4 #
5 # (c) 2009 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 use File::Basename;
19 use File::Spec;
20 use bigint; # require perl >= 5.8
21
22 # parse command line -----------------------------------------------
23
24 $finderinfo = 0;              #  0: unknown   1: file   2: directory
25 while ($arg = shift @ARGV)
26 {
27     if  ($arg =~ /^(-h|-help|--help)$/ ) {
28         printf ("usage: %s [-a] FILE|DIR\n"     ,basename($0));
29         printf (" or:   %s -f FILE\n"           ,basename($0));
30         printf (" or:   %s -d FILE\n"           ,basename($0));
31         printf (" or:   %s -h|-help|--help\n"   ,basename($0));
32         printf ("Dump AppleSingle/AppleDouble format file.\n");
33         printf ("\n");
34         printf ("  -a (default)     Dump a AppleSingle/AppleDouble file for FILE or DIR\n");
35         printf ("                   automatically.\n");
36         printf ("                   Extrapolate FinderInfo type from absolute path.\n");
37         printf ("                   If FILE is not AppleSingle/AppleDouble format,\n");
38         printf ("                   look for '.AppleDouble/FILE' and '._FILE'.\n");
39         printf ("                   If DIR, look for 'DIR/.AppleDouble/.Parent' and '._DIR'.\n");
40         printf ("  -f               Dump FILE. Assume FinderInfo to be FileInfo.\n");
41         printf ("  -d               Dump FILE. Assume FinderInfo to be DirInfo.\n");
42         printf ("  -h,-help,--help  Display this help and exit\n");
43         printf ("\n");
44         printf ("There is no way to detect whether FinderInfo is FileInfo or DirInfo.\n");
45         printf ("By default, %s examins whether file or directory,\n"   ,basename($0));
46         printf ("a parent directory is .AppleDouble, filename is ._*, filename is .Parent,\n");
47         printf ("and so on.\n");
48         printf ("If setting option -f or -d, %s assume FinderInfo and doesn't look for\n");
49         printf ("another file.\n");
50         exit 0;
51     } elsif ($arg eq "-a") {
52         $finderinfo = 0;
53     } elsif ($arg eq "-f") {
54         $finderinfo = 1;
55     } elsif ($arg eq "-d") {
56         $finderinfo = 2;
57     } elsif ($arg =~ /^-/) {
58         printf (STDERR "%s: invalid option %s\n", basename($0), $arg);
59         printf (STDERR "Try \`%s\ -h' for more information.\n", basename($0));
60         exit 1;
61     } else {
62         $afile = $arg;
63     }
64 }
65
66 if (!($afile)) {
67     printf (STDERR "missing file operand.\n");
68     exit 1;
69 }
70 if (!( -e $afile)) {
71     printf (STDERR "\"%s\" not found.\n", $afile);
72     exit 1;
73 }
74
75 # detect FinderInfo, and search AppleSingle/AppleDouble file --------------
76
77 $abspath = File::Spec->rel2abs($afile);
78 ($basename, $path, $ext) = fileparse($abspath);
79
80 if ( $finderinfo != 0 ) {
81     ;
82 } elsif ( -f $afile ) {
83     if ( $basename eq ".Parent") {
84         $finderinfo = 2;
85     } elsif ( $path =~ /\/.AppleDouble\/$/ ) {
86         $finderinfo =1;
87     } elsif ( $basename =~ /^._/ ) {
88         if ( -f $path.substr($basename, 2) ) {
89             $finderinfo =1;
90         } elsif ( -d $path.substr($basename, 2) ) {
91             $finderinfo =2;
92         }
93     }
94     if (!open(INFILE, "<$afile")) {
95         printf (STDERR "cannot open %s\n",  $afile);
96         exit 1;
97     }
98     read(INFILE,$buf,4);
99     $val = unpack("N", $buf );
100     close(INFILE);
101     if ($val == 0x00051600 || $val == 0x00051607) {
102         ;
103     } else {
104         printf ("\"%s\" is not AppleSingle/AppleDouble format.\n", $afile);
105         $finderinfo = 1;
106         $netatalkfile = $path.".AppleDouble/".$basename;
107         $osxfile = $path."._".$basename;
108         if ( -e $netatalkfile ) {
109             $afile = $netatalkfile;
110         } elsif ( -e $osxfile )  {
111             printf ("\"%s\" not found.\n", $netatalkfile);
112             $afile = $osxfile;
113         } else {
114             printf ("\"%s\" not found.\n", $netatalkfile);
115             printf ("\"%s\" not found.\n", $osxfile);
116             exit 1;
117         }
118     }
119 } elsif ( -d $afile) {
120     printf ("\"%s\" is directory.\n", $afile);
121     $finderinfo =2;
122     $netatalkfile = $path.$basename."/.AppleDouble/.Parent";
123     $osxfile = $path."._".$basename;
124     if ( -e $netatalkfile ) {
125         $afile = $netatalkfile;
126     } elsif ( -e $osxfile )  {
127         printf ("\"%s\" not found.\n", $netatalkfile);
128         $afile = $osxfile;
129     } else {
130         printf ("\"%s\" not found.\n", $netatalkfile);
131         printf ("\"%s\" not found.\n", $osxfile);
132         exit 1;
133     }
134 } else {
135     printf (STDERR "unknown error: %s\n", $afile);
136     exit 1;
137 }
138
139 if (!open(INFILE, "<$afile")) {
140     printf (STDERR "cannot open %s\n",  $afile);
141     exit 1;
142 }
143 printf ("%s:\n\n", $afile);
144
145 # Magic Number -----------------------------------------------
146
147 read(INFILE,$buf,4);
148 $val = unpack("N", $buf );
149 printf("Magic Num. : %08X", $val);
150 if    ( $val == 0x00051600 ) {
151     printf("                                        : AppleSingle");
152 }
153 elsif ( $val == 0x00051607 ) {
154     printf("                                        : AppleDouble");
155 }
156 else                         {
157     printf("                                        : Unknown"    );
158 }
159 print "\n";
160
161 # Version Number ---------------------------------------------
162
163 read(INFILE,$buf,4);
164 $val = unpack("N", $buf );
165 printf("Ver. Num.  : %08X", $val);
166 if    ( $val == 0x00010000 ) {
167     printf("                                        : Version 1");
168 }
169 elsif ( $val == 0x00020000 ) {
170     printf("                                        : Version 2");
171 }
172 else                         {
173     printf("                                        : Unknown"  );
174 }
175 print "\n";
176
177 # v1:Home file system / v2:Filler ----------------------------
178
179 read(INFILE,$buf,16);
180 print "Filler     :";
181 hexdump($buf, 16, 16, " ");
182
183 # Number of entities -----------------------------------------
184
185 read(INFILE,$buf,2);
186 $entnum = unpack("n", $buf );
187 printf("Num. of ent: %04X    ", $entnum);
188 printf("                                        : %d", $entnum);
189 print "\n";
190
191 # data -------------------------------------------------------
192
193 for ( $num = 0 ; $num < $entnum ; $num++) {
194
195     seek(INFILE, ($num * 12 + 26), 0);
196
197 #    Entry ---------------------------------------------------
198
199     read(INFILE,$buf,4);
200     $entid = unpack("N", $buf );
201     printf("\nEntry ID   : %08X", $entid);
202     if    ( $entid ==  1 )         { printf(" : Data Fork ---------------------------------------------"); }
203     elsif ( $entid ==  2 )         { printf(" : Resource Fork -----------------------------------------"); }
204     elsif ( $entid ==  3 )         { printf(" : Real Name ---------------------------------------------"); }
205     elsif ( $entid ==  4 )         { printf(" : Comment -----------------------------------------------"); }
206     elsif ( $entid ==  5 )         { printf(" : Icon, B&W ---------------------------------------------"); }
207     elsif ( $entid ==  6 )         { printf(" : Icon Color --------------------------------------------"); }
208     elsif ( $entid ==  7 )         { printf(" : File Info ---------------------------------------------"); }
209     elsif ( $entid ==  8 )         { printf(" : File Dates Info ---------------------------------------"); }
210     elsif ( $entid ==  9 )         { printf(" : Finder Info -------------------------------------------"); }
211     elsif ( $entid == 10 )         { printf(" : Macintosh File Info -----------------------------------"); }
212     elsif ( $entid == 11 )         { printf(" : ProDOS File Info --------------------------------------"); }
213     elsif ( $entid == 12 )         { printf(" : MS-DOS File Info --------------------------------------"); }
214     elsif ( $entid == 13 )         { printf(" : Short Name --------------------------------------------"); }
215     elsif ( $entid == 14 )         { printf(" : AFP File Info -----------------------------------------"); }
216     elsif ( $entid == 15 )         { printf(" : Directory ID ------------------------------------------"); }
217     elsif ( $entid == 0x8053567E ) { printf(" : CNID (Netatalk Extended) ------------------------------"); }
218     elsif ( $entid == 0x8053594E ) { printf(" : DB stamp (Netatalk Extended) --------------------------"); }
219     elsif ( $entid == 0x80444556 ) { printf(" : dev (Netatalk Extended) -------------------------------"); }
220     elsif ( $entid == 0x80494E4F ) { printf(" : inode (Netatalk Extended) -----------------------------"); }
221     else                           { printf(" : Unknown -----------------------------------------------"); }
222     print "\n";
223
224 #    Offset -------------------------------------------------
225
226     read(INFILE,$buf,4);
227     $ofst = unpack("N", $buf );
228     printf("Offset     : %08X", $ofst);
229     printf(" : %d ", $ofst);
230
231 #    Length -------------------------------------------------
232
233     read(INFILE,$buf,4);
234     $len = unpack("N", $buf );
235     printf("\nLength     : %08X", $len);
236     printf(" : %d", $len);
237     $quo = $len >> 4;
238     $rem = $len & 0xF;
239     print "\n";
240
241 #     Dump for each Entry ID --------------------------------
242
243 #    if ( $entid ==  1 ) { ; } # Data Fork
244 #    if ( $entid ==  2 ) { ; } # Resource Fork
245 #    if ( $entid ==  3 ) { ; } # Real Name
246 #    if ( $entid ==  4 ) { ; } # Comment
247 #    if ( $entid ==  5 ) { ; } # Icon, B&W
248 #    if ( $entid ==  6 ) { ; } # Icon Color
249 #    if ( $entid ==  7 ) { ; } # File Info
250     if ( $entid ==  8 ) { filedatesdump($ofst,$len); }
251     elsif ( $entid ==  9 ) { finderinfodump($ofst,$len); }
252 #    if ( $entid == 10 ) { ; } # Macintosh File Info
253 #    if ( $entid == 11 ) { ; } # ProDOS File Info
254 #    if ( $entid == 12 ) { ; } # MS-DOS File Info
255 #    if ( $entid == 13 ) { ; } # Short Name
256 #    if ( $entid == 14 ) { ; } # AFP File Info 
257     elsif ( $entid == 15 ) { bedump($ofst,$len); } # Directory ID
258     elsif ( $entid == 0x8053567E  ) { bedump($ofst,$len); } # CNID (Netatalk Extended)
259     elsif ( $entid == 0x8053594E  ) { bedump($ofst,$len); ledump($ofst,$len); } # DB stamp (Netatalk Extended)
260     elsif ( $entid == 0x80444556  ) { bedump($ofst,$len); ledump($ofst,$len); } # dev (Netatalk Extended)
261     elsif ( $entid == 0x80494E4F  ) { bedump($ofst,$len); ledump($ofst,$len); } # inode (Netatalk Extended)
262
263 #    RAW Dump ---------------------------------------------------
264
265     if ( ($quo > 0) || ($rem > 0)) {
266         print "-RAW DUMP--:  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F : (ASCII)\n";
267     }
268
269     seek(INFILE, $ofst, 0);
270     $addrs = 0;
271     for ( $line = 0 ; $line < $quo ; $line++) {
272         read(INFILE, $buf, 16);
273         printf ( "%08X   :", $addrs);
274         hexdump($buf, 16, 16, " ");
275         $addrs = $addrs + 0x10;
276     }
277     if ( $rem != 0 ) {
278         read(INFILE, $buf, $rem);
279         printf ( "%08X   :", $addrs);
280         hexdump($buf, $rem, 16, " ");
281     }
282 }
283
284 close(INFILE);
285 exit 0;
286
287 #sub -----------------------------------------------------------
288
289 sub filedatesdump {
290     my ($ofst, $len) = @_;
291     my ($datedata);
292     my ($i);
293     my ($datestr);
294
295     @datetype =('create    ', 'modify    ', 'backup    ', 'access    ');
296
297     seek(INFILE, $ofst, 0);
298
299     printf ("-DATE------:          : (GMT)                    : (Local)\n");
300
301     for ( $i = 0 ; $i < 4 ; $i++) {
302         read(INFILE,$buf,4);
303         $datedata = unpack("N", $buf );
304         if ($datedata < 0x80000000) {
305             $datestr = gmtime( $datedata + 946684800)
306                 ." : "
307                 .localtime( $datedata + 946684800);
308         } elsif ($datedata == 0x80000000) {
309             $datestr = "Unknown or Initial";
310         } else {
311             $datestr = gmtime( $datedata - 3348282496)
312                 ." : "
313                 .localtime( $datedata - 3348282496);
314         }
315         printf ("%s : %08X : %s\n",$datetype[$i], $datedata, $datestr);
316     }
317 }
318
319 sub finderinfodump {
320     my ($ofst, $len) = @_;
321
322     seek(INFILE, $ofst, 0);
323
324     if ($finderinfo == 0) {
325         print "-NOTE------: cannot detect whether FInfo or DInfo. assume FInfo.\n";
326     }
327
328     if ($finderinfo == 0 || $finderinfo == 1) {
329         filefinderinfodump();
330     } elsif ($finderinfo == 2) {
331         dirfinderinfodump();
332     } else {
333         print STDERR "unknown FinderInfo type\n"
334     }
335
336     if ($len > 32) { eadump(); }
337 }
338
339 sub filefinderinfodump {
340
341     print "-FInfo-----:\n";
342
343     read(INFILE,$buf,4);
344     print "Type       : ";
345     hexdump($buf, 4, 4, "");
346
347     read(INFILE,$buf,4);
348     print "Creator    : ";
349     hexdump($buf, 4, 4, "");
350
351     read(INFILE,$buf,2);
352     $flags = unpack("n", $buf );
353     printf ("isAlias    : %d\n", ($flags >> 15) & 1);
354     printf ("Invisible  : %d\n", ($flags >> 14) & 1);
355     printf ("hasBundle  : %d\n", ($flags >> 13) & 1);
356     printf ("nameLocked : %d\n", ($flags >> 12) & 1);
357     printf ("Stationery : %d\n", ($flags >> 11) & 1);
358     printf ("CustomIcon : %d\n", ($flags >> 10) & 1);
359     printf ("Reserved   : %d\n", ($flags >>  9) & 1);
360     printf ("Inited     : %d\n", ($flags >>  8) & 1);
361     printf ("NoINITS    : %d\n", ($flags >>  7) & 1);
362     printf ("Shared     : %d\n", ($flags >>  6) & 1);
363     printf ("SwitchLaunc: %d\n", ($flags >>  5) & 1);
364     printf ("ExtHidden  : %d\n", ($flags >>  4) & 1);
365     printf ("color      : %d%d%d\n", ($flags >>  3) & 1,
366             ($flags >>  2) & 1,
367             ($flags >>  1) & 1);
368     printf ("isOnDesk   : %d\n", ($flags >>  0) & 1);
369
370     read(INFILE,$buf,2);
371     $val = unpack("n", $buf );
372     printf("Location v : %04X", $val);
373     printf("     : %d \n", $val);
374
375     read(INFILE,$buf,2);
376     $val = unpack("n", $buf );
377     printf("Location h : %04X", $val);
378     printf("     : %d \n", $val);
379
380     read(INFILE,$buf,2);
381     print "Fldr       : ";
382     hexdump($buf, 2, 4, "");
383
384     print "-FXInfo----:\n";
385
386     read(INFILE,$buf,2);
387     print "IconID     : ";
388     hexdump($buf, 2, 4, "");
389
390     read(INFILE,$buf,2);
391     print "Unused     : ";
392     hexdump($buf, 2, 4, "");
393     read(INFILE,$buf,2);
394     print "Unused     : ";
395     hexdump($buf, 2, 4, "");
396     read(INFILE,$buf,2);
397     print "Unused     : ";
398     hexdump($buf, 2, 4, "");
399
400     read(INFILE,$buf,1);
401     print "Script     : ";
402     hexdump($buf, 1, 4, "");
403
404     read(INFILE,$buf,1);
405     print "XFlags     : ";
406     hexdump($buf, 1, 4, "");
407
408     read(INFILE,$buf,2);
409     print "Comment    : ";
410     hexdump($buf, 2, 4, "");
411
412     read(INFILE,$buf,4);
413     print "PutAway    : ";
414     hexdump($buf, 4, 4, "");
415
416 }
417
418 sub dirfinderinfodump {
419
420     print "-DInfo-----:\n";
421
422     read(INFILE,$buf,2);
423     $val = unpack("n", $buf );
424     printf("Rect top   : %04X", $val);
425     printf("     : %d \n", $val);
426
427     read(INFILE,$buf,2);
428     $val = unpack("n", $buf );
429     printf("Rect left  : %04X", $val);
430     printf("     : %d \n", $val);
431
432     read(INFILE,$buf,2);
433     $val = unpack("n", $buf );
434     printf("Rect bottom: %04X", $val);
435     printf("     : %d \n", $val);
436
437     read(INFILE,$buf,2);
438     $val = unpack("n", $buf );
439     printf("Rect right : %04X", $val);
440     printf("     : %d \n", $val);
441
442     read(INFILE,$buf,2);
443     $flags = unpack("n", $buf );
444     printf ("isAlias    : %d\n", ($flags >> 15) & 1);
445     printf ("Invisible  : %d\n", ($flags >> 14) & 1);
446     printf ("hasBundle  : %d\n", ($flags >> 13) & 1);
447     printf ("nameLocked : %d\n", ($flags >> 12) & 1);
448     printf ("Stationery : %d\n", ($flags >> 11) & 1);
449     printf ("CustomIcon : %d\n", ($flags >> 10) & 1);
450     printf ("Reserved   : %d\n", ($flags >>  9) & 1);
451     printf ("Inited     : %d\n", ($flags >>  8) & 1);
452     printf ("NoINITS    : %d\n", ($flags >>  7) & 1);
453     printf ("Shared     : %d\n", ($flags >>  6) & 1);
454     printf ("SwitchLaunc: %d\n", ($flags >>  5) & 1);
455     printf ("ExtHidden  : %d\n", ($flags >>  4) & 1);
456     printf ("color      : %d%d%d\n", ($flags >>  3) & 1,
457             ($flags >>  2) & 1,
458             ($flags >>  1) & 1);
459     printf ("isOnDesk   : %d\n", ($flags >>  0) & 1);
460
461     read(INFILE,$buf,2);
462     $val = unpack("n", $buf );
463     printf("Location v : %04X", $val);
464     printf("     : %d \n", $val);
465
466     read(INFILE,$buf,2);
467     $val = unpack("n", $buf );
468     printf("Location h : %04X", $val);
469     printf("     : %d \n", $val);
470
471     read(INFILE,$buf,2);
472     print "View       : ";
473     hexdump($buf, 2, 4, "");
474
475     print "-DXInfo----:\n";
476
477     read(INFILE,$buf,4);
478     print "Scroll     : ";
479     hexdump($buf, 4, 4, "");
480
481     read(INFILE,$buf,4);
482     print "OpenChain  : ";
483     hexdump($buf, 4, 4, "");
484
485     read(INFILE,$buf,1);
486     print "Script     : ";
487     hexdump($buf, 1, 4, "");
488
489     read(INFILE,$buf,1);
490     print "XFlags     : ";
491     hexdump($buf, 1, 4, "");
492
493     read(INFILE,$buf,2);
494     print "Comment    : ";
495     hexdump($buf, 2, 4, "");
496
497     read(INFILE,$buf,4);
498     print "PutAway    : ";
499     hexdump($buf, 4, 4, "");
500
501 }
502
503 sub eadump {
504     
505     print "-EA--------:\n";
506
507     read(INFILE,$buf,2);
508     print "pad        : ";
509     hexdump($buf, 2, 4, "");
510
511     read(INFILE,$buf,4);
512     print "magic      : ";
513     hexdump($buf, 4, 4, "");
514
515     read(INFILE,$buf,4);
516     print "debug_tag  : ";
517     hexdump($buf, 4, 4, "");
518
519     read(INFILE,$buf,4);
520     $ea_total_size = unpack("N", $buf );
521     printf("total_size : %08X", $ea_total_size);
522     printf(" : %d \n", $ea_total_size);
523
524     read(INFILE,$buf,4);
525     $ea_data_start = unpack("N", $buf );
526     printf("data_start : %08X", $ea_data_start);
527     printf(" : %d \n", $ea_data_start);
528
529     read(INFILE,$buf,4);
530     $ea_data_length = unpack("N", $buf );
531     printf("data_length: %08X", $ea_data_length);
532     printf(" : %d \n", $ea_data_length);
533
534     read(INFILE,$buf,4);
535     print "reserved[0]: ";
536     hexdump($buf, 4, 4, "");
537
538     read(INFILE,$buf,4);
539     print "reserved[1]: ";
540     hexdump($buf, 4, 4, "");
541
542     read(INFILE,$buf,4);
543     print "reserved[2]: ";
544     hexdump($buf, 4, 4, "");
545
546     read(INFILE,$buf,2);
547     print "flags      : ";
548     hexdump($buf, 2, 4, "");
549
550     read(INFILE,$buf,2);
551     $ea_num_attrs = unpack("n", $buf );
552     printf("num_attrs  : %04X", $ea_num_attrs);
553     printf("     : %d \n", $ea_num_attrs);
554
555 }
556
557 sub bedump  {
558     my ($ofst, $len) = @_;
559     my ($i);
560     my ($value);
561     
562     seek(INFILE, $ofst, 0);
563     
564     printf("%2dbit-BE   : ", $len * 8 );
565     
566     $value = 0;
567     for ( $i=0 ; $i < $len ; $i++ ) {
568         read(INFILE,$buf,1);
569         $bytedata[$i] = unpack("C", $buf );
570         $value += $bytedata[$i] << (($len - $i -1) * 8) ;
571     }
572
573     for ( $i=0 ; $i < $len ; $i++ ) {
574         printf("%02X", $bytedata[$i]);
575     }
576
577     printf(" : %s", $value);
578     print "\n";
579     
580 }
581
582 sub ledump  {
583     my ($ofst, $len) = @_;
584     my ($i);
585     my ($value);
586     
587     seek(INFILE, $ofst, 0);
588     
589     printf("%2dbit-LE   : ", $len * 8 );
590     
591     $value = 0;
592     for ( $i=0 ; $i < $len ; $i++ ) {
593         read(INFILE,$buf,1);
594         $bytedata[$len - $i - 1] = unpack("C", $buf );
595         $value += $bytedata[$len - $i -1] << ($i * 8) ;
596     }
597
598     for ( $i=0 ; $i < $len ; $i++ ) {
599         printf("%02X", $bytedata[$i]);
600     }
601
602     printf(" : %s", $value);
603     print "\n";
604     
605 }
606
607 sub hexdump {
608     my ($buf, $len, $col, $delimit) = @_;
609     my ($i);
610
611     $hexstr = "";
612     $ascstr = "";
613
614     for ( $i=0 ; $i < $len ; $i++ ) {
615         $val = substr($buf, $i, 1);
616         $ascval = ord($val);
617         $hexstr .= sprintf("%s%02X", $delimit, $ascval);
618
619         if (($ascval < 32) || (  $ascval > 126 )) {
620             $val = ".";
621         }
622         $ascstr .= $val;
623     }
624     for ( ; $i < $col ; $i++) {
625         $hexstr .= "  ".$delimit;
626         $ascstr .= " ";
627     }
628
629     printf("%s : %s", $hexstr,$ascstr);
630
631     print "\n";
632 }
633
634 #EOF