<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#!/usr/bin/perl
# gribdump.pl - from grib to raw binary, CDL, or HTML
#
# Copyright (C) GFD-Dennou Club, 2002.  All rights reserved.
#

$dump = {};
$$dump{'head'} = 1;
$$dump{'body'} = 1;
$$dump{'format'} = 'TEXT';
$$dump{'factor'} = 1.0;

@gribs = ();

foreach (@ARGV) {
	if (/^-b/) { $$dump{'body'} = 1; $$dump{'head'} = 0; next; }
	if (/^-m/) { $$dump{'body'} = 0; $$dump{'head'} = 1; next; }
	if (/^-a/) { $$dump{'body'} = 1; $$dump{'head'} = 1; next; }
	if (/^-c/) { $$dump{'format'} = "CDL"; next; }
	if (/^-r/) { $$dump{'format'} = "RAW"; next; }
	if (/^-t/) { $$dump{'format'} = "TEXT"; $$dump{'printf'} = $'; next; }
	if (/^-f/) { $$dump{'format'} = "F32"; next; }
	if (/^-h/) { $$dump{'format'} = "HTML"; next; }
	if (/^-x/) { $$dump{'factor'} = $'; next; }
	if (/^--color[:=]?/) { $$dump{'color'} = $' || 1;  next; }
	if (/^--pandora/) { $$dump{'pandora'} = 1;  next; }
	if (/^-/) { die "unknown option $_\n"; }
	&amp;scan_file($_, \@gribs);
}

for $grib (@gribs) {
	&amp;dump_file($grib, $dump);
}

exit 0;

sub dump_file(\%\%) {
	my($grib, $dump) = @_;
	if ($$dump{'format'} eq 'HTML') {
		&amp;dump_html($grib, $dump);
	} elsif ($$dump{'format'} eq 'CDL') {
		&amp;dump_cdl($grib, $dump);
	} elsif ($$dump{'format'} eq 'RAW') {
		&amp;dump_raw($grib, $dump);
	} elsif ($$dump{'format'} eq 'F32') {
		&amp;dump_f32($grib, $dump);
	} elsif ($$dump{'format'} eq 'TEXT') {
		&amp;dump_text($grib, $dump);
	} else {
		die "unsupported output format\n";
	}
}

sub floatconv($) {
	my($m) = @_;
	my($s, $a, $b) = ($m &amp; 0x80000000,
		($m &amp; 0x7F000000) &gt;&gt; 24, $m &amp; 0x00FFFFFF);
	$s = ($s ? -1 : 1);
	return $s * (2 ** -24) * $b * (16 ** ($a - 64));
}

sub scale_grid($\%) {
	my($val, $grib) = @_;
	my($ref, $d, $e) = ($$grib{'b_refvalue'},
		$$grib{'p_decimal_factor'}, $$grib{'b_scale_e'});
	return ($ref + $val * (2 ** $e)) / (10 ** $d);
}

sub expand_iaxis(\%) {
	my($grib) = @_;
	my($ni) = $$grib{'g_ni'};
	my($x0) = $$grib{'g_lo1'};
	my($dx) = ($$grib{'g_lo2'} - $x0) / $ni;
	my($i, @ans);
	for ($i = 0; $i &lt; $ni; $i++) {
		push @ans, $x0 + $dx * $i;
	}
	return @ans;
}

sub expand_jaxis(\%) {
	my($grib) = @_;
	my($ni) = $$grib{'g_nj'};
	my($x0) = $$grib{'g_la1'};
	my($dx) = ($$grib{'g_la2'} - $x0) / $ni;
	my($i, @ans);
	for ($i = 0; $i &lt; $ni; $i++) {
		push @ans, $x0 + $dx * $i;
	}
	return @ans;
}

sub expand_grid(\%) {
	my($grib) = @_;
	die "spherical harmonic coefficient not supported"
		if $$grib{'b_harmonic'};
	die "complex/second compression not supported"
		if $$grib{'b_compress2'};
	my($section) = substr($$grib{':sec4:'}, 11);

	my($ni, $nj, $nk) = ($$grib{'g_ni'}, $$grib{'g_nj'}, $$grib{'b_nbits'});
	my($i, $j, $k, @result, $row, $bits, $ofs);
	@result = ();
	$bits = unpack('B*', $section);
	$ofs = 0;
	for ($j = 0; $j &lt; $nj; $j++) {
		$row = [];
		for ($i = 0; $i &lt; $ni; $i++) {
			$val = 0;
			for ($k = $nk - 1; $k &gt;= 0; $k--) {
				$val |= (substr($bits, $ofs, 1) &lt;&lt; $k);
				$ofs++;
			}
			push @$row, $val;
		}
		push @result, $row;
	}
	return @result;
}

sub dequote($) {
	my($val) = @_;
	if ($val =~ /^:/) {
		$val =~ s/:\s*://g;
	} else {
		$val =~ s/X\s*X//g;
	}
	return $val;
}

sub shadecolor($$) {
	my($val, $nbits) = @_;
	my($rel) = int(6 * 16 * $val / (2 ** $nbits));
	my($hi) = int($rel / 16);
	my($lo) = $rel % 16;
	my($R, $G, $B);
	if ($hi == 0) {
		$R = 8 - $lo / 2; $G = 0; $B = 15;
	} elsif ($hi == 1) {
		$R = 0; $G = $lo; $B = 15;
	} elsif ($hi == 2) {
		$R = 0; $G = 15; $B = 15 - $lo;
	} elsif ($hi == 3) {
		$R = $lo; $G = 15; $B = 0;
	} elsif ($hi == 4) {
		$R = 15; $G = 15 - $lo; $B = 0;
	} elsif ($hi == 5) {
		$R = 15; $G = $lo; $B = $lo;
	} else {
		die "#shadecolor val=$val nbits=$nbits hi=$hi lo=$lo";
	}
	return sprintf(' bgcolor="#%1xF%1xF%1xF"', $R, $G, $B);
}

sub dump_cdl(\%\%) {
	my($grib, $dump) = @_;
	my($ni, $nj) = ($$grib{'g_ni'}, $$grib{'g_nj'});
	print dequote(&lt;&lt;EOF);
X	Xnetcdf gribdump {
X	Xdimensions:
X	X lat = $nj, lon = $ni ;
X	Xvariables:
X       X float lat(lat), lon(lon), var(lat, lon);
X	X	lat:long_name = "latitude" ;
X	X	lat:units = "degree_N" ;
X	X	lon:long_name = "longitude" ;
X	X	lon:units = "degree_E" ;
EOF
	foreach (sort keys %$grib) {
		next unless (/^[a-z]/);
		$key = $_;
		$val = $$grib{$_};
		$note = $GRIBTAB{$key}-&gt;{$val + 0};
		if ($note) {
			print "\t:$key = \"$note\" ;\n";
			print "\t:${key}_code = $val ;\n";
		} else {
			print "\t:$key = \"$val\" ;\n";
		}
EOF
	}
	$key = 'p_element';
	$note = $GRIBTAB{$key}-&gt;{$$grib{$key} + 0};
	$note = substr($note, 0, 20) if length($note) &gt; 20;
	print "\tvar:long_name = \"$note\" ;\n";
	$key = 'p_units';
	$note = $GRIBTAB{$key}-&gt;{$$grib{$key} + 0};
	print "\tvar:units = \"$note\" ;\n";
	$key = 'p_create_center';
	$note = $GRIBTAB{$key}-&gt;{$$grib{$key} + 0};
	print "\t:institute = \"$note\" ;\n";
	if ($$dump{'body'}) {
		my(@grid) = expand_grid(%$grib);
		my(@iaxis) = expand_iaxis(%$grib);
		my(@jaxis) = expand_jaxis(%$grib);
		print "data:\n";
		if (@iaxis) {
			print " lon = ", join(', ', @iaxis), ";\n";
		}
		if (@jaxis) {
			print " lat = ", join(', ', @jaxis), ";\n";
		}
		print " var = \n";
		my($row, $val);
		my $nbits = $$grib{'b_nbits'};
		my $fmt = $$dump{'printf'} || sprintf("%%#%d.%dg",
			($nbits / 3) + 3, ($nbits / 3) + 1);
		my(@array) = ();
		foreach $row (@grid) {
			my(@row) = ();
			foreach (@$row) {
				$val = scale_grid($_, %$grib);
				$val *= $$dump{'factor'};
				$val = sprintf($fmt, $val);
				push @row, $val;
			}
			push @array, join(', ', @row);
		}
		print join(",\n", @array), ";\n";
	}
	print "}\n";
}

sub dump_html(\%\%) {
	my($grib, $dump) = @_;
	print dequote(&lt;&lt;EOF);
:	:&lt;?xml version="1.0" standalone="no"?&gt;
:	:&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
:	: "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
:	:&lt;html&gt;
EOF
	if ($$dump{'pandora'}) {
		print dequote(&lt;&lt;EOF);
:		:&lt;head&gt;
:		:&lt;title&gt;GRIB dump of $$grib{'INPUT_FILE'}&lt;/title&gt;
:		:&lt;/head&gt;
:		:&lt;body&gt;
:		:&lt;h1&gt;data list&lt;/h1&gt;
:		:&lt;img src="data.gif"&gt;
:		:&lt;ul&gt;
:		: &lt;li&gt;&lt;a href="data.gif"&gt;GIF&lt;/a&gt;
:		: &lt;li&gt;&lt;a href="data.jpeg"&gt;JPEG&lt;/a&gt;
:		: &lt;li&gt;&lt;a href="data.png"&gt;PNG&lt;/a&gt;
:		: &lt;li&gt;&lt;a href="data.f32"&gt;IEEE 32bit float big-endian&lt;/a&gt;
:		: &lt;li&gt;&lt;a href="data.txt"&gt;plain text&lt;/a&gt;
:		: &lt;li&gt;&lt;a href="meta.html"&gt;metadata&lt;/a&gt;
:		:&lt;/ul&gt;
EOF
	} else {
		print dequote(&lt;&lt;EOF);
:		:&lt;head&gt;
:		:&lt;title&gt;GRIB dump of $$grib{'INPUT_FILE'}&lt;/title&gt;
:		:&lt;/head&gt;
:		:&lt;body&gt;
:		:&lt;h1&gt;GRIB dump of $$grib{'INPUT_FILE'}&lt;/h1&gt;
EOF
	}
	if ($$dump{'head'}) {
		print dequote(&lt;&lt;EOF);
:		:&lt;h2&gt;metadata&lt;/h2&gt;
:		:&lt;table border="1"&gt;
EOF
		foreach (sort keys %$grib) {
			next unless (/^[a-z]/);
			$key = $_;
			$val = $$grib{$_};
			$note = $GRIBTAB{$key}-&gt;{$val + 0};
			$desc = $note || "&amp;nbsp;";
			print dequote(&lt;&lt;EOF);
:		:	&lt;tr&gt;
:		:	 &lt;td&gt;$key&lt;/td&gt;
:		:	 &lt;td key="$key"&gt;$val&lt;/td&gt;
:		:	 &lt;td&gt;$desc&lt;/td&gt;
:		:	&lt;/tr&gt;
EOF
		}
		print "&lt;/table&gt;\n";
	}
	if ($$dump{'body'}) {
		my(@grid) = expand_grid(%$grib);
		my(@iaxis) = expand_iaxis(%$grib);
		my(@jaxis) = expand_jaxis(%$grib);
		my($nbits, $fmt, $row);
		$nbits = $$grib{'b_nbits'};
		$fmt = $$dump{'printf'} ||
			sprintf("%%#%d.%dg",
			($nbits / 3) + 3, ($nbits / 3) + 1);
		print dequote(&lt;&lt;EOF);
:		:&lt;h2&gt;body&lt;/h2&gt;
:		:&lt;table&gt;
EOF
		my $val;
		my $color = undef;
		if (@iaxis) {
			print "&lt;tr&gt;\n";
			print "&lt;td&gt;&amp;nbsp;&lt;/td&gt;" if (@jaxis);
			for $val (@iaxis) {
				printf "&lt;th&gt;%05.1f&lt;/th&gt;", $val;
			}
			print "&lt;/tr&gt;\n";
		}
		my $j = 0;
		foreach $row (@grid) {
			print "&lt;tr&gt;\n";
			if (@jaxis) {
				printf "&lt;th&gt;%05.1f&lt;/th&gt;", $jaxis[$j];
			}
			foreach (@$row) {
				$color = shadecolor($_, $nbits)
					if $$dump{'color'};
				$val = scale_grid($_, %$grib);
				$val *= $$dump{'factor'};
				$val = sprintf($fmt, $val);
				print "&lt;td$color&gt;$val&lt;/td&gt;";
			}
			print "&lt;/tr&gt;\n";
			$j++;
		}
		print "&lt;/table&gt;\n";
	}
	print dequote(&lt;&lt;EOF);
:	:&lt;/body&gt;
:	:&lt;/html&gt;
EOF
}

sub dump_text(\%\%) {
	my($grib, $dump) = @_;
	if ($$dump{'head'}) {
		foreach (sort keys %$grib) {
			next if (/^:/);
			$key = $_;
			$val = $$grib{$_};
			$note = $GRIBTAB{$key}-&gt;{$val + 0};
			print "$key: $val", ($note ? "\t[$note]" : ""), "\n";
		}
		print "\n";
	}
	if ($$dump{'body'}) {
		my(@grid) = expand_grid(%$grib);
		my($nbits, $fmt, $row, $val);
		$nbits = $$grib{'b_nbits'};
		$fmt = $$dump{'printf'} ||
			sprintf("%%#%d.%dg ",
			($nbits / 3) + 3, ($nbits / 3) + 1);
		foreach $row (@grid) {
			foreach (@$row) {
				$val = scale_grid($_, %$grib);
				$val *= $$dump{'factor'};
				printf $fmt, $val;
			}
			print "\n";
		}
	}
}

sub get_section(\$) {
	my ($blockp) = @_;
	my($lenh, $len) = unpack("nC", $$blockp);
	$len += $lenh * 0x100;
	my($section) = substr($$blockp, 0, $len);
	$$blockp = substr($$blockp, $len);
	return ($len, $section);
}

sub ensign_short($) {
	my($ref) = @_;
	if ($ref &amp; 0x8000) {
		return -($ref &amp; 0x7FFF);
	} else {
		return $ref &amp; 0x7FFF;
	}
}

sub parse_section1($\%) {
	my ($section, $rp) = @_;
	my($f11, $f12, $p1, $p2, $c, $y, $m, $d, $h, $min, $dfact);
	($$rp{'p_table_version'},
		$$rp{'p_create_center'},
		$$rp{'p_create_process'},
		$$rp{'p_grid_system'},
		$flags,
		$$rp{'p_element'},
		$$rp{'p_plane_type'},
		$pl1,
		$pl2,
		$y,
		$m,
		$d,
		$h,
		$min,
		$$rp{'p_time_units'},
		$p1,
		$p2,
		$$rp{'p_time_type'},
		$$rp{'p_average_size'},
		$$rp{'p_average_miss'},
		$c,
		$$rp{'p_create_subcenter'},
		$dfact,
	) = unpack('x3C18nC3n', $section);

	$$rp{'p_decimal_factor'} = ensign_short($dfact);

	# postprocess create_center
	$GRIBTAB{"p_create_process"} =
		$GRIBTAB{"p_create_process:$$rp{'p_create_center'}"};

	# postprocess flags
	$$rp{'p_has_sec2'} = (($flags &amp; 0x80) ? 1 : 0);
	$$rp{'p_has_sec3'} = (($flags &amp; 0x40) ? 1 : 0);

	# postprocess date
	$y = ($c - 1) * 100 + $y;
	$$rp{'p_ref_date'} = sprintf("%04d-%02d-%02dt%02d%02d",
		$y, $m, $d, $h, $min);

	# postprocess element
	$$rp{'p_units'} = $$rp{'p_element'};

	# postprocess plane
	my($planetab) = $GRIBTAB{'plane_type'};
	my($planetype, $join, $title1, $units1, $title2, $units2) =
		split(/\|/, $$planetab{$$rp{'p_plane_type'}});
	$$rp{'p_plane_type'} .= " ($planetype)"
		if $planetype;
	if ($join) {
		$$rp{'p_plane'} = $pl1 * 0x10000 + $pl2;
		$$rp{'p_plane_title'} = $title1;
		$$rp{'p_plane_units'} = $units1;
	} elsif ($title1) {
		$$rp{'p_plane_title'} = $title1;
		$$rp{'p_plane_units'} = $units1;
		$$rp{'p_plane_title2'} = $title2;
		$$rp{'p_plane_units2'} = $units2;
	}

	# time type
	my($ttypetab) = $GRIBTAB{'|p_time_type'};
	my($timeop, $p1usage, $p2usage) =
		split(/\|/, $$ttypetab{$$rp{'p_time_type'}});
	$GRIBTAB{'p_time_type'}-&gt;{$$rp{'p_time_type'}} = $timeop;
	if ($p2usage eq '&lt;-') {
		$p1 = $p1 * 0x100 + $p2;
		$p2usage = undef;
	}
	$$rp{"p_time_$p1usage"} = $p1 if ($p1usage);
	$$rp{"p_time_$p2usage"} = $p2 if ($p2usage);
}

sub parse_section2($\%) {
	my ($section, $rp) = @_;
	($$rp{'g_nv'},
	$pvpl,
	$$rp{'grid_system'},
	) = unpack('x3C3', $section);

	if ($$rp{'grid_system'} == 0) {
		($$rp{'g_ni'},
		$$rp{'g_nj'},
		$la1, $$rp{'g_la1'},
		$lo1, $$rp{'g_lo1'},
		$flags,
		$la2, $$rp{'g_la2'},
		$lo2, $$rp{'g_lo2'},
		$$rp{'g_di'},
		$$rp{'g_dj'},
		$scanflags,
		$splatA, $splat,
		$splonA, $splon,
		$rotA, $rot,
		$scalelatA, $scalelat,
		$scalelonA, $scalelon,
		$scalefactorA, $scalefactor,
		) = unpack("x6nnCnCnCCnCnnnCx3CnCnCnCnCnCn", $section);
		&amp;mildeg_Cn_merge($la1, \$$rp{'g_la1'});
		&amp;mildeg_Cn_merge($lo1, \$$rp{'g_lo1'});
		&amp;mildeg_Cn_merge($la2, \$$rp{'g_la2'});
		&amp;mildeg_Cn_merge($lo2, \$$rp{'g_lo2'});
		$$rp{'g_di'} *= 0.001;
		$$rp{'g_dj'} *= 0.001;
	} else {
		die "unknown grid_format = $$rp{grid_format}";
	}

	$$rp{'g_has_didj'} = !!($flags &amp; 0x80) + 0;
	$$rp{'g_earth_shape'} = !!($flags &amp; 0x40) + 0;
	$$rp{'g_uv_definition'} = !!($flags &amp; 0x08) + 0;
	$$rp{'g_scan_positive_i'} = not ($scanflags &amp; 0x80) + 0;
	$$rp{'g_scan_positive_j'} = !!($scanflags &amp; 0x40) + 0;
	$$rp{'g_scan_j_first'} = !!($scanflags &amp; 0x20) + 0;
}

sub parse_section4($\%) {
	my ($section, $rp) = @_;
	my($flags, $scaleE, $refvalue, $nbits) = unpack("x3CnNC", $section);
	$$rp{'b_scale_e'} = ensign_short($scaleE);
	$$rp{'b_refvalue'} = &amp;floatconv($refvalue);
	$$rp{'b_nbits'} = $nbits;
	$$rp{'b_harmonic'} = (($flags &amp; 0x80) ? "1 (yes)" : 0);
	$$rp{'b_compress2'} = (($flags &amp; 0x40) ? "1 (yes)" : 0);
	$$rp{'b_integer'} = (($flags &amp; 0x20) ? "1 (yes)" : 0);
	my($extend_flags) = (($flags &amp; 0x10) ? "1 (yes)" : 0);
	$$rp{'b_ignored_bits'} = ($flags &amp; 0x0F);
}

sub mildeg_Cn_merge($\$) {
	my($hi, $lo) = @_;
	my($sign) = $hi &amp; 0x80;
	$hi = $hi &amp; 0x7F;
	$$lo += $hi * 0x10000;
	$$lo *= 0.001;
	$$lo *= -1 if $sign;
	$$lo;
}

sub table_init() {
	return if defined $GRIB1{'init'};
	$GRIBTAB{'init'} = 1;

	$GRIBTAB{'p_table_version'} = {
		3 =&gt; "current",
		2 =&gt; "old",
		1 =&gt; "old",
		0 =&gt; "old",
	};

	$GRIBTAB{'p_create_center'} = {
		7 =&gt; "US Weather Service - National Met. Center",
		8 =&gt; "US Weather Service - NWS Telecomms Gateway",
		9 =&gt; "US Weather Service - Field Stations",
		34 =&gt; "Japan Meteorological Agency - Tokyo",
		52 =&gt; "National Hurricane Center, Miami",
		54 =&gt; "Canadian Meteorological Service - Montreal",
		57 =&gt; "U.S. Air Force - Global Weather Center",
		58 =&gt; "US Navy - Fleet Numerical Oceanography Center",
		59 =&gt; "NOAA Forecast Systems Lab, Boulder CO",
		74 =&gt; "U.K. Met Office - Bracknell",
		85 =&gt; "French Weather Service - Toulouse",
		97 =&gt; "European Space Agency (ESA)",
		98 =&gt; "European Center for Medium-Range Weather Forecasts - Reading",
		99 =&gt; "DeBilt, Netherlands",
	};

	$GRIBTAB{'p_create_process:34'} = {
		4 =&gt; "GSM0103_T231L40",
		21 =&gt; "GSM0103_T106L40M25",
		52 =&gt; "GSM0103_T106L40M13",
	};

	$table = {
		0 =&gt; "(reserved)|1",
		1 =&gt; "pressure|Pa",
		2 =&gt; "pressure reduced to MSL|Pa",
		3 =&gt; "pressure tendency|Pa.s-1",
		4 =&gt; "potential vorticity|K.m2.kg-1.s-1",
		5 =&gt; "ICAO Standard Atmosphere Reference Height|m",
		6 =&gt; "geopotential|m2s-2",
		7 =&gt; "geopotential height|m",
		8 =&gt; "geometric height|m",
		9 =&gt; "standard deviation of height|m",
		10 =&gt; "total ozone|Dobson",
		11 =&gt; "temperature|K",
		12 =&gt; "virtual temperature|K",
		13 =&gt; "potential temperature|K",
		14 =&gt; "pseudo-adiabatic potential temperature|K",
		15 =&gt; "maximum temperature|K",
		16 =&gt; "minumum temperature|K",
		17 =&gt; "dew point temperature|K",
		18 =&gt; "dew point depression|K",
		19 =&gt; "minumum temperature|K",
		20 =&gt; "visibility|m",
		21 =&gt; "radar spectra(1)|1",
		22 =&gt; "radar spectra(2)|1",
		23 =&gt; "radar spectra(3)|1",
		24 =&gt; "parcel lifted index to 500hPa|K",
		25 =&gt; "temperature anomaly|K",
		26 =&gt; "pressure anomaly|Pa",
		27 =&gt; "geopotential height anomaly|Pa",
		31 =&gt; "wind direction|degree",
		32 =&gt; "wind speed|m.s-1",
		33 =&gt; "u component of wind speed|m.s-1",
		34 =&gt; "v component of wind speed|m.s-1",
		35 =&gt; "stream function|m2s-1",
		36 =&gt; "velocity potential|m2s-1",
		37 =&gt; "Montgomery stream function|m2s-1",
		38 =&gt; "vertical velocity in sigma|s-1",
		39 =&gt; "vertical velocity|Pa.s-1",
		40 =&gt; "vertical velocity|m.s-1",
		41 =&gt; "absolute vorticity|s-1",
		42 =&gt; "absolute divergence|s-1",
		43 =&gt; "relative vorticity|s-1",
		44 =&gt; "relative divergence|s-1",
		61 =&gt; "total precipitation|kg.m-2.s-1",
		71 =&gt; "total cloud amount|percent",
	};
	foreach $key (%$table) {
		($GRIBTAB{'p_element'}-&gt;{$key}, 
		 $GRIBTAB{'p_units'}-&gt;{$key}) = split(/\|/, $$table{$key});
	}

	$GRIBTAB{'p_plane_type'} = {
		1 =&gt; "surface",
		2 =&gt; "cloud base level",
		3 =&gt; "cloud top level",
		4 =&gt; "0 deg C isotherm level",
		5 =&gt; "adiabatic condensation level (parcel lifted from surface)",
		6 =&gt; "maximum wind speed level",
		7 =&gt; "tropopause level",
		8 =&gt; "nominal top of atmosphere",
		9 =&gt; "sea bottom",
		20 =&gt; "isentropic level|JOIN|temperature|0.01K||",
		100 =&gt; "isobaric level|JOIN|pressure|hPa||",
		101 =&gt; "layer between isobaric levels||top pressure|kPa|bottom pressure|kPa",
		102 =&gt; "mean sea level|||||",
		103 =&gt; "fixed height level|JOIN|height|m||",
	};

	$GRIBTAB{'p_time_units'} = {
		0 =&gt; "m",
		1 =&gt; "h",
		2 =&gt; "day",
		3 =&gt; "calendar_month",
		4 =&gt; "calendar_year",
		5 =&gt; "10 calendar_year",
		6 =&gt; "30 calendar_year",
		7 =&gt; "100 calendar_year",
		10 =&gt; "3 h",
		11 =&gt; "6 h",
		12 =&gt; "12 h",
		254 =&gt; "s",
	};

	$GRIBTAB{'|p_time_type'} = {
		0 =&gt; "anal-fcst|fcst|",
		1 =&gt; "initial||",
		2 =&gt; "range|fcst1|fcst2",
		3 =&gt; "average|fcst1|fcst2",
		4 =&gt; "sum|fcst1|fcst2",
		5 =&gt; "difference|fcst1|fcst2",
		10 =&gt; "|fcst|&lt;-",
		113 =&gt; "average-base|fcst|base-step",
		114 =&gt; "sum-base|fcst|base-step",
		115 =&gt; "average-fcst|fcst|fcst-step",
		116 =&gt; "sum-fcst|fcst|fcst-step",
		117 =&gt; "average|fcst|step",
		118 =&gt; "initial-variance||base-step",
		119 =&gt; "standard-deviation|fcst|fcst-step",
		123 =&gt; "analysis-average||base-step",
		124 =&gt; "analysis-average||base-step",
	};

	$GRIBTAB{'grid_system'} = {
		0 =&gt; "Equidistant Cylindrical",
		1 =&gt; "Mercator",
		2 =&gt; "Gnomonic",
		3 =&gt; "Lambert Conformal",
		4 =&gt; "Gaussian Latitude",
		5 =&gt; "Polar Stereographic",
		6 =&gt; "UTM",
		13 =&gt; "Oblique Lambert Conformal",
		50 =&gt; "Spherical Harmonic Coefficients",
		90 =&gt; "Space View Orthographic",
	};

	$GRIBTAB{'g_earth_shape'} = {
		0 =&gt; "sphere with radius 6367.47km",
		1 =&gt; "IAU 1965 daentai, r = 6378.160, 1/f = 297.0",
	};

	$GRIBTAB{'g_uv_definition'} = {
		1 =&gt; "east-and-north" ,
		0 =&gt; "grid-xy",
	};

	$GRIBTAB{'b_compress2'} = {
		1 =&gt; "complex or second compression" ,
		0 =&gt; "simple compression",
	};

	$GRIBTAB{'b_harmonic'} = {
		1 =&gt; "spherical harmonic coefficients" ,
		0 =&gt; "grid data",
	};

	$GRIBTAB{'b_integer'} = {
		1 =&gt; "integer" ,
		0 =&gt; "floating point",
	};

	my($yesno) = { 0 =&gt; "no", 1 =&gt; "yes" };
	$GRIBTAB{'p_has_sec2'} = $yesno;
	$GRIBTAB{'p_has_sec3'} = $yesno;
	$GRIBTAB{'g_has_didj'} = $yesno;
	$GRIBTAB{'g_scan_j_first'} = $yesno;
	$GRIBTAB{'g_scan_positive_i'} = $yesno;
	$GRIBTAB{'g_scan_positive_j'} = $yesno;
}

sub scan_file ($\@) {
	my ($fnam, $gribsp) = @_;
        my($block);
	table_init;
	open(INPUT, "&lt;$fnam") || die "open $fnam";
	while (1) {
		$/ = "GRIB";
		last unless ($block = &lt;INPUT&gt;);
		warn "header skipped before GRIB" if (length($block) != 4);
		read(INPUT, $block, 4) == 4 || die "read error 1";
		my($lenh, $len, $gribver) = unpack("nCC", $block);
		die "GRIB $gribver not supported" if ($gribver != 1);
		$len += $lenh * 0x100;
		$lenh = $len - 8;
		read(INPUT, $block, $lenh) == $lenh || die "read error 2";
		die "BROKEN RECORD" if (substr($block, -4) ne "7777");
		my(%grib) = ('version' =&gt; $gribver, 'INPUT_FILE' =&gt; $fnam,
			'length' =&gt; $len);
		my($section);
		($grib{'length1'}, $section) = &amp;get_section(\$block);
		&amp;parse_section1($section, \%grib);
		if ($grib{'p_has_sec2'}) {
			($grib{'length2'}, $section) = &amp;get_section(\$block);
			&amp;parse_section2($section, \%grib);
		}
		if ($grib{'p_has_sec3'}) {
			($grib{'length3'}, $grib{':sec3:'})
				= &amp;get_section(\$block);
		}
		($grib{'length4'}, $section) = &amp;get_section(\$block);
		&amp;parse_section4($section, \%grib);
		$grib{':sec4:'} = $section;
		die "section 5 error" if $block !~ /^7777/;

		push @$gribsp, \%grib;
	}
	close(INPUT);
}
</pre></body></html>