#!/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"; }
	&scan_file($_, \@gribs);
}

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

exit 0;

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

sub floatconv($) {
	my($m) = @_;
	my($s, $a, $b) = ($m & 0x80000000,
		($m & 0x7F000000) >> 24, $m & 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 < $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 < $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 < $nj; $j++) {
		$row = [];
		for ($i = 0; $i < $ni; $i++) {
			$val = 0;
			for ($k = $nk - 1; $k >= 0; $k--) {
				$val |= (substr($bits, $ofs, 1) << $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(<<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}->{$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}->{$$grib{$key} + 0};
	$note = substr($note, 0, 20) if length($note) > 20;
	print "\tvar:long_name = \"$note\" ;\n";
	$key = 'p_units';
	$note = $GRIBTAB{$key}->{$$grib{$key} + 0};
	print "\tvar:units = \"$note\" ;\n";
	$key = 'p_create_center';
	$note = $GRIBTAB{$key}->{$$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(<<EOF);
:	:<?xml version="1.0" standalone="no"?>
:	:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
:	: "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
:	:<html>
EOF
	if ($$dump{'pandora'}) {
		print dequote(<<EOF);
:		:<head>
:		:<title>GRIB dump of $$grib{'INPUT_FILE'}</title>
:		:</head>
:		:<body>
:		:<h1>data list</h1>
:		:<img src="data.gif">
:		:<ul>
:		: <li><a href="data.gif">GIF</a>
:		: <li><a href="data.jpeg">JPEG</a>
:		: <li><a href="data.png">PNG</a>
:		: <li><a href="data.f32">IEEE 32bit float big-endian</a>
:		: <li><a href="data.txt">plain text</a>
:		: <li><a href="meta.html">metadata</a>
:		:</ul>
EOF
	} else {
		print dequote(<<EOF);
:		:<head>
:		:<title>GRIB dump of $$grib{'INPUT_FILE'}</title>
:		:</head>
:		:<body>
:		:<h1>GRIB dump of $$grib{'INPUT_FILE'}</h1>
EOF
	}
	if ($$dump{'head'}) {
		print dequote(<<EOF);
:		:<h2>metadata</h2>
:		:<table border="1">
EOF
		foreach (sort keys %$grib) {
			next unless (/^[a-z]/);
			$key = $_;
			$val = $$grib{$_};
			$note = $GRIBTAB{$key}->{$val + 0};
			$desc = $note || "&nbsp;";
			print dequote(<<EOF);
:		:	<tr>
:		:	 <td>$key</td>
:		:	 <td key="$key">$val</td>
:		:	 <td>$desc</td>
:		:	</tr>
EOF
		}
		print "</table>\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(<<EOF);
:		:<h2>body</h2>
:		:<table>
EOF
		my $val;
		my $color = undef;
		if (@iaxis) {
			print "<tr>\n";
			print "<td>&nbsp;</td>" if (@jaxis);
			for $val (@iaxis) {
				printf "<th>%05.1f</th>", $val;
			}
			print "</tr>\n";
		}
		my $j = 0;
		foreach $row (@grid) {
			print "<tr>\n";
			if (@jaxis) {
				printf "<th>%05.1f</th>", $jaxis[$j];
			}
			foreach (@$row) {
				$color = shadecolor($_, $nbits)
					if $$dump{'color'};
				$val = scale_grid($_, %$grib);
				$val *= $$dump{'factor'};
				$val = sprintf($fmt, $val);
				print "<td$color>$val</td>";
			}
			print "</tr>\n";
			$j++;
		}
		print "</table>\n";
	}
	print dequote(<<EOF);
:	:</body>
:	:</html>
EOF
}

sub dump_text(\%\%) {
	my($grib, $dump) = @_;
	if ($$dump{'head'}) {
		foreach (sort keys %$grib) {
			next if (/^:/);
			$key = $_;
			$val = $$grib{$_};
			$note = $GRIBTAB{$key}->{$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 & 0x8000) {
		return -($ref & 0x7FFF);
	} else {
		return $ref & 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 & 0x80) ? 1 : 0);
	$$rp{'p_has_sec3'} = (($flags & 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'}->{$$rp{'p_time_type'}} = $timeop;
	if ($p2usage eq '<-') {
		$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);
		&mildeg_Cn_merge($la1, \$$rp{'g_la1'});
		&mildeg_Cn_merge($lo1, \$$rp{'g_lo1'});
		&mildeg_Cn_merge($la2, \$$rp{'g_la2'});
		&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 & 0x80) + 0;
	$$rp{'g_earth_shape'} = !!($flags & 0x40) + 0;
	$$rp{'g_uv_definition'} = !!($flags & 0x08) + 0;
	$$rp{'g_scan_positive_i'} = not ($scanflags & 0x80) + 0;
	$$rp{'g_scan_positive_j'} = !!($scanflags & 0x40) + 0;
	$$rp{'g_scan_j_first'} = !!($scanflags & 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'} = &floatconv($refvalue);
	$$rp{'b_nbits'} = $nbits;
	$$rp{'b_harmonic'} = (($flags & 0x80) ? "1 (yes)" : 0);
	$$rp{'b_compress2'} = (($flags & 0x40) ? "1 (yes)" : 0);
	$$rp{'b_integer'} = (($flags & 0x20) ? "1 (yes)" : 0);
	my($extend_flags) = (($flags & 0x10) ? "1 (yes)" : 0);
	$$rp{'b_ignored_bits'} = ($flags & 0x0F);
}

sub mildeg_Cn_merge($\$) {
	my($hi, $lo) = @_;
	my($sign) = $hi & 0x80;
	$hi = $hi & 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 => "current",
		2 => "old",
		1 => "old",
		0 => "old",
	};

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

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

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

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

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

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

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

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

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

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

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

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

	my($yesno) = { 0 => "no", 1 => "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, "<$fnam") || die "open $fnam";
	while (1) {
		$/ = "GRIB";
		last unless ($block = <INPUT>);
		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' => $gribver, 'INPUT_FILE' => $fnam,
			'length' => $len);
		my($section);
		($grib{'length1'}, $section) = &get_section(\$block);
		&parse_section1($section, \%grib);
		if ($grib{'p_has_sec2'}) {
			($grib{'length2'}, $section) = &get_section(\$block);
			&parse_section2($section, \%grib);
		}
		if ($grib{'p_has_sec3'}) {
			($grib{'length3'}, $grib{':sec3:'})
				= &get_section(\$block);
		}
		($grib{'length4'}, $section) = &get_section(\$block);
		&parse_section4($section, \%grib);
		$grib{':sec4:'} = $section;
		die "section 5 error" if $block !~ /^7777/;

		push @$gribsp, \%grib;
	}
	close(INPUT);
}
