#!/bin/sh # -*-Perl-*- #======================================================================# # Run the right perl version: if [ -x /usr/local/bin/perl ]; then perl=/usr/local/bin/perl elif [ -x /usr/bin/perl ]; then perl=/usr/bin/perl else perl=`which perl| sed 's/.*aliased to *//'` fi exec $perl -x -S $0 "$@" # -x: start from the following line #======================================================================# #! /Good_Path/perl -w # line 17 # Name: frecords # Author: wd (wdobler2s [at] gmail [dot] com) # Date: 27-Nov-2003 # Description: # Summarize the individual records in a Fortran unformatted file. # Usage: # frecords [-hdriv] # Options: # -h, --help This help # -d, --double Assume double precision data # -r, --recordmarker= # Set length of record marker (see e.g. gfortran's # -frecord-marker option) # -i, --int Print data as int, too (default is just real) # -I, --int-only Print data only as int (default is just real) # -b, --brief Print only length of each record (no real + no int data) # -v, --version Print version number use strict; use Getopt::Long; # Allow for `-Plp' as equivalent to `-P lp' etc: Getopt::Long::config("bundling"); my (%opts); # Variables written by GetOptions my $debug=0; # Activate with (undocumented) `--debug' option ## Process command line GetOptions(\%opts, qw( -h --help --debug -d --double -r=i --recordmarker=i -i --int -I --int-only -b --brief -q --quiet -v --version ) ); if ($opts{'debug'}) { $debug = 1 } else { $debug = 0 } if ($debug) { printopts(\%opts); print "\@ARGV = `@ARGV'\n"; } if ($opts{'h'} || $opts{'help'} ) { die usage(); } if ($opts{'v'} || $opts{'version'}) { die version(); } my $quiet = ($opts{'q'} || $opts{'quiet'} || ''); my $double = ($opts{'d'} || $opts{'double'} || ''); my $nmarker = ($opts{'r'} || $opts{'recordmarker'} || 4); my $print_real = 1; my $print_int = 0; # print only real by default if ($opts{'i'} || $opts{'int'} ) { $print_int=1 }; if ($opts{'I'} || $opts{'int-only'}) { $print_int=1; $print_real=0 }; if ($opts{'b'} || $opts{'brief'} ) { $print_real = $print_int = 0 }; ## my ($bytes,$ffmt,$ifmt,$nn1,$nn2,$N,$swap); if ($double) { $bytes = 8; $ffmt = "d"; $ifmt = "l"; } else { $bytes = 4; $ffmt = "f"; $ifmt = "i"; } print "\$bytes = $bytes, \$ffmt = $ffmt, \$ifmt = $ifmt\n" if ($debug); ## Figure out how to print \times # Contrary to what I'd have expected, we seem to be able to print in utf8 # mode even in an xterm with LC_CTYPE set to latin1. Possibly this is # because the X server is running with a utf-8 locale? # Let us stick to utf-8 only for the time being: my $times = 'x'; #my $lc_ctype = $ENV{LC_CTYPE} || $ENV{LC_ALL} || $ENV{LANG}; #if (defined($lc_ctype)) { # if ($ENV{LC_ALL} =~ /utf-?8/i) { $times = "\x{00D7}"; binmode(STDOUT, ':utf8'); # } #} ## Process files file: foreach my $file (@ARGV) { if (!open(INPUT,"<$file")) { print STDERR "Can't open input file $file\n"; next; } print "$file:"; # The first four bytes contain the size of the record read INPUT, $nn1, $nmarker; $N = unpack("I",$nn1); # Interpret $N as a $nmarker-byte integer if (!defined($N) || $N == 0) { print STDERR "$file:\t (corrupt)\n"; next file; } # Skip $N bytes and check record end for consistency: if (seek(INPUT,$N+$nmarker,0) && read(INPUT,$nn2,$nmarker) && ($nn2 eq $nn1)) { $swap=0; } else { $N = unpack("I",reverse($nn1)); # Try same for swapped byte order if (seek(INPUT,$N+$nmarker,0) && read(INPUT,$nn2,$nmarker) && ($nn2 eq $nn1)) { $swap=1; } else { printf(STDERR "\$nn1=%x, \$nn2=%x, \$N=$N\n", $nn1, $nn2, $N) if ($debug); print "$file:\t (Corrupt )\n"; next file; } } if ($swap) { print " swapped byte order;"; } if ($double) { print " double;"; } else { print " real;"; } print "\n"; ## Summarize all records my ($realfmt,$intfmt); if ($double) { $realfmt='%22.9g' } else { $realfmt='%11.5g ' }; if ($double) { $intfmt='%22d' } else { $intfmt='%11d ' }; # my ($pos0,$recnum) = (0,1); # Loop over records: do { print STDERR "--------------------------------\n" if ($debug); print STDERR "\$pos0 = $pos0\n" if ($debug); unless (seek(INPUT,$pos0,0) && read(INPUT,$nn1,$bytes)) { print STDERR "Can't read at position $pos0\n"; next file; } $N = unpack_swap("I",$nn1,$swap); # record length print "\$N = $N\n" if ($debug); my $Ndata = $N/$bytes; # Read first and last two numbers in given record: my (@nf,@ni); my ($i,$pos); number: foreach $i (1..4) { last number if ($i > $Ndata); if ($i==1) { $pos = $pos0 + $nmarker; } elsif ($i==3) { if ($Ndata>3) { $pos = $pos0 + $N + $nmarker - 2*$bytes ; } else { $pos = $pos0 + $N + $nmarker - $bytes ; # only 3 items in record } } else { $pos = $pos + $bytes; } printf STDERR "Loop: \$pos = $pos\n" if ($debug); unless (seek(INPUT,$pos,0) && read(INPUT,$nn1,$bytes)) { print STDERR "Can't read $pos\n"; next file; } my $n1 = unpack_swap("$ffmt",$nn1,$swap); if (defined($n1)) { push @nf, $n1 } else { warn "Can't unpack \n" }; my $n2 = unpack_swap("$ifmt",$nn1,$swap); if (defined($n2)) { push @ni, $n2 } else { warn "Can't unpack \n" }; } # Summarize current record: printf("%2d: %d = %d${times}${bytes}bytes\n",$recnum, $N, $Ndata); print_data($realfmt,\@nf,$Ndata) if ($print_real); print_data($intfmt ,\@ni,$Ndata) if ($print_int); # Step to next record $pos0 = $pos0 + $N + 2*$nmarker; $recnum++; seek(INPUT,$nmarker,1); # move $nmarker bytes on, so we can detect eof if ($debug) { print STDERR 'Current position: ', tell(INPUT), ", next record starts at \$pos0=$pos0\n"; } } until (eof(INPUT)); } # ---------------------------------------------------------------------- # sub unpack_swap { # Unpack with local or swapped byte order. my $fmt = shift; my $nn = shift; my $swap = shift; my $n; if ($swap) { $n = unpack($fmt,reverse($nn)); } else { $n = unpack($fmt,$nn); } $n; } # ---------------------------------------------------------------------- # sub print_data { # Print up to four data items in the given format my $fmt = shift; my $dataref = shift; my @data = @$dataref; my $ntot = shift; print "\@data = <@data>\n" if ($debug); my $dots = ($ntot > 4); # Print dots only if > 4 items my $format = " "; foreach my $i (1..@data) { $format .= " $fmt"; if ($i == 2) { if ($dots) { $format .= ' ...'; } else { $format .= ' '; # keep short records aligned } } } print STDERR "\$format=<$format>\n" if ($debug); printf("$format\n", @data); } # ---------------------------------------------------------------------- # sub printopts { # Print command line options. my $optsref = shift; my %opts = %$optsref; foreach my $opt (keys(%opts)) { print STDERR "\$opts{$opt} = `$opts{$opt}'\n"; } } # ---------------------------------------------------------------------- # sub usage { # Extract description and usage information from this file's header. my $thisfile = __FILE__; local $/ = ''; # Read paragraphs open(FILE, "<$thisfile") or die "Cannot open $thisfile\n"; while () { # Paragraph _must_ contain `Description:' or `Usage:' next unless /^\s*\#\s*(Description|Usage):/m; # Drop `Author:', etc: s/.*?\n(\s*\#\s*(Description|Usage):\s*\n.*)/$1/s; # Don't print comment sign: s/^\s*# ?//mg; last; # ignore body } $_ or "\n"; } # ---------------------------------------------------------------------- # sub version { # Return CVS data and version info. my $doll='\$'; # Need this to trick CVS my $cmdname = (split('/', $0))[-1]; my $rev = '$Revision: 1.1 $'; my $date = '$Date: 2007-08-13 13:55:23 $'; $rev =~ s/${doll}Revision:\s*(\S+).*/$1/; $date =~ s/${doll}Date:\s*(\S+).*/$1/; "$cmdname version $rev ($date)\n"; } # ---------------------------------------------------------------------- # # End of file frecords