#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <dirent.h>
#include <math.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
#ifdef NIO_LIB_ONLY
#include "niohlu.h"
#include "nioNresDB.h"
#include "nioCallbacks.h"
#include "cmpf.h"
#else
#include <ncarg/hlu/hlu.h>
#include <ncarg/hlu/NresDB.h>
#include <ncarg/hlu/Callbacks.h>
#include <ncarg/hlu/hluutil.h>
#endif
#include "defs.h"
#include <netcdf.h>
#include "NclDataDefs.h"
#include "NclFileInterfaces.h"
#include "NclMdInc.h"
#include "DataSupport.h"
#include "date.h"
#include "NclGRIB.h"
#include "NclFile.h"

# include "NclGRIB2.h"

static void Grib2FreeCodeTableRec(
#if NhlNeedProto
g2codeTable *ct
#endif
);

static NhlErrorTypes Grib2ReadCodeTable(
#if NhlNeedProto
char *center, 
int secid, 
char *table, 
int oct, 
int cat,
g2codeTable *ct
#endif
);

#define GRIB2_MISSING_LEVEL_VAL     -9999.0

static void *vbuf;

const char  *grib2_codetable_dir = NULL;
int g2_codetable_dirname_len = 0;

#define PI 3.14159265358979323846
static double RadPerDeg = PI / 180.0;
static double DegPerRad = 180.0 / PI;
#define EAST 1
#define WEST -1
/* GRIB table 3.2 -- we cannot handle oblate spheriods so default to 0 or 6 in that case 
 * units are meters 
 */
static double Earth_Radius[] =
{ 6.367470e6, -999.0, -999.0, -999.0, -999.0, -999.0, 6.371229e6 };


unsigned int
g2rightrot(unsigned int x, unsigned int n)
{
    while (n > 0) {
        if ((x & 1) == 1)
            x = (x >> 1) | ~(~0U >> 1);
        else
            x = (x >> 1);
        n--;
    }

    return x;
}

unsigned int
g2getbits(unsigned int x, int p, int n) {
    return (x >> (p + 1 - n)) & ~(~0 << n);
}


/***
static void g2GenAtts
#if     NhlNeedProto
(Grib2ParamList *thevarrec, Grib2AttInqRecList **lat_att_list, int *nlatatts, 
Grib2AttInqRecList **lon_att_list, int *lonatts, int do_rot, int grid_oriented,
Grib2AttInqRecList **rot_att_list, int *rotatts)
#endif
);
***/
void g2GenAtts
#if NhlNeedProto
(Grib2ParamList *thevarrec, Grib2AttInqRecList **lat_att_list, int *nlatatts, 
Grib2AttInqRecList **lon_att_list, int *lonatts, int do_rot, int grid_oriented,
Grib2AttInqRecList **rot_att_list, int *rotatts)
#endif
{
#ifdef NOTNOW
	Grib2AttInqRecList* tmp_att_list_ptr;
	NclQuark *tmp_string = NULL;
	ng_size_t tmp_dimsizes = 1;

	tmp_att_list_ptr = (*lat_att_list_ptr);
	(*lat_att_list_ptr) = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
	(*lat_att_list_ptr)->next = tmp_att_list_ptr;
	(*lat_att_list_ptr)->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
	(*lat_att_list_ptr)->att_inq->name = NrmStringToQuark("units");
	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark("degrees_north");
	(*lat_att_list_ptr)->att_inq->thevalue = (NclMultiDValData)_NclCreateVal( NULL, NULL, Ncl_MultiDValData, 0, (void*) tmp_string, NULL, 1 , &tmp_dimsizes, PERMANENT, NULL, nclTypestringClass);
	(*nlatatts)++;

	tmp_att_list_ptr = (*lat_att_list_ptr);
	(*lat_att_list_ptr) = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
	(*lat_att_list_ptr)->next = tmp_att_list_ptr;
	(*lat_att_list_ptr)->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
	(*lat_att_list_ptr)->att_inq->name = NrmStringToQuark("grid_description");
	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark(grid[thevarrec->grid_tbl_index].grid_name);
	(*lat_att_list_ptr)->att_inq->thevalue = (NclMultiDValData)_NclCreateVal( NULL, NULL, Ncl_MultiDValData, 0, (void*) tmp_string, NULL, 1 , &tmp_dimsizes, PERMANENT, NULL, nclTypestringClass);
	(*nlatatts)++;

	tmp_att_list_ptr = (*lat_att_list_ptr);
	(*lat_att_list_ptr) = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
	(*lat_att_list_ptr)->next = tmp_att_list_ptr;
	(*lat_att_list_ptr)->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
	(*lat_att_list_ptr)->att_inq->name = NrmStringToQuark("long_name");
	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark("latitude");
	(*lat_att_list_ptr)->att_inq->thevalue = (NclMultiDValData)_NclCreateVal( NULL, NULL, Ncl_MultiDValData, 0, (void*) tmp_string, NULL, 1 , &tmp_dimsizes, PERMANENT, NULL, nclTypestringClass);
	(*nlatatts)++;

	tmp_att_list_ptr = (*lon_att_list_ptr);
	(*lon_att_list_ptr) = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
	(*lon_att_list_ptr)->next = tmp_att_list_ptr;
	(*lon_att_list_ptr)->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
	(*lon_att_list_ptr)->att_inq->name = NrmStringToQuark("units");
	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark("degrees_east");
	(*lon_att_list_ptr)->att_inq->thevalue = (NclMultiDValData)_NclCreateVal( NULL, NULL, Ncl_MultiDValData, 0, (void*) tmp_string, NULL, 1 , &tmp_dimsizes, PERMANENT, NULL, nclTypestringClass);
	(*nlonatts)++;

	tmp_att_list_ptr = (*lon_att_list_ptr);
	(*lon_att_list_ptr) = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
	(*lon_att_list_ptr)->next = tmp_att_list_ptr;
	(*lon_att_list_ptr)->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
	(*lon_att_list_ptr)->att_inq->name = NrmStringToQuark("grid_description");
	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark(grid[thevarrec->grid_tbl_index].grid_name);
(*lon_att_list_ptr)->att_inq->thevalue = (NclMultiDValData)_NclCreateVal( NULL, NULL, Ncl_MultiDValData, 0, (void*) tmp_string, NULL, 1 , &tmp_dimsizes, PERMANENT, NULL, nclTypestringClass);
	(*nlonatts)++;

	tmp_att_list_ptr = (*lon_att_list_ptr);
	(*lon_att_list_ptr) = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
	(*lon_att_list_ptr)->next = tmp_att_list_ptr;
	(*lon_att_list_ptr)->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
	(*lon_att_list_ptr)->att_inq->name = NrmStringToQuark("long_name");
	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark("longitude");
	(*lon_att_list_ptr)->att_inq->thevalue = (NclMultiDValData)_NclCreateVal( NULL, NULL, Ncl_MultiDValData, 0, (void*) tmp_string, NULL, 1 , &tmp_dimsizes, PERMANENT, NULL, nclTypestringClass);
	(*nlonatts)++;

	if (do_rot) {
		Do_Rotation_Atts(NrmNULLQUARK,rot_att_list_ptr,nrotatts,grid_oriented);
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark(grid[thevarrec->grid_tbl_index].grid_name);
		Grib2PushAtt(rot_att_list_ptr,"grid_description",tmp_string,1,nclTypestringClass); (*nrotatts)++;
	}
#endif
    return;
}

void Grib2PushAtt
#if NhlNeedProto
(Grib2AttInqRecList **att_list_ptr,char* name,void *val,ng_size_t dimsize,NclObjClass type) 
#else
(att_list_ptr,name,val,dimsize,type) 
Grib2AttInqRecList **att_list_ptr;
char* name;
void *val;
ng_size_t dimsize;
NclObjClass type;
#endif
{
	Grib2AttInqRecList* tmp_att_list_ptr;

	tmp_att_list_ptr = (*att_list_ptr);
	(*att_list_ptr) = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
	(*att_list_ptr)->next = tmp_att_list_ptr;
	(*att_list_ptr)->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
	(*att_list_ptr)->att_inq->name = NrmStringToQuark(name);
	(*att_list_ptr)->att_inq->thevalue = (NclMultiDValData)_NclCreateVal(NULL, NULL,
            Ncl_MultiDValData, 0, (void *) val, NULL, 1 , &dimsize, PERMANENT, NULL, type);
}

static void g2Do_Rotation_Atts
(
	NrmQuark grid_name,
	Grib2AttInqRecList** rot_att_list,
	int* nrotatts,
	NhlBoolean grid_oriented
)
{
	NclQuark* tmp_string;
	
	char *note1[2] = {"u and v components of vector quantities are resolved relative to grid",
			  "u and v components of vector quantities are resolved relative to earth"};
	char *note2[2] = {"apply formulas to derive u and v components relative to earth",
			  "apply formulas to derive u and v components relative to grid"};
	char *formula_u[2] = {"Uearth = sin(rot)*Vgrid + cos(rot)*Ugrid",
			      "Ugrid = cos(rot)*Uearth - sin(rot)*Vearth"};
	char *formula_v[2] = {"Vearth = cos(rot)*Vgrid - sin(rot)*Ugrid",
			      "Vgrid = sin(rot)*Uearth + cos(rot)*Vearth"};
			  

	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark(grid_oriented ? note2[0] : note2[1]);
	Grib2PushAtt(rot_att_list,"note2",tmp_string,1,nclTypestringClass); (*nrotatts)++;

	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark(grid_oriented ? note1[0] : note1[1]);
	Grib2PushAtt(rot_att_list,"note1",tmp_string,1,nclTypestringClass); (*nrotatts)++;

	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark(grid_oriented ? formula_v[0] : formula_v[1]);
	Grib2PushAtt(rot_att_list,"formula_v",tmp_string,1,nclTypestringClass); (*nrotatts)++;

	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark(grid_oriented ? formula_u[0] : formula_u[1]);
	Grib2PushAtt(rot_att_list,"formula_u",tmp_string,1,nclTypestringClass); (*nrotatts)++;

	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark("radians");
	Grib2PushAtt(rot_att_list,"units",tmp_string,1,nclTypestringClass); (*nrotatts)++;

	if (grid_name > NrmNULLQUARK) {
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = grid_name;
		Grib2PushAtt(rot_att_list,"GridType",tmp_string,1,nclTypestringClass); (*nrotatts)++;
	}

	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark("vector rotation angle");
	Grib2PushAtt(rot_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nrotatts)++;
}


void g2GetThinnedLonParams
# if NhlNeedProto
(G2_GDS *gds,
 int nlat,
 double lo1,
 double lo2,
 int idir,
 int *nlon,
 double *di
)
# else
(gds, nlat, lo1, lo2, idir, nlon, di)
    G2_GDS *gds,
    int nlat;
    double lo1;
    double lo2;
    int idir;
    int *nlon;
    double *di;
# endif
{
    int pl_ix;
    int nmax = 0;
    int max_ix = 0;
    int i,
        n;
    double diff;

    *nlon = 0;
    pl_ix = gds->grid_list_num_oct_num;

    if (pl_ix <= 0) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,
            "NclGRIB2: Invalid thinned longitude grid");
        return;
    }

    for (i = 0; i < nlat; i++) {
        n = gds->grid_list_num_oct_opt[i];
        if (n > nmax) {
            nmax = n;
            max_ix = i;
        }
    }

    if (nmax == 0) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,
            "NclGRIB2: Invalid thinned longitude grid");
        return;
    }

    *nlon = nmax;
    if (idir == 1) {
        while (lo2 < lo1) {
            lo2 += 360;
        }
        diff = lo2 - lo1;
    } else {
        while (lo1 < lo2) {
            lo1 += 360;
        }
        diff = lo1 - lo2;
    }

    *di =  diff / (double) (*nlon - 1);

    return;
}

void g2GetThinnedLatParams
# if NhlNeedProto
/*(unsigned char *gds,*/
 (G2_GDS *gds,
 int nlon,
 double la1,
 double la2,
 int jdir,
 int *nlat,
 double *dj
)
# else
(gds, nlon, la1, la2, jdir, nlat, dj)
/*    unsigned char *gds;*/
    G2_GDS *gds,
    int nlon;
    double la1;
    double la2;
    int jdir;
    int *nlat;
    double *dj;
# endif
{
	
    int pl_ix;
    int nmax = 0;
    int max_ix = 0;
    int i,
        n;
    double diff;

    *nlat = 0;
    pl_ix = gds->grid_list_num_oct_num;

    if (pl_ix == -1) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,
            "NclGRIB2: Invalid thinned latitude grid");
        return;
    }

    for (i = 0; i < nlon; i++) {
        n = gds->grid_list_num_oct_opt[i];
        if (n > nmax) {
            nmax = n;
            max_ix = i;
        }
    }

    if (nmax == 0) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,
            "NclGRIB2: Invalid thinned latitude grid");
        return;
    }

    *nlat = nmax;
    if (jdir == 1) {
        diff = la2 - la1;
    } else {
        diff = la1 - la2;
    }

    *dj =  diff / (double)(*nlat - 1);
    
    return;
}

void g2GenLatLon 
#if NhlNeedProto
(Grib2ParamList* thevarrec, float** lat, ng_size_t* n_dims_lat, ng_size_t** dimsizes_lat, float** lon, ng_size_t* n_dims_lon, ng_size_t** dimsizes_lon,int xsize,int ysize, float lon_start,float lat_start, float lon_dir, float lat_dir)
#else
(thevarrec, lat, n_dims_lat, dimsizes_lat, lon, n_dims_lon, dimsizes_lon,xsize,ysize, lon_start,lat_start, lon_dir, lat_dir)
Grib2ParamList* thevarrec;
float** lat;
ng_size_t* n_dims_lat;
ng_size_t** dimsizes_lat;
float** lon;
ng_size_t* n_dims_lon;
ng_size_t** dimsizes_lon;
int xsize;
int ysize;
float lon_start;
float lat_start;
float lon_dir;
float lat_dir;
#endif
{
	int j;

*lat = (float*)NclMalloc(sizeof(float)*ysize);
*lon = (float*)NclMalloc(sizeof(float)*xsize);
    *dimsizes_lat = (ng_size_t*)NclMalloc(sizeof(ng_size_t));
    *dimsizes_lon = (ng_size_t*)NclMalloc(sizeof(ng_size_t));
    *n_dims_lat = 1;
    *n_dims_lon = 1;
    (*dimsizes_lat)[0] = ysize;
    (*dimsizes_lon)[0] = xsize;

    for (j = 0; j < ysize; j++) {
        (*lat)[j] = lat_start + lat_dir * j;
    }

    for (j = 0; j < xsize; j++) {
        (*lon)[j] = lon_start + lon_dir * j;
    }

	return;
}


void g2GetAtts_1
#if NhlNeedProto
(Grib2ParamList* thevarrec, Grib2AttInqRecList **lat_att_list_ptr, int *nlatatts,
 Grib2AttInqRecList **lon_att_list_ptr, int *nlonatts, int do_rot, int grid_oriented,
 Grib2AttInqRecList **rot_att_list_ptr, int *nrotatts)
#else
(thevarrec,lat_att_list_ptr, nlatatts, lon_att_list_ptr, nlonatts, do_rot,rot_att_list_ptr, nrotatts)
    Grib2ParamList *thevarrec;
    Grib2AttInqRecList **lat_att_list_ptr;
    int *nlatatts; 
    Grib2AttInqRecList **lon_att_list_ptr;
    int *nlonatts;
    int do_rot;
    int grid_oriented;
    Grib2AttInqRecList **rot_att_list_ptr;
    int *nrotatts;
#endif
{
	NclQuark *tmp_string = NULL;
	float *tmp_float = NULL;


	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark("MERCATOR");
	Grib2PushAtt(lat_att_list_ptr,"mpProjection",tmp_string,1,nclTypestringClass); (*nlatatts)++;

	tmp_float= (float*)NclMalloc(sizeof(float));
	*tmp_float = 0.0;
	Grib2PushAtt(lat_att_list_ptr,"mpCenterLatF",tmp_float,1,nclTypefloatClass); (*nlatatts)++;

	tmp_float= (float*)NclMalloc(sizeof(float));
	*tmp_float = 180.0;
	Grib2PushAtt(lat_att_list_ptr,"mpCenterLonF",tmp_float,1,nclTypefloatClass); (*nlatatts)++;

	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	*tmp_string = NrmStringToQuark("MERCATOR");
	Grib2PushAtt(lon_att_list_ptr,"mpProjection",tmp_string,1,nclTypestringClass); (*nlonatts)++;

	tmp_float= (float*)NclMalloc(sizeof(float));
	*tmp_float = 0.0;
	Grib2PushAtt(lon_att_list_ptr,"mpCenterLatF",tmp_float,1,nclTypefloatClass); (*nlonatts)++;

	tmp_float= (float*)NclMalloc(sizeof(float));
	*tmp_float = 180.0;
	Grib2PushAtt(lon_att_list_ptr,"mpCenterLonF",tmp_float,1,nclTypefloatClass); (*nlonatts)++;

    g2GenAtts(thevarrec, lat_att_list_ptr, nlatatts, lon_att_list_ptr, nlonatts,
            do_rot, grid_oriented, rot_att_list_ptr, nrotatts);

    return;
}




void g2GDSDimsOnlyGrid
#if NhlNeedProto
(
	Grib2ParamList* thevarrec, 
	float** lat, 
	int* n_dims_lat,
	ng_size_t** dimsizes_lat,
	float** lon,
	int* n_dims_lon,
	ng_size_t** dimsizes_lon,
	float** rot,
	int* n_dims_rot,
	ng_size_t **dimsizes_rot,
	Grib2AttInqRecList** lat_att_list, 
	int* nlatatts, 
	Grib2AttInqRecList** lon_att_list, 
	int* nlonatts,
	Grib2AttInqRecList** rot_att_list,
	int* nrotatts
)
#else
(thevarrec, lat, n_dims_lat, dimsizes_lat, lon, n_dims_lon, dimsizes_lon,
 rot, n_dims_rot, dimsizes_rot,
 lat_att_list,nlatatts,lon_att_list, nlonatts, rot_att_list, nrotatts)
GribParamList* thevarrec; 
float** lat; 
int* n_dims_lat;
ng_size_t** dimsizes_lat;
float** lon;
int* n_dims_lon;
ng_size_t** dimsizes_lon;
float** rot;
int* n_dims_rot;
ng_size_t **dimsizes_rot;
GribAttInqRecList** lat_att_list; 
int* nlatatts; 
GribAttInqRecList** lon_att_list; 
int* nlonatts;
GribAttInqRecList** rot_att_list;
int* nrotatts;
#endif
{
	G2_GDS *gds;
	int nlon,nlat;
	g2CETemplate *ce;

	/* 
	 * This can handle any grid that has the dimensions of the grid specified as 
	 * elements 7 and 8 of the g2clib grid template -- whatever the dims happen to be called
	 * currently includes templates 1,2,3, 31, 41, 42, 43, 90, and 110. 
	 */
	
	*lat = NULL;
	*lon = NULL;
	*rot = NULL;
	*n_dims_lat = 0;
	*dimsizes_lat = NULL;
	*n_dims_lon= 0;
	*dimsizes_lon = NULL;
	*n_dims_rot = 0;
	*dimsizes_rot = NULL;
	if((thevarrec->thelist == NULL)||(thevarrec->ref_rec == NULL)) 
		return;

	gds = (G2_GDS *) thevarrec->ref_rec->gds;
	if (gds == NULL) {
		return;
	}
	ce = (g2CETemplate *) gds->grid_template;

	nlon = ce->npts_along_parallel;
	nlat = ce->npts_along_meridian;
	if (nlon <= 1 || nlat <= 1 ) {
		return;
	}

	*dimsizes_lat = (ng_size_t*)NclMalloc(sizeof(ng_size_t));
	*dimsizes_lon = (ng_size_t*)NclMalloc(sizeof(ng_size_t));
	*(*dimsizes_lon) = nlon;
	*(*dimsizes_lat) = nlat;
	*n_dims_lat = 1;
	*n_dims_lon = 1;

	return;
}

/* Grid template 0 */

void g2GDSCEGrid
# if NhlNeedProto
(
	Grib2ParamList *thevarrec, 
	float **lat, 
	int *n_dims_lat, 
	ng_size_t **dimsizes_lat,
	float **lon, 
	int *n_dims_lon, 
	ng_size_t **dimsizes_lon, 
	float **rot, 
	int *n_dims_rot,
	ng_size_t **dimsizes_rot, 
	Grib2AttInqRecList **lat_att_list, 
	int *nlatatts, 
	Grib2AttInqRecList **lon_att_list, 
	int *nlonatts, 
	Grib2AttInqRecList **rot_att_list,
	int *nrotatts
)
# else
(thevarrec, lat, n_dims_lat, dimsizes_lat, lon, n_dims_lon, dimsizes_lon,
 rot, n_dims_rot, dimsizes_rot, lat_att_list, nlatatts, lon_att_list, nlonatts,
 rot_att_list, nrotatts)
    Grib2ParamList *thevarrec; 
    float **lat; 
    int *n_dims_lat;
    ng_size_t **dimsizes_lat;
    float **lon;
    int *n_dims_lon;
    ng_size_t **dimsizes_lon;
    float **rot;
    int *n_dims_rot;
    int **dimsizes_rot;
    Grib2AttInqRecList **lat_att_list; 
    int *nlatatts; 
    Grib2AttInqRecList **lon_att_list; 
    int *nlonatts;
    Grib2AttInqRecList **rot_att_list;
    int *nrotatts;
# endif
{
    G2_GDS *gds;
    double la1,la2,lo1,lo2;
    double di = 1;
    double dj = 1;
    int idir;
    int jdir;
    int is_thinned_lat;
    int is_thinned_lon;
    int i;
    float *tmp_float;
    NclQuark* tmp_string;
    int nlon, nlat;
    g2CETemplate *ce;
    double scale_factor;
    double start_lat;
	
    *lat = NULL;
    *n_dims_lat = 0;
    *dimsizes_lat = NULL;
    *lon = NULL;
    *n_dims_lon= 0;
    *dimsizes_lon= NULL;
    if ((thevarrec->thelist == NULL)  ||  (thevarrec->ref_rec == NULL)) 
        return;

    gds = (G2_GDS *) thevarrec->ref_rec->gds;
    if (gds == NULL) {
        return;
    }
    ce = (g2CETemplate *) gds->grid_template;

    nlon = ce->npts_along_parallel;
    nlat = ce->npts_along_meridian;

    /* all bits set indicates missing: missing means thinned */
    is_thinned_lon = (nlon == -1); 
    is_thinned_lat = (nlat == -1);
    if ((nlon <= 1 && !is_thinned_lon) || (nlat <= 1 && !is_thinned_lat)) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,
            "GdsCEGrid: Invalid grid detected");
        *lat = NULL;
        *n_dims_lat = 0;
        *dimsizes_lat = NULL;
        *lon = NULL;
        *n_dims_lon = 0;
        *dimsizes_lon = NULL;
        return;
    }

    idir = g2getbits(ce->scan_mode_flags, 7, 1) == 0 ? 1 : -1;
    jdir = g2getbits(ce->scan_mode_flags, 6, 1) == 0 ? -1 : 1;
    if (ce->subdiv_basic_angle != 0 && ce->angl_init_prod_domain != 0)
	    scale_factor = (double) ce->angl_init_prod_domain / 
		    (double) ce->subdiv_basic_angle;
    else 
	    scale_factor = 1.0 / (double) G2_SCALE_FACTOR;

    la1 = ce->lat_first_gridpt * scale_factor;
    lo1 = ce->lon_first_gridpt * scale_factor;
    la2 = ce->lat_last_gridpt * scale_factor;
    lo2 = ce->lon_last_gridpt * scale_factor;

    if (is_thinned_lon) {
        g2GetThinnedLonParams(gds, nlat, lo1, lo2, idir, &nlon, &di);
	thevarrec->gds->is_thinned_grid = 1;
    } else if (nlon > 1) {
	    if (lo1 == lo2) {
		    if (idir == 1) {
			    if (lo1 > 0) {
				    lo1 -= 360.0;
			    }
			    else {
				    lo2 += 360.0;
			    }
		    }
		    else {
			    if (lo1 > 0) {
				    lo2 -= 360.0;
			    }
			    else {
				    lo1 += 360.0;
			    }
		    }
	    }
	    if (idir == 1) {
		    float ti = lo2;
		    while (ti < lo1) {
			    ti += 360.0;
		    }
		    di = (ti - lo1) / (double) (nlon - 1);
	    }
	    else {
		    float ti = lo1;
		    while (ti < lo2) {
			    ti += 360.0;
		    }
		    di = (ti - lo2) / (double) (nlon - 1);
	    }
    }

    if (is_thinned_lat) {
	thevarrec->gds->is_thinned_grid = 1;
        g2GetThinnedLatParams(gds, nlon, la1, la2, jdir, &nlat, &dj);
    } else  if (nlat > 1) {
        /* Not specified: must be calculated from the endpoints and number of steps */
        dj = (la2 - la1) / (double) (nlat - 1);
        if (dj < 0)
            dj = -dj;
    }
			
    *dimsizes_lat = (ng_size_t *) NclMalloc(sizeof(ng_size_t));
    *dimsizes_lon = (ng_size_t *) NclMalloc(sizeof(ng_size_t));
    *(*dimsizes_lon) = nlon;
    *(*dimsizes_lat) = nlat;
    *n_dims_lat = 1;
    *n_dims_lon = 1;
    *lat = (float *) NclMalloc((unsigned)sizeof(float) * nlat);
    *lon = (float *) NclMalloc((unsigned)sizeof(float) * nlon);
    start_lat = jdir == 1 ? MIN(la1,la2) : MAX(la1,la2);

    for (i = 0; i < *(*dimsizes_lat) ; i++)
        (*lat)[i] = (float) (start_lat + jdir * i * dj) ;

    for (i = 0; i < *(*dimsizes_lon) ; i++)
        (*lon)[i] = (float)(lo1 + idir * i * di) ;

    /* save the scan mode flags -- needed for decoding the data */
    thevarrec->gds->scan_mode_offset = (int*)&(ce->scan_mode_flags) - (int*)ce;

	if (lon_att_list != NULL) {
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la1;
		Grib2PushAtt(lon_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;

		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo1;
		Grib2PushAtt(lon_att_list,"Lo1",tmp_float,1,nclTypefloatClass);
        (*nlonatts)++;

		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la2;
		Grib2PushAtt(lon_att_list,"La2",tmp_float,1,nclTypefloatClass);
        (*nlonatts)++;

		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo2;
		Grib2PushAtt(lon_att_list,"Lo2",tmp_float,1,nclTypefloatClass);
        (*nlonatts)++;

		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = di;
		Grib2PushAtt(lon_att_list,"Di",tmp_float,1,nclTypefloatClass);
        (*nlonatts)++;

		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = dj;
		Grib2PushAtt(lon_att_list,"Dj",tmp_float,1,nclTypefloatClass);
        (*nlonatts)++;

		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("degrees_east");
		Grib2PushAtt(lon_att_list,"units",tmp_string,1,nclTypestringClass);
        (*nlonatts)++;

		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		if (is_thinned_lat || is_thinned_lon)
			*tmp_string = NrmStringToQuark(
                    "Latitude/longitude Grid (Quasi-Regular)");
		else 
			*tmp_string = NrmStringToQuark("Latitude/Longitude");
		Grib2PushAtt(lon_att_list,"grid_type",tmp_string,1,nclTypestringClass);
        (*nlonatts)++;

		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("longitude");
		Grib2PushAtt(lon_att_list,"long_name",tmp_string,1,nclTypestringClass);
        (*nlonatts)++;
	}

	if (lat_att_list != NULL) {
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la1;
		Grib2PushAtt(lat_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;

		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo1;
		Grib2PushAtt(lat_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;

		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la2;
		Grib2PushAtt(lat_att_list,"La2",tmp_float,1,nclTypefloatClass); (*nlatatts)++;

		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo2;
		Grib2PushAtt(lat_att_list,"Lo2",tmp_float,1,nclTypefloatClass); (*nlatatts)++;

		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = di;
		Grib2PushAtt(lat_att_list,"Di",tmp_float,1,nclTypefloatClass); (*nlatatts)++;

		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = dj;
		Grib2PushAtt(lat_att_list,"Dj",tmp_float,1,nclTypefloatClass); (*nlatatts)++;

		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("degrees_north");
		Grib2PushAtt(lat_att_list,"units",tmp_string,1,nclTypestringClass); (*nlatatts)++;

		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		if (is_thinned_lon || is_thinned_lat)
			*tmp_string = NrmStringToQuark(
                    "Latitude/longitude Grid (Quasi-Regular)");
		else 
			*tmp_string = NrmStringToQuark("Latitude/Longitude");
		Grib2PushAtt(lat_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlatatts)++;

		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("latitude");
		Grib2PushAtt(lat_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlatatts)++;
	}

    return;
}

static void rot2ll
(
	double latpole,
	double lonpole,
	double latin,
	double lonin,
	double *latout,
	double *lonout
)
{
	double dtr = atan(1) / 45.0;
	double x,y,z;
	double rotang,sinrot,cosrot;
	double rx,ry,rz;
	double tlat,tlon;
	    
	/* convert to xyz coordinates */

	x = cos(latin * dtr) * cos(lonin * dtr);
	y = cos(latin * dtr) * sin(lonin * dtr);
	z = sin(latin * dtr);

	/*  rotate around y axis */
	rotang = - (latpole + 90) * dtr;
	sinrot = sin(rotang);
	cosrot = cos(rotang);
	ry = y;
	rx = x * cosrot + z * sinrot;
	rz = -x * sinrot + z * cosrot;
    
	/* convert back to lat/lon */

	tlat = asin(rz) / dtr;
	if (fabs(rx) > 0.0001) {
		tlon = atan2(ry,rx) / dtr;
	}
	else if (ry > 0) {
		tlon = 90.0;
	}
        else {
		tlon = -90.0;
	}
	/* remove the longitude rotation */

	tlon = tlon + lonpole;
	if (tlon < 0) {
		tlon = tlon + 360.0;
	}
	if (tlon > 360) {
		tlon = tlon - 360.0;
	}

	*latout = tlat;
	*lonout = tlon;

	return;
}
static void ll2rot
(
	double latpole,
	double lonpole,
	double latin,
	double lonin,
	double *latout,
	double *lonout
)
{
	double dtr = atan(1) / 45.0;
	double x,y,z;
	double rotang,sinrot,cosrot;
	double rx,ry,rz;
	double tlat,tlon;
	    
	tlon = lonin - lonpole;

	/* convert to xyz coordinates */

	x = cos(latin * dtr) * cos(tlon * dtr);
	y = cos(latin * dtr) * sin(tlon * dtr);
	z = sin(latin * dtr);

	/*  rotate around y axis */
	rotang = (latpole + 90) * dtr;
	sinrot = sin(rotang);
	cosrot = cos(rotang);
	ry = y;
	rx = x * cosrot + z * sinrot;
	rz = -x * sinrot + z * cosrot;
    
	/* convert back to lat/lon */

	tlat = asin(rz) / dtr;
	if (fabs(rx) > 0.0001) {
		tlon = atan2(ry,rx) / dtr;
	}
	else if (ry > 0) {
		tlon = 90.0;
	}
        else {
		tlon = -90.0;
	}

	if (tlon < -180) {
		tlon = tlon + 360.0;
	}
	if (tlon >= 180) {
		tlon = tlon - 360.0;
	}

	*latout = tlat;
	*lonout = tlon;

	return;
}

void g2GDSRLLGrid
# if NhlNeedProto
(
	Grib2ParamList *thevarrec, 
	float **lat, 
	int *n_dims_lat, 
	ng_size_t **dimsizes_lat,
	float **lon, 
	int *n_dims_lon, 
	ng_size_t **dimsizes_lon, 
	float **rot, 
	int *n_dims_rot,
	ng_size_t **dimsizes_rot, 
	Grib2AttInqRecList **lat_att_list, 
	int *nlatatts, 
	Grib2AttInqRecList **lon_att_list, 
	int *nlonatts, 
	Grib2AttInqRecList **rot_att_list,
	int *nrotatts
)
# else
(thevarrec, lat, n_dims_lat, dimsizes_lat, lon, n_dims_lon, dimsizes_lon,
 rot, n_dims_rot, dimsizes_rot, lat_att_list, nlatatts, lon_att_list, nlonatts,
 rot_att_list, nrotatts)
    Grib2ParamList *thevarrec; 
    float **lat; 
    int *n_dims_lat;
    ng_size_t **dimsizes_lat;
    float **lon;
    int *n_dims_lon;
    ng_size_t **dimsizes_lon;
    float **rot;
    int *n_dims_rot;
    ng_size_t **dimsizes_rot;
    Grib2AttInqRecList **lat_att_list; 
    int *nlatatts; 
    Grib2AttInqRecList **lon_att_list; 
    int *nlonatts;
    Grib2AttInqRecList **rot_att_list;
    int *nrotatts;
# endif
{
    G2_GDS *gds;
    double la1,la2,lo1,lo2;
    double di = 1;
    double dj = 1;
    int idir;
    int jdir;
    int is_thinned_lat;
    int is_thinned_lon;
    int i,j;
    float *tmp_float;
    NclQuark* tmp_string;
    int ni, nj;
    g2RotCETemplate *rll;
    int grid_oriented,do_rot;
    int do_180 = 0;
    NrmQuark grid_name;
    double scale_factor;
    double lasp;
    double losp;
    double rotang;
    double clat,llat,llon,rlat,rlon;
	
    *lat = NULL;
    *n_dims_lat = 0;
    *dimsizes_lat = NULL;
    *lon = NULL;
    *n_dims_lon= 0;
    *dimsizes_lon= NULL;
    if ((thevarrec->thelist == NULL)  ||  (thevarrec->ref_rec == NULL)) 
        return;

    gds = (G2_GDS *) thevarrec->ref_rec->gds;
    if (gds == NULL) {
        return;
    }
    rll = (g2RotCETemplate *) gds->grid_template;

    ni = rll->ce.npts_along_parallel;
    nj = rll->ce.npts_along_meridian;

    /* all bits set indicates missing: missing means thinned */
    is_thinned_lon = (ni == -1); 
    is_thinned_lat = (nj == -1);
    if ((ni <= 1 && !is_thinned_lon) || (nj <= 1 && !is_thinned_lat)) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,
            "GdsRLLGrid: Invalid grid detected");
        *lat = NULL;
        *n_dims_lat = 0;
        *dimsizes_lat = NULL;
        *lon = NULL;
        *n_dims_lon = 0;
        *dimsizes_lon = NULL;
        return;
    }

    idir = g2getbits(rll->ce.scan_mode_flags, 7, 1) == 0 ? 1 : -1;
    jdir = g2getbits(rll->ce.scan_mode_flags, 6, 1) == 0 ? -1 : 1;
    /* save the scan mode flags -- needed for decoding the data */
    thevarrec->gds->scan_mode_offset = (int*)&(rll->ce.scan_mode_flags) - (int*)rll;

    if (rll->ce.subdiv_basic_angle != 0 && rll->ce.angl_init_prod_domain != 0)
	    scale_factor = (double) rll->ce.angl_init_prod_domain / 
		    (double) rll->ce.subdiv_basic_angle;
    else 
	    scale_factor = 1.0 / (double) G2_SCALE_FACTOR;

    la1 = rll->ce.lat_first_gridpt * scale_factor;
    lo1 = rll->ce.lon_first_gridpt * scale_factor;
    la2 = rll->ce.lat_last_gridpt * scale_factor;
    lo2 = rll->ce.lon_last_gridpt * scale_factor;
    /* 
     * It does not seem to be completely consistent whether the following values have 
     * the scale factor applied, so as a heuristic, check to see if the values
     * are 'large'
     */
    if (fabs(rll->lat_south_pole_proj) > 100000) {
	    lasp = rll->lat_south_pole_proj * scale_factor;
	    losp = rll->lon_south_pole_proj * scale_factor;
    }
    else {
	    lasp = rll->lat_south_pole_proj * scale_factor;
	    losp = rll->lon_south_pole_proj * scale_factor;
    }


#if 0
    /* temp fix for test file */
    lo1 = -12.5;
#endif
    rotang = rll->rot_ang_proj;
    grid_oriented = g2getbits(rll->ce.res_comp_flags, 3, 1) == 0 ? 0 : 1;
    do_rot = 1;

    if (rotang != 0) {
	    NhlPError(NhlWARNING,NhlEUNKNOWN,
          "GdsRLLGrid: Nonzero rotation angle not yet supported for Grid template 1; no coordinates will be returned");
	    g2GDSDimsOnlyGrid(thevarrec, lat, n_dims_lat, dimsizes_lat, lon,
			      n_dims_lon,dimsizes_lon, rot, n_dims_rot, dimsizes_rot,
			      lat_att_list, nlatatts, lon_att_list, nlonatts,
			      rot_att_list, nrotatts);
	    
	    return;
	}


    if (is_thinned_lon) {
        g2GetThinnedLonParams(gds, nj, lo1, lo2, idir, &ni, &di);
	thevarrec->gds->is_thinned_grid = 1;
    } else {
	    if (ni == 1) {
		    di = 0;
	    }
	    else if (idir == 1) {
		    float ti = lo2;
		    while (ti < lo1) {
			    ti += 360.0;
		    }
		    di = (ti - lo1) / (double) (ni - 1);
	    }
	    else {
		    float ti = lo1;
		    while (ti < lo2) {
			    ti += 360.0;
		    }
		    di = (ti - lo2) / (double) (ni - 1);
	    }
    }

    if (is_thinned_lat) {
	    thevarrec->gds->is_thinned_grid = 1;
	    g2GetThinnedLatParams(gds, ni, la1, la2, jdir, &nj, &dj);
    } else {
        /* Not specified: must be calculated from the endpoints and number of steps */

	    if (nj == 1) {
		    dj = 0;
	    }
	    else {
		    dj = (la2 - la1) / (double) (nj - 1);
		    if (dj < 0)
			    dj = -dj;
	    }
    }

    *dimsizes_lat = (ng_size_t*)NclMalloc(2 * sizeof(ng_size_t));
    *dimsizes_lon = (ng_size_t*)NclMalloc(2 * sizeof(ng_size_t));
    (*dimsizes_lon)[0] = nj;
    (*dimsizes_lon)[1] = ni;
    (*dimsizes_lat)[0] = nj;
    (*dimsizes_lat)[1] = ni;
    *n_dims_lat = 2;
    *n_dims_lon = 2;
    *lat = (float*)NclMalloc((unsigned)sizeof(float)* nj * ni);
    *lon = (float*)NclMalloc((unsigned)sizeof(float)* nj * ni);

    clat = lasp + 90.0;
    rot2ll(lasp,losp,la1,lo1,&llat,&llon);
    rot2ll(lasp,losp,la2,lo2,&rlat,&rlon);

    if (idir == 1) {
	    if (llon > rlon) {
		    llon -= 360;
	    }
	    if (llon < 0 && rlon > 0) {
		    do_180 = 1;
	    }
    }
    else {
	    if (rlon > llon) {
		    rlon -= 360;
	    }
	    if (rlon < 0 && llon > 0) {
		    do_180 = 1;
	    }
    }
			

    if (do_rot) {
	    double dtr = atan(1) / 45.0;
	    double rlat = la1; 
	    double rlon = lo1;
	    *rot = (float*)NclMalloc((unsigned)sizeof(float)* nj * ni);

	    for(j = 0;j < nj; j++) {
		    for (i = 0; i < ni; i++) {
			    double tlon,tlat;
			    double cgridlat, slon,srot,crot;
			    rot2ll(lasp,losp,rlat + j * jdir * dj,rlon + i * idir * di,&tlat,&tlon);
			    if (do_180) {
				    tlon = tlon > 180 ? tlon - 360 : tlon;
			    }
			    (*lat)[j * ni + i] = (float)tlat;
			    (*lon)[j * ni + i] = (float)tlon;
			    slon = sin((tlon - losp)*dtr);
			    cgridlat = cos((rlat + j * jdir * dj) * dtr);
			    if (cgridlat <= 0.0)
				    (*rot)[j * ni + i] = 0.0;
			    else {
				    crot = (cos(clat * dtr) * cos(tlat * dtr) + sin(clat * dtr) * sin(tlat * dtr) * cos(tlon * dtr)) / cgridlat;
				    srot = - sin(clat * dtr) * slon / cgridlat;
				    (*rot)[j * ni + i] = (float) atan2(srot,crot);
#if 0

				    /* diagnostics */
				    crot1 = sqrt(1 - srot * srot);
				    eps = fabs(crot)-fabs(crot1);
			    }
			    if ((i%10 == 0 && j%10 == 0) ) {
				    printf("j/i %d %d lat/lon %f %f rot %f slon cgridlat srot crot %f %f %f %f crot1 eps %f %f\n",
					   j,i,tlat,tlon,(*rot)[j * ni + i],
					   slon,cgridlat,srot,crot,crot1,eps);
#endif
			    }
		    }
	    }
    }
    else {
	    double rlat = la1; 
	    double rlon = lo1;
	    for(j = 0;j < nj; j++) {
		    for (i = 0; i < ni; i++) {
			    double tlon,tlat;
			    rot2ll(lasp,losp,rlat + j * jdir * dj,rlon + i * idir * di,&tlat,&tlon);
			    if (do_180) {
				    tlon = tlon > 180 ? tlon - 360 : tlon;
			    }
			    (*lat)[j * ni + i] = (float)tlat;
			    (*lon)[j * ni + i] = (float)tlon;
		    }
	    }
    }
	
    grid_name = (is_thinned_lat || is_thinned_lon) ?
	    NrmStringToQuark("Rotated Latitude/Longitude (Quasi-Regular)") :
	    NrmStringToQuark("Rotated Latitude/Longitude");
    if(lon_att_list != NULL) {
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lasp;
	    Grib2PushAtt(lon_att_list,"Latitude_of_southern_pole",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = losp;
	    Grib2PushAtt(lon_att_list,"Longitude_of_southern_pole",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = rotang;
	    Grib2PushAtt(lon_att_list,"Angle_of_rotation",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la1;
	    Grib2PushAtt(lon_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo1;
	    Grib2PushAtt(lon_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la2;
	    Grib2PushAtt(lon_att_list,"La2",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo2;
	    Grib2PushAtt(lon_att_list,"Lo2",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = di;
	    Grib2PushAtt(lon_att_list,"Di",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = dj;
	    Grib2PushAtt(lon_att_list,"Dj",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("degrees_east");
	    Grib2PushAtt(lon_att_list,"units",tmp_string,1,nclTypestringClass); (*nlonatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = grid_name;
	    Grib2PushAtt(lon_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlonatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("longitude");
	    Grib2PushAtt(lon_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlonatts)++;
    }
    if(lat_att_list != NULL) {
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lasp;
	    Grib2PushAtt(lat_att_list,"Latitude_of_southern_pole",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = losp;
	    Grib2PushAtt(lat_att_list,"Longitude_of_southern_pole",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = rotang;
	    Grib2PushAtt(lat_att_list,"Angle_of_rotation",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la1;
	    Grib2PushAtt(lat_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo1;
	    Grib2PushAtt(lat_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la2;
	    Grib2PushAtt(lat_att_list,"La2",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo2;
	    Grib2PushAtt(lat_att_list,"Lo2",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = di;
	    Grib2PushAtt(lat_att_list,"Di",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = dj;
	    Grib2PushAtt(lat_att_list,"Dj",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("degrees_north");
	    Grib2PushAtt(lat_att_list,"units",tmp_string,1,nclTypestringClass); (*nlatatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = grid_name;
	    Grib2PushAtt(lat_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlatatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("latitude");
	    Grib2PushAtt(lat_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlatatts)++;
    }
    if (do_rot && rot_att_list != NULL) {
	    g2Do_Rotation_Atts(grid_name,rot_att_list,nrotatts,grid_oriented);
    }

    return;
}

void g2GDSArakawaRLLGrid
# if NhlNeedProto
(
	Grib2ParamList *thevarrec, 
	float **lat, 
	int *n_dims_lat, 
	ng_size_t **dimsizes_lat,
	float **lon, 
	int *n_dims_lon, 
	ng_size_t **dimsizes_lon, 
	float **rot, 
	int *n_dims_rot,
	ng_size_t **dimsizes_rot, 
	Grib2AttInqRecList **lat_att_list, 
	int *nlatatts, 
	Grib2AttInqRecList **lon_att_list, 
	int *nlonatts, 
	Grib2AttInqRecList **rot_att_list,
	int *nrotatts
)
# else
(thevarrec, lat, n_dims_lat, dimsizes_lat, lon, n_dims_lon, dimsizes_lon,
 rot, n_dims_rot, dimsizes_rot, lat_att_list, nlatatts, lon_att_list, nlonatts,
 rot_att_list, nrotatts)
    Grib2ParamList *thevarrec; 
    float **lat; 
    int *n_dims_lat;
    ng_size_t **dimsizes_lat;
    float **lon;
    int *n_dims_lon;
    ng_size_t **dimsizes_lon;
    float **rot;
    int *n_dims_rot;
    ng_size_t **dimsizes_rot;
    Grib2AttInqRecList **lat_att_list; 
    int *nlatatts; 
    Grib2AttInqRecList **lon_att_list; 
    int *nlonatts;
    Grib2AttInqRecList **rot_att_list;
    int *nrotatts;
# endif
{
    G2_GDS *gds;
    double la1,la2,lo1,lo2;
    double di = 1;
    double dj = 1;
    int idir;
    int jdir;
    int is_thinned_lat;
    int is_thinned_lon;
    int i,j;
    float *tmp_float;
    NclQuark* tmp_string;
    int ni, nj;
    g2ArakawaRLLTemplate *rll;
    int grid_oriented,do_rot;
    int do_180 = 0;
    NrmQuark grid_name;
    double scale_factor;
    double lasp;
    double losp;
    double rotang;
    double clat,clon,llat,llon,rlat,rlon;
	
    *lat = NULL;
    *n_dims_lat = 0;
    *dimsizes_lat = NULL;
    *lon = NULL;
    *n_dims_lon= 0;
    *dimsizes_lon= NULL;
    if ((thevarrec->thelist == NULL)  ||  (thevarrec->ref_rec == NULL)) 
        return;

    gds = (G2_GDS *) thevarrec->ref_rec->gds;
    if (gds == NULL) {
        return;
    }
    rll = (g2ArakawaRLLTemplate *) gds->grid_template;

    ni = rll->npts_along_parallel;
    nj = rll->npts_along_meridian;

    /* all bits set indicates missing: missing means thinned */
    is_thinned_lon = (ni == -1); 
    is_thinned_lat = (nj == -1);
    if ((ni <= 1 && !is_thinned_lon) || (nj <= 1 && !is_thinned_lat)) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,
            "GdsRLLGrid: Invalid grid detected");
        *lat = NULL;
        *n_dims_lat = 0;
        *dimsizes_lat = NULL;
        *lon = NULL;
        *n_dims_lon = 0;
        *dimsizes_lon = NULL;
        return;
    }

    idir = g2getbits(rll->scan_mode_flags, 7, 1) == 0 ? 1 : -1;
    jdir = g2getbits(rll->scan_mode_flags, 6, 1) == 0 ? -1 : 1;
    /* save the scan mode flags -- needed for decoding the data */
    thevarrec->gds->scan_mode_offset = (int*)&(rll->scan_mode_flags) - (int*)rll;

    if (rll->subdiv_basic_angle != 0 && rll->angl_init_prod_domain != 0)
	    scale_factor = (double) rll->angl_init_prod_domain / 
		    (double) rll->subdiv_basic_angle;
    else 
	    scale_factor = 1.0 / (double) G2_SCALE_FACTOR;

    la1 = rll->lat_first_gridpt * scale_factor;
    lo1 = rll->lon_first_gridpt * scale_factor;
    clon = rll->center_lon * scale_factor;
    clat = rll->center_lat * scale_factor;


    grid_oriented = g2getbits(rll->res_comp_flags, 3, 1) == 0 ? 0 : 1;
    do_rot = grid_oriented ? 1 : 0;


    lasp = clat - 90;
    losp = clon;
    ll2rot(lasp,losp,la1,lo1,&llat,&llon);

    /* since we don't have valid la2, lo2 values (in the rapid update files anyway) try to use just the center lat/lon 
       and la1, lo1 values to calculate the la2, lo2 values, using the fact that the center lat/lon is actually centered in
       the grid according to the template. */
    rot2ll(lasp,losp,-llat,-llon,&la2,&lo2);
    rlat = -llat;
    rlon = -llon;

    if (is_thinned_lon) {
        g2GetThinnedLonParams(gds, nj, llon, rlon, idir, &ni, &di);
	thevarrec->gds->is_thinned_grid = 1;
    } else {
	    if (ni == 1) {
		    di = 0;
	    }
	    else if (idir == 1) {
		    float ti = rlon;
		    while (ti < llon) {
			    ti += 360.0;
		    }
		    di = (ti - llon) / (double) (ni - 1);
	    }
	    else {
		    float ti = llon;
		    while (ti < rlon) {
			    ti += 360.0;
		    }
		    di = (ti - rlon) / (double) (ni - 1);
	    }
    }


    if (is_thinned_lat) {
	    thevarrec->gds->is_thinned_grid = 1;
	    g2GetThinnedLatParams(gds, ni, llat, rlat, jdir, &nj, &dj);
    } else {
        /* Not specified: must be calculated from the endpoints and number of steps */

	    if (nj == 1) {
		    dj = 0;
	    }
	    else {
		    dj = (rlat - llat) / (double) (nj - 1);
		    if (dj < 0)
			    dj = -dj;
	    }
    }

#if 0
    di = rll->idir_incr * scale_factor;
    dj = rll->jdir_incr * scale_factor;
#endif

    *dimsizes_lat = (ng_size_t*)NclMalloc(2 * sizeof(ng_size_t));
    *dimsizes_lon = (ng_size_t*)NclMalloc(2 * sizeof(ng_size_t));
    (*dimsizes_lon)[0] = nj;
    (*dimsizes_lon)[1] = ni;
    (*dimsizes_lat)[0] = nj;
    (*dimsizes_lat)[1] = ni;
    *n_dims_lat = 2;
    *n_dims_lon = 2;
    *lat = (float*)NclMalloc((unsigned)sizeof(float)* nj * ni);
    *lon = (float*)NclMalloc((unsigned)sizeof(float)* nj * ni);


    if (idir == 1) {
	    if (llon > rlon) {
		    llon -= 360;
	    }
	    if (llon < 0 && rlon > 0) {
		    do_180 = 1;
	    }
    }
    else {
	    if (rlon > llon) {
		    rlon -= 360;
	    }
	    if (rlon < 0 && llon > 0) {
		    do_180 = 1;
	    }
    }
			

    if (do_rot) {
	    double dtr = atan(1) / 45.0;
	    *rot = (float*)NclMalloc((unsigned)sizeof(float)* nj * ni);

	    for(j = 0;j < nj; j++) {
		    for (i = 0; i < ni; i++) {
			    double tlon,tlat;
			    double cgridlat, slon,srot,crot;
			    rot2ll(lasp,losp,llat + j * jdir * dj,llon + i * idir * di,&tlat,&tlon);
			    if (do_180) {
				    tlon = tlon > 180 ? tlon - 360 : tlon;
			    }
			    (*lat)[j * ni + i] = (float)tlat;
			    (*lon)[j * ni + i] = (float)tlon;
			    slon = sin((tlon - losp)*dtr);
			    cgridlat = cos((llat + j * jdir * dj) * dtr);
			    if (cgridlat <= 0.0)
				    (*rot)[j * ni + i] = 0.0;
			    else {
				    crot = (cos(clat * dtr) * cos(tlat * dtr) + sin(clat * dtr) * sin(tlat * dtr) * cos(tlon * dtr)) / cgridlat;
				    srot = - sin(clat * dtr) * slon / cgridlat;
				    (*rot)[j * ni + i] = (float) atan2(srot,crot);
#if 0

				    /* diagnostics */
				    crot1 = sqrt(1 - srot * srot);
				    eps = fabs(crot)-fabs(crot1);
			    }
			    if ((i%10 == 0 && j%10 == 0) ) {
				    printf("j/i %d %d lat/lon %f %f rot %f slon cgridlat srot crot %f %f %f %f crot1 eps %f %f\n",
					   j,i,tlat,tlon,(*rot)[j * ni + i],
					   slon,cgridlat,srot,crot,crot1,eps);
#endif
			    }
		    }
	    }
    }
    else {
	    for(j = 0;j < nj; j++) {
		    for (i = 0; i < ni; i++) {
			    double tlon,tlat;
			    rot2ll(lasp,losp,llat + j * jdir * dj,llon + i * idir * di,&tlat,&tlon);
			    if (do_180) {
				    tlon = tlon > 180 ? tlon - 360 : tlon;
			    }
			    (*lat)[j * ni + i] = (float)tlat;
			    (*lon)[j * ni + i] = (float)tlon;
		    }
	    }
    }
    lo2 = (*lon)[ni * nj - 1];
    la2 = (*lat)[ni * nj - 1];
	
    grid_name = (is_thinned_lat || is_thinned_lon) ?
	    NrmStringToQuark("Arakawa Non-E Staggered rotated Latitude/Longitude Grid (Quasi-Regular)") :
	    NrmStringToQuark("Arakawa Non-E Staggered rotated Latitude/Longitude Grid");
    if(lon_att_list != NULL) {
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la1;
	    Grib2PushAtt(lon_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo1;
	    Grib2PushAtt(lon_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = clat;
	    GribPushAtt(lon_att_list,"CenterLat",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = clon;
	    GribPushAtt(lon_att_list,"CenterLon",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la2;
	    Grib2PushAtt(lon_att_list,"La2",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo2;
	    Grib2PushAtt(lon_att_list,"Lo2",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = di;
	    Grib2PushAtt(lon_att_list,"Di",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = dj;
	    Grib2PushAtt(lon_att_list,"Dj",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("degrees_east");
	    Grib2PushAtt(lon_att_list,"units",tmp_string,1,nclTypestringClass); (*nlonatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = grid_name;
	    Grib2PushAtt(lon_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlonatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("longitude");
	    Grib2PushAtt(lon_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlonatts)++;
    }
    if(lat_att_list != NULL) {
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la1;
	    Grib2PushAtt(lat_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo1;
	    Grib2PushAtt(lat_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = clat;
	    GribPushAtt(lat_att_list,"CenterLat",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = clon;
	    GribPushAtt(lat_att_list,"CenterLon",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la2;
	    Grib2PushAtt(lat_att_list,"La2",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo2;
	    Grib2PushAtt(lat_att_list,"Lo2",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = di;
	    Grib2PushAtt(lat_att_list,"Di",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = dj;
	    Grib2PushAtt(lat_att_list,"Dj",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("degrees_north");
	    Grib2PushAtt(lat_att_list,"units",tmp_string,1,nclTypestringClass); (*nlatatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = grid_name;
	    Grib2PushAtt(lat_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlatatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("latitude");
	    Grib2PushAtt(lat_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlatatts)++;
    }
    if (do_rot && rot_att_list != NULL) {
	    g2Do_Rotation_Atts(grid_name,rot_att_list,nrotatts,grid_oriented);
    }

    return;
}

void g2GDSMEGrid
# if NhlNeedProto
(
	Grib2ParamList *thevarrec, 
	float **lat, 
	int *n_dims_lat, 
	ng_size_t **dimsizes_lat,
	float **lon, 
	int *n_dims_lon, 
	ng_size_t **dimsizes_lon, 
	float **rot, 
	int *n_dims_rot,
	ng_size_t **dimsizes_rot, 
	Grib2AttInqRecList **lat_att_list, 
	int *nlatatts, 
	Grib2AttInqRecList **lon_att_list, 
	int *nlonatts, 
	Grib2AttInqRecList **rot_att_list,
	int *nrotatts
)
# else
(thevarrec, lat, n_dims_lat, dimsizes_lat, lon, n_dims_lon, dimsizes_lon,
 rot, n_dims_rot, dimsizes_rot, lat_att_list, nlatatts, lon_att_list, nlonatts,
 rot_att_list, nrotatts)
    Grib2ParamList *thevarrec; 
    float **lat; 
    ng_size_t *n_dims_lat;
    ng_size_t **dimsizes_lat;
    float **lon;
    ng_size_t *n_dims_lon;
    ng_size_t **dimsizes_lon;
    float **rot;
    ng_size_t *n_dims_rot;
    ng_size_t **dimsizes_rot;
    Grib2AttInqRecList **lat_att_list; 
    int *nlatatts; 
    Grib2AttInqRecList **lon_att_list; 
    int *nlonatts;
    Grib2AttInqRecList **rot_att_list;
    int *nrotatts;
# endif
{
    G2_GDS *gds;
    double la1,la2,lo1,lo2;
    double tlo1,tlo2;
    double tmplon,tmplat;
    double dumx,dumy;
    double nx1,ny1,nx0,ny0;
    double udx,udy;
    double di;
    double dj;
    int idir;
    int jdir;
    int i,j;
    float *tmp_float;
    NclQuark* tmp_string;
    int nlon, nlat;
    g2METemplate *me;
    double scale_factor;
    double earth_radius;
    double latd;
	
    *lat = NULL;
    *n_dims_lat = 0;
    *dimsizes_lat = NULL;
    *lon = NULL;
    *n_dims_lon= 0;
    *dimsizes_lon= NULL;
    if ((thevarrec->thelist == NULL)  ||  (thevarrec->ref_rec == NULL)) 
        return;

    gds = (G2_GDS *) thevarrec->ref_rec->gds;
    if (gds == NULL) {
        return;
    }
    me = (g2METemplate *) gds->grid_template;

    nlon = me->npts_along_parallel;
    nlat = me->npts_along_meridian;

    if (nlon <= 1 || nlat <= 1) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,
		  "g2GDSMEGrid: Invalid grid detected");
        *lat = NULL;
        *n_dims_lat = 0;
        *dimsizes_lat = NULL;
        *lon = NULL;
        *n_dims_lon = 0;
        *dimsizes_lon = NULL;
        return;
    }
    if (me->orientation != 0) {
        NhlPError(NhlWARNING,NhlEUNKNOWN,
		  "g2GDSMEGrid: Cannot decode rotated mercator grids");
        *lat = NULL;
        *n_dims_lat = 0;
        *dimsizes_lat = NULL;
        *lon = NULL;
        *n_dims_lon = 0;
        *dimsizes_lon = NULL;
        return;
    }

    idir = g2getbits(me->scan_mode_flags, 7, 1) == 0 ? 1 : -1;
    jdir = g2getbits(me->scan_mode_flags, 6, 1) == 0 ? -1 : 1;
    scale_factor = 1.0 / (double) G2_SCALE_FACTOR;

    /* save the scan mode flags -- needed for decoding the data */
    thevarrec->gds->scan_mode_offset = (int*)&(me->scan_mode_flags) - (int*)me;

    la1 = me->lat_first_gridpt * scale_factor;
    lo1 = me->lon_first_gridpt * scale_factor;
    la2 = me->lat_last_gridpt * scale_factor;
    lo2 = me->lon_last_gridpt * scale_factor;
    latd  = me->latD_intersect * scale_factor;
    di = me->idir_incr / 1000.0;  /* meters */
    dj = me->jdir_incr / 1000.0;  /* meters */

    if (me->ep.shapeOfEarth < 0 || me->ep.shapeOfEarth > 6)
	    earth_radius = Earth_Radius[6];
    else if (Earth_Radius[me->ep.shapeOfEarth] < 0)
	    earth_radius = Earth_Radius[6];
    else 
	    earth_radius = Earth_Radius[me->ep.shapeOfEarth];	

    *dimsizes_lat = (ng_size_t *) NclMalloc(sizeof(ng_size_t));
    *dimsizes_lon = (ng_size_t *) NclMalloc(sizeof(ng_size_t));
    *(*dimsizes_lon) = nlon;
    *(*dimsizes_lat) = nlat;
    *n_dims_lat = 1;
    *n_dims_lon = 1;
    *lat = (float *) NclMalloc((unsigned)sizeof(float) * nlat);
    *lon = (float *) NclMalloc((unsigned)sizeof(float) * nlon);

    _NclInitMapTrans("ME",0,idir * (lo2 - lo1)/2.0,0.0);

    if (lo1 == lo2) { /* global grid probably specified according to GRIB2 spec (lo1 and lo2 both must be positive - but this is too inconvenient for us)  */
	    if (idir == 1) {
		    if (lo1 > 0) {
			    lo1 -= 360000;
		    }
		    else {
			    lo2 += 360000;
		    }
	    }
	    else {
		    if (lo1 > 0) {
			    lo2 -= 360000;
		    }
		    else {
			    lo1 += 360000;
		    }
	    }
    }

    tlo1 = lo1;
    tlo2 = lo2;
    if (idir == 1) {
	    if (tlo2 < tlo1) {
		    tlo1 -= 360.0;
	    }
    }
    else {
	    if (tlo2 > tlo1) {
		    tlo2 -= 360.0;
	    }
    }
    tmplon = (tlo2 - tlo1) / 2.0;
    tmplat = jdir * (la2 - la1) / 2.0;
    NGCALLF(mdptrn,MDPTRN)(&tmplat,&tmplon,&dumx,&dumy);
    NGCALLF(mdptrn,MDPTRN)(&la1,&lo1,&nx0,&ny0);
    NGCALLF(mdptrn,MDPTRN)(&la2,&lo2,&nx1,&ny1);
    udx = fabs(nx1 - nx0) / (nlon -1);
    udy = fabs(ny1 - ny0) / (nlat -1);

    for(i = 0; i < nlat; i++) {
	    double uy = ny0 + i * udy * idir;
	    NGCALLF(mdptri,MDPTRI)(&dumx,&uy,&tmplat,&tmplon);
	    (*lat)[i] = (float) tmplat;
    }
    for(j = 0; j < nlon; j++) {
	    double ux = nx0 + j * udx * jdir;
	    NGCALLF(mdptri,MDPTRI)(&ux,&dumy,&tmplat,&tmplon);
	    (*lon)[j] = (float) tmplon;
    }
    for(j = 0; j < nlon; j++) {
	    (*lon)[j] = ((*lon)[j] < 0)? ((*lon)[j] + 360) : (*lon)[j];
    }

    if(lon_att_list != NULL) {
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la1;
	    Grib2PushAtt(lon_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo1;
	    Grib2PushAtt(lon_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la2;
	    Grib2PushAtt(lon_att_list,"La2",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo2;
	    Grib2PushAtt(lon_att_list,"Lo2",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = latd;
	    Grib2PushAtt(lon_att_list,"LaD",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = di/1000.0;
	    Grib2PushAtt(lon_att_list,"Di",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = dj/1000.0;
	    Grib2PushAtt(lon_att_list,"Dj",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("degrees_east");
	    Grib2PushAtt(lon_att_list,"units",tmp_string,1,nclTypestringClass); (*nlonatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("Mercator");
	    Grib2PushAtt(lon_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlonatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("longitude");
	    Grib2PushAtt(lon_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlonatts)++;
    }
    if(lat_att_list != NULL) {
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la1;
	    Grib2PushAtt(lat_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo1;
	    Grib2PushAtt(lat_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = la2;
	    Grib2PushAtt(lat_att_list,"La2",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = lo2;
	    Grib2PushAtt(lat_att_list,"Lo2",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = latd;
	    Grib2PushAtt(lat_att_list,"LaD",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = di/1000.0;
	    Grib2PushAtt(lat_att_list,"Di",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_float= (float*)NclMalloc(sizeof(float));
	    *tmp_float = dj/1000.0;
	    Grib2PushAtt(lat_att_list,"Dj",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("degrees_north");
	    Grib2PushAtt(lat_att_list,"units",tmp_string,1,nclTypestringClass); (*nlatatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("Mercator Projection Grid");
	    Grib2PushAtt(lat_att_list,"GridType",tmp_string,1,nclTypestringClass); (*nlatatts)++;
	    tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	    *tmp_string = NrmStringToQuark("latitude");
	    Grib2PushAtt(lat_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlatatts)++;
    }
    return;
}

void g2GDSSTGrid
#if NhlNeedProto
(
	Grib2ParamList* thevarrec, 
	float** lat, 
	int* n_dims_lat,
	ng_size_t** dimsizes_lat,
	float** lon,
	int* n_dims_lon,
	ng_size_t** dimsizes_lon,
	float** rot,
	int* n_dims_rot,
	ng_size_t **dimsizes_rot,
	Grib2AttInqRecList** lat_att_list, 
	int* nlatatts, 
	Grib2AttInqRecList** lon_att_list, 
	int* nlonatts,
	Grib2AttInqRecList** rot_att_list,
	int* nrotatts
)
#else
(thevarrec, lat, n_dims_lat, dimsizes_lat, lon, n_dims_lon, dimsizes_lon,
 rot, n_dims_rot, dimsizes_rot,
 lat_att_list,nlatatts,lon_att_list, nlonatts, rot_att_list, nrotatts)
Grib2ParamList* thevarrec; 
float** lat; 
int* n_dims_lat;
ng_size_t** dimsizes_lat;
float** lon;
int* n_dims_lon;
ng_size_t** dimsizes_lon;
float** rot;
int* n_dims_rot;
ng_size_t **dimsizes_rot;
Grib2AttInqRecList** lat_att_list; 
int* nlatatts; 
Grib2AttInqRecList** lon_att_list; 
int* nlonatts;
Grib2AttInqRecList** rot_att_list;
int* nrotatts;
#endif
{
	int nx;
	int ny;
	double la1;
	double lo1;
	double lat_d;
	double lov;
	double dx;
	double dy;
	int idir,jdir;
	int grid_oriented;
	int do_rot;
	int north;
	g2STTemplate *st;
	double scale_factor;
	G2_GDS *gds;
	double tlon;
	double de,dr,xpole,ypole;
	double dxx,dyy,de2,dr2,trot;
	int x,y;
	NrmQuark grid_name = NrmNULLQUARK;
	NrmQuark *tmp_string;
	float *tmp_float;
	double earth_radius;

	*lat = NULL;
	*n_dims_lat = 0;
	*dimsizes_lat = NULL;
	*lon = NULL;
	*n_dims_lon= 0;
	*dimsizes_lon= NULL;
	if ((thevarrec->thelist == NULL)  ||  (thevarrec->ref_rec == NULL)) 
		return;

	gds = (G2_GDS *) thevarrec->ref_rec->gds;
	if (gds == NULL) {
		return;
	}
	st = (g2STTemplate *) gds->grid_template;

	nx = st->npts_along_x_axis;
	ny = st->npts_along_y_axis;

	scale_factor = 1.0 / (double) G2_SCALE_FACTOR;
	la1 = st->lat_first_gridpt * scale_factor;
	lo1 = st->lon_first_gridpt * scale_factor;
	lov = st->loV_orientation * scale_factor;
	lat_d = st->latD_intersect;

	dx = st->dx_incr * scale_factor;  /* units of 10-3 meters converted to kilometers */
	dy = st->dy_incr * scale_factor;

	/* save the scan mode flags -- needed for decoding the data */
	thevarrec->gds->scan_mode_offset = (int*)&(st->scan_mode_flags) - (int*)st;

	*lat = (float*)NclMalloc(sizeof(float)*nx*ny);
	*lon = (float*)NclMalloc(sizeof(float)*nx*ny);
	*rot = (float*)NclMalloc(sizeof(float)*nx*ny);
        *dimsizes_lat = (ng_size_t*)NclMalloc(sizeof(ng_size_t) * 2);
        *dimsizes_lon = (ng_size_t*)NclMalloc(sizeof(ng_size_t) * 2);
        *n_dims_lat = 2;
        *n_dims_lon = 2;
        (*dimsizes_lat)[0] = ny;
        (*dimsizes_lat)[1] = nx;
        (*dimsizes_lon)[0] = ny;
        (*dimsizes_lon)[1] = nx;

	idir = g2getbits(st->scan_mode_flags, 7, 1) == 0 ? 1 : -1;
	jdir = g2getbits(st->scan_mode_flags, 6, 1) == 0 ? -1 : 1;
	north = g2getbits(st->proj_center_flag, 7, 1) == 0 ? 1 : 0;
	grid_oriented = g2getbits(st->res_comp_flags, 3, 1) == 0 ? 0 : 1;
	if (st->ep.shapeOfEarth < 0 || st->ep.shapeOfEarth > 6)
		earth_radius = Earth_Radius[6];
	else if (Earth_Radius[st->ep.shapeOfEarth] < 0)
		earth_radius = Earth_Radius[6];
	else 
		earth_radius = Earth_Radius[st->ep.shapeOfEarth];	

	do_rot = 1;

	if (north) {
		double dxs = dx * idir;
		double dys = dy * jdir;
		de = (1. + sin(60. * RadPerDeg)) * (earth_radius / 1000.0);
		dr = de * cos(la1 * RadPerDeg) / (1. + sin(la1 * RadPerDeg));
		de2 = de * de;
		xpole = 1 - sin((lo1 - lov)* RadPerDeg) * dr / dxs;
		ypole = 1 + cos((lo1 - lov) * RadPerDeg) * dr / dys;
		for (y = 0; y < ny; y++) {
			for (x = 0; x < nx; x++) {
				dxx = (x + 1 - xpole) * dxs;
				dyy = (y + 1 - ypole) * dys;
				dr2 = dxx * dxx + dyy * dyy;
				if (dr2 < de2 * 1e-6) {
					(*lon)[y * nx + x] = 0;
					(*lat)[y * nx + x] = 90;
				}
				else {
					tlon = fmod(lov + DegPerRad * atan2(dxx,-dyy) + 3600,360.0);
					tlon = tlon > 180 ? tlon - 360 : tlon;
					tlon = tlon < -180 ? tlon + 360 : tlon; 
					(*lon)[y * nx + x] = tlon;
					(*lat)[y * nx + x] = DegPerRad * asin((de2-dr2)/(de2+dr2));
				}
				trot = (*lon)[y * nx + x] - lov;
				if (trot > 180)
					trot -= 360;
				if (trot < -180)
					trot += 360;
				(*rot)[y * nx + x] = trot * RadPerDeg;
			}
		}
	}
	else {
		double dxs = dx * idir;
		double dys = dy * jdir;
		double slov = lov - 180;
		de = (1. + sin(60. * RadPerDeg)) * (earth_radius / 1000.0);
		dr = de * cos(la1 * RadPerDeg) / (1. - sin(la1 * RadPerDeg));
		de2 = de * de;
		xpole = 1 + sin((lo1 - slov)* RadPerDeg) * dr / dxs;
		ypole = 1 + cos((lo1 - slov) * RadPerDeg) * dr / dys;
		for (y = 0; y < ny; y++) {
			for (x = 0; x < nx; x++) {
				dxx = (x + 1 - xpole) * dxs;
				dyy = (y + 1 - ypole) * dys;
				dr2 = dxx * dxx + dyy * dyy;
				if (dr2 < de2 * 1e-6) {
					(*lon)[y * nx + x] = 0;
					(*lat)[y * nx + x] = 90;
				}
				else {
					tlon = fmod(slov - DegPerRad * atan2(dxx,-dyy) + 3600,360.0);
					tlon = tlon > 180 ? tlon - 360 : tlon;
					tlon = tlon < -180 ? tlon + 360 : tlon; 
					(*lon)[y * nx + x] = tlon;
					(*lat)[y * nx + x] = - DegPerRad * asin((de2-dr2)/(de2+dr2));
				}
				trot = (*lon)[y * nx + x] - slov;
				if (trot > 180)
					trot -= 360;
				if (trot < -180)
					trot += 360;
				(*rot)[y * nx + x] = trot * RadPerDeg;
			}
		}
	}

	if(lon_att_list != NULL) {
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la1;
		Grib2PushAtt(lon_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo1;
		Grib2PushAtt(lon_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lov;
		Grib2PushAtt(lon_att_list,"Lov",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = dx;
		Grib2PushAtt(lon_att_list,"Dx",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = dy;
		Grib2PushAtt(lon_att_list,"Dy",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("degrees_east");
		Grib2PushAtt(lon_att_list,"units",tmp_string,1,nclTypestringClass); (*nlonatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("Polar Sterographic Projection (North or South)");
		Grib2PushAtt(lon_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlonatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("longitude");
		Grib2PushAtt(lon_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlonatts)++;
	}
	if(lat_att_list != NULL) {
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la1;
		Grib2PushAtt(lat_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo1;
		Grib2PushAtt(lat_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lov;
		Grib2PushAtt(lat_att_list,"Lov",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = dx;
		Grib2PushAtt(lat_att_list,"Dx",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = dy;
		Grib2PushAtt(lat_att_list,"Dy",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("degrees_north");
		Grib2PushAtt(lat_att_list,"units",tmp_string,1,nclTypestringClass); (*nlatatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("Polar Sterographic Projection (North or South)");
		grid_name = *tmp_string;
		Grib2PushAtt(lat_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlatatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("latitude");
		Grib2PushAtt(lat_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlatatts)++;
	}
	if(do_rot && rot_att_list != NULL) {
		g2Do_Rotation_Atts(grid_name,rot_att_list,nrotatts,grid_oriented);
	}
}

void g2GDSLCGrid
#if NhlNeedProto
(
	Grib2ParamList* thevarrec, 
	float** lat, 
	int* n_dims_lat,
	ng_size_t** dimsizes_lat,
	float** lon,
	int* n_dims_lon,
	ng_size_t** dimsizes_lon,
	float** rot,
	int* n_dims_rot,
	ng_size_t **dimsizes_rot,
	Grib2AttInqRecList** lat_att_list, 
	int* nlatatts, 
	Grib2AttInqRecList** lon_att_list, 
	int* nlonatts,
	Grib2AttInqRecList** rot_att_list,
	int* nrotatts
)
#else
(thevarrec, lat, n_dims_lat, dimsizes_lat, lon, n_dims_lon, dimsizes_lon,
 rot, n_dims_rot, dimsizes_rot,
 lat_att_list,nlatatts,lon_att_list, nlonatts, rot_att_list, nrotatts)
Grib2ParamList* thevarrec; 
float** lat; 
int* n_dims_lat;
ng_size_t** dimsizes_lat;
float** lon;
int* n_dims_lon;
ng_size_t** dimsizes_lon;
float** rot;
int* n_dims_rot;
ng_size_t **dimsizes_rot;
Grib2AttInqRecList** lat_att_list; 
int* nlatatts; 
Grib2AttInqRecList** lon_att_list; 
int* nlonatts;
Grib2AttInqRecList** rot_att_list;
int* nrotatts;
#endif
{
	int nx;
	int ny;
	double la1;
	double lo1;
	double lov,tlon;
	double dx;
	double dy;
	double latin1;
	double latin2;
	int north;
	int idir,jdir,i,j;
	double nx0,nx1,ny0,ny1;
	double C,d_per_km,dlon;
	double ndcdx,ndcdy;
	double an;
	float *tmp_float;
	NclQuark *tmp_string;
	int do_rot;
	NhlBoolean grid_oriented;
	NrmQuark grid_name = NrmNULLQUARK;
	g2LCTemplate *lc;
	double scale_factor;
	G2_GDS *gds;
	double earth_radius;

	*lat = NULL;
	*n_dims_lat = 0;
	*dimsizes_lat = NULL;
	*lon = NULL;
	*n_dims_lon= 0;
	*dimsizes_lon= NULL;
	if ((thevarrec->thelist == NULL)  ||  (thevarrec->ref_rec == NULL)) 
		return;

	gds = (G2_GDS *) thevarrec->ref_rec->gds;
	if (gds == NULL) {
		return;
	}
	lc = (g2LCTemplate *) gds->grid_template;

	nx = lc->npts_along_x_axis;
	ny = lc->npts_along_y_axis;

	scale_factor = 1.0 / (double) G2_SCALE_FACTOR;
	la1 = lc->lat_first_gridpt * scale_factor;
	lo1 = lc->lon_first_gridpt * scale_factor;
	lov = lc->loV_central_meridian * scale_factor;

	dx = lc->dx_incr * scale_factor;  /* units of 10-3 meters converted to kilometers */
	dy = lc->dy_incr * scale_factor;
	latin1 = lc->latin1 * scale_factor;
	latin2 = lc->latin2 * scale_factor;

	/* save the scan mode flags -- needed for decoding the data */
	thevarrec->gds->scan_mode_offset = (int*)(&(lc->scan_mode_flags)) - (int*)lc;


	*lat = (float*)NclMalloc(sizeof(float)*nx*ny);
	*lon = (float*)NclMalloc(sizeof(float)*nx*ny);
	*rot = (float*)NclMalloc(sizeof(float)*nx*ny);
        *dimsizes_lat = (ng_size_t*)NclMalloc(sizeof(ng_size_t) * 2);
        *dimsizes_lon = (ng_size_t*)NclMalloc(sizeof(ng_size_t) * 2);
        *n_dims_lat = 2;
        *n_dims_lon = 2;
        (*dimsizes_lat)[0] = ny;
        (*dimsizes_lat)[1] = nx;
        (*dimsizes_lon)[0] = ny;
        (*dimsizes_lon)[1] = nx;

	idir = g2getbits(lc->scan_mode_flags, 7, 1) == 0 ? 1 : -1;
	jdir = g2getbits(lc->scan_mode_flags, 6, 1) == 0 ? -1 : 1;
	north = g2getbits(lc->proj_center_flag, 7, 1) == 0 ? 1 : 0;
	grid_oriented = g2getbits(lc->res_comp_flags, 3, 1) == 0 ? 0 : 1;
	if (lc->ep.shapeOfEarth < 0 || lc->ep.shapeOfEarth > 6)
		earth_radius = Earth_Radius[6];
	else if (Earth_Radius[lc->ep.shapeOfEarth] < 0)
		earth_radius = Earth_Radius[6];
	else 
		earth_radius = Earth_Radius[lc->ep.shapeOfEarth];	

	do_rot = 1;
	_NclInitMapTrans("LC",latin1,lov,latin2);
/*
* Southern case
*/
	if((latin1 < 0)&&(latin2 < 0)) {
		
		if (latin1 == latin2) {
			an = sin(-1 * latin1 * RadPerDeg);
		}
		else {
			an = log(cos(latin1 * RadPerDeg)/cos(latin2 * RadPerDeg)) /
				log(tan(RadPerDeg * (-90 - latin1) / 2) / tan(RadPerDeg * (-90 - latin2) / 2));
		}
		C = 2 * PI * (earth_radius / 1000.0) * cos(RadPerDeg * latin1);
		d_per_km = 360.0/C;
		dlon = dx * d_per_km;
		tlon = lov + dlon;
		NGCALLF(mdptrn,MDPTRN)(&latin1,&lov,&nx0,&ny0);
		NGCALLF(mdptrn,MDPTRN)(&latin1,&tlon,&nx1,&ny1);
		ndcdx = fabs(nx0 - nx1);
		ndcdy = dy/dx * ndcdx;
		NGCALLF(mdptrn,MDPTRN)(&la1,&lo1,&nx0,&ny0);
		for(i = 0; i < ny; i++) {
			for(j = 0; j < nx; j++) {
				double tmpx =  nx0 + j * ndcdx * idir;
				double tmpy =  ny0 + i * ndcdy * jdir;
				double tmplat,tmplon;
				NGCALLF(mdptri,MDPTRI)
					(&tmpx,&tmpy,&tmplat,&tmplon);
				(*lat)[i * nx + j] = (float)tmplat;
				(*lon)[i * nx + j] = (float)tmplon;
				tlon = fmod(tmplon - lov + 180 + 3600, 360) - 180.0;
				(*rot)[i * nx + j] = (float)( an * tlon * RadPerDeg);
			}
		}
	} else {
/*
* Northern case
*/
		if (latin1 == latin2) {
			an = sin(latin1 * RadPerDeg);
		}
		else {
			an = log(cos(latin1 * RadPerDeg)/cos(latin2 * RadPerDeg)) /
				log(tan(RadPerDeg * (90 - latin1) / 2) / tan(RadPerDeg * (90 - latin2) / 2));
		}
		C = 2 * PI * (earth_radius / 1000.0) * cos(RadPerDeg * latin1);
		d_per_km = 360.0/C;
		dlon = dx * d_per_km;
		tlon = lov + dlon;
		NGCALLF(mdptrn,MDPTRN)(&latin1,&lov,&nx0,&ny0);
		NGCALLF(mdptrn,MDPTRN)(&latin1,&tlon,&nx1,&ny1);
		ndcdx = fabs(nx0 - nx1);
		ndcdy = dy/dx * ndcdx;
		NGCALLF(mdptrn,MDPTRN)(&la1,&lo1,&nx0,&ny0);
		for(i = 0; i < ny; i++) {
			for(j = 0; j < nx; j++) {
				double tmpx =  nx0 + j * ndcdx * idir;
				double tmpy =  ny0 + i * ndcdy * jdir;
				double tmplat,tmplon;
				NGCALLF(mdptri,MDPTRI)
					(&tmpx,&tmpy,&tmplat,&tmplon);
				(*lat)[i * nx + j] = (float)tmplat;
				(*lon)[i * nx + j] = (float)tmplon;
				tlon = fmod(tmplon - lov + 180 + 3600, 360) - 180.0;
				(*rot)[i * nx + j] = (float) (an * tlon * RadPerDeg);
			}
		}
	}

	if(lon_att_list != NULL) {
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la1;
		Grib2PushAtt(lon_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo1;
		Grib2PushAtt(lon_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lov;
		Grib2PushAtt(lon_att_list,"Lov",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = dx;
		Grib2PushAtt(lon_att_list,"Dx",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = dy;
		Grib2PushAtt(lon_att_list,"Dy",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = latin1;
		Grib2PushAtt(lon_att_list,"Latin1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = latin2;
		Grib2PushAtt(lon_att_list,"Latin2",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("degrees_east");
		Grib2PushAtt(lon_att_list,"units",tmp_string,1,nclTypestringClass); (*nlonatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("Lambert Conformal (secant, tangent, conical or bipolar)");
		Grib2PushAtt(lon_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlonatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("longitude");
		Grib2PushAtt(lon_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlonatts)++;
	}
	if(lat_att_list != NULL) {
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la1;
		Grib2PushAtt(lat_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo1;
		Grib2PushAtt(lat_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lov;
		Grib2PushAtt(lat_att_list,"Lov",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = dx;
		Grib2PushAtt(lat_att_list,"Dx",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = dy;
		Grib2PushAtt(lat_att_list,"Dy",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = latin1;
		Grib2PushAtt(lat_att_list,"Latin1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = latin2;
		Grib2PushAtt(lat_att_list,"Latin2",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("degrees_north");
		Grib2PushAtt(lat_att_list,"units",tmp_string,1,nclTypestringClass); (*nlatatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("Lambert Conformal (secant, tangent, conical or bipolar)");
		grid_name = *tmp_string;
		Grib2PushAtt(lat_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlatatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("latitude");
		Grib2PushAtt(lat_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlatatts)++;
	}
	if(do_rot && rot_att_list != NULL) {
		g2Do_Rotation_Atts(grid_name,rot_att_list,nrotatts,grid_oriented);
	}

}


/* Grid template 40 */
void g2GDSGAGrid
#if NhlNeedProto
(
	Grib2ParamList* thevarrec, 
	float** lat, 
	int* n_dims_lat,
	ng_size_t** dimsizes_lat,
	float** lon,
	int* n_dims_lon,
	ng_size_t** dimsizes_lon,
	float** rot,
	int* n_dims_rot,
	ng_size_t **dimsizes_rot,
	Grib2AttInqRecList** lat_att_list, 
	int* nlatatts, 
	Grib2AttInqRecList** lon_att_list, 
	int* nlonatts,
	Grib2AttInqRecList** rot_att_list,
	int* nrotatts
)
#else
(thevarrec, lat, n_dims_lat, dimsizes_lat, lon, n_dims_lon, dimsizes_lon,
 rot, n_dims_rot, dimsizes_rot,
 lat_att_list,nlatatts,lon_att_list, nlonatts, rot_att_list, nrotatts)
GribParamList* thevarrec; 
float** lat; 
int* n_dims_lat;
ng_size_t** dimsizes_lat;
float** lon;
int* n_dims_lon;
ng_size_t** dimsizes_lon;
float** rot;
int* n_dims_rot;
ng_size_t **dimsizes_rot;
Grib2AttInqRecList** lat_att_list; 
int* nlatatts; 
Grib2AttInqRecList** lon_att_list; 
int* nlonatts;
Grib2AttInqRecList** rot_att_list;
int* nrotatts;
#endif
{
	double rtod = DegPerRad;
	int nlat,nlon;
	double *theta;
	double *wts;
	int lwork= 0;
	double *work = NULL;
	int i,ierror,k;
	float *tmp_float;
	NclQuark *tmp_string;
	int is_thinned_lon = 0;
	int idir,jdir;
	int try = 0;
	double la1,la2,lo1,lo2;
	double di = 1;
	G2_GDS *gds;
	g2GATemplate *ga;
	double scale_factor;


	*lat = NULL;
	*n_dims_lat = 0;
	*dimsizes_lat = NULL;
	*lon = NULL;
	*n_dims_lon= 0;
	*dimsizes_lon= NULL;
	if ((thevarrec->thelist == NULL)  ||  (thevarrec->ref_rec == NULL)) 
		return;

	gds = (G2_GDS *) thevarrec->ref_rec->gds;
	if (gds == NULL) {
		return;
	}
	ga = (g2GATemplate *) gds->grid_template;
	
	nlon = ga->npts_along_parallel;
	is_thinned_lon = (nlon == -1); 
	idir = g2getbits(ga->scan_mode_flags, 7, 1) == 0 ? 1 : -1;
	jdir = g2getbits(ga->scan_mode_flags, 6, 1) == 0 ? -1 : 1;
	if (ga->subdiv_basic_angle != 0 && ga->angl_init_prod_domain != 0)
		scale_factor = (double) ga->angl_init_prod_domain / 
			(double) ga->subdiv_basic_angle;
	else 
		scale_factor = 1.0 / (double) G2_SCALE_FACTOR;
	/* save the scan mode flags -- needed for decoding the data */
	thevarrec->gds->scan_mode_offset = (int*)(&(ga->scan_mode_flags)) - (int*)ga;

	la1 = ga->lat_first_gridpt * scale_factor;
	lo1 = ga->lon_first_gridpt * scale_factor;
	la2 = ga->lat_last_gridpt * scale_factor;
	lo2 = ga->lon_last_gridpt * scale_factor;

	*n_dims_lat = 1;
	*dimsizes_lat = malloc(sizeof(ng_size_t));
	(*dimsizes_lat)[0] = ga->npts_along_meridian;
	nlat = 2 * ga->nparallels_pole2equator;


	/* 
	 * this is a hack for certain IPCC data that does not have the correct info in gds[25+]. 
	 * I hope it doesn't screw anything else up.
	 */
		
	if (nlat > (*dimsizes_lat)[0]) {
		nlat = (*dimsizes_lat)[0];
		NhlPError(NhlWARNING,NhlEUNKNOWN,
			  "g2GDSGAGrid: Invalid value for Gaussian LatLon (grid template 30) parameter N: trying to recover");
	}

	/* do the longitude */
	if (is_thinned_lon) {
		thevarrec->gds->is_thinned_grid = 1;
		g2GetThinnedLonParams(gds, nlat, lo1, lo2, idir, &nlon, &di);
	} else {
		if (lo1 == lo2) { /* global grid probably specified according to GRIB2 spec (lo1 and lo2 both must be positive - but this is too inconvenient for us)  */
			if (idir == 1) {
				if (lo1 > 0) {
					lo1 -= 360000;
				}
				else {
					lo2 += 360000;
				}
			}
			else {
				if (lo1 > 0) {
					lo2 -= 360000;
				}
				else {
					lo1 += 360000;
				}
			}
		}
		if (idir == 1) {
			float ti = lo2;
			while (ti < lo1) {
				ti += 360.0;
			}
			di = (ti - lo1) / (double) (nlon - 1);
		}
		else {
			float ti = lo1;
			while (ti < lo2) {
				ti += 3600.0;
			}
			di = (ti - lo2) / (double) (nlon - 1);
		}
	}
	*dimsizes_lon = (ng_size_t *) NclMalloc(sizeof(ng_size_t));
	*(*dimsizes_lon) = nlon;
	*n_dims_lon = 1;
	*lon = (float *) NclMalloc((unsigned)sizeof(float) * nlon);
	for (i = 0; i < *(*dimsizes_lon) ; i++)
		(*lon)[i] = (float)(lo1 + idir * i * di) ;


	/* now the gaussian latitudes */
	theta = (double*)NclMalloc(sizeof(double)*nlat);
	wts = (double*)NclMalloc(sizeof(double)*nlat);
	lwork = 4 * nlat*(nlat+1)+2;
	work = (double*)NclMalloc(sizeof(double)*lwork);
	*lat = (float*)NclMalloc(sizeof(float)*nlat);
/*
 * These come out south to north
 * The conditional that goes to TRY2 is also part of the IPCC hack.
 */
TRY2:
	NGCALLF(gaqdnio,GAQDNIO)(&nlat,theta,wts,work,&lwork,&ierror);


	if(jdir == -1) {
		/* -j direction implies north to south*/
		i = nlat -1;
		while(i >= 0) {
			if(_NhlCmpDAny2(la1,rtod*theta[i] - 90,5,1.0e-20) >= 0.0) {
				break;
			} else {
				i--;	
			}
		}
		if (i == 0 && try < 1 && nlat != (*dimsizes_lat)[0]) {
			NhlPError(NhlWARNING,NhlEUNKNOWN,
				  "g2GDSGAGrid: Invalid value Gaussian LatLon grid: setting N equal to Nj (See GRIB 2 Template 3.40)");
			try++;
			nlat = (*dimsizes_lat)[0];
			theta = (double*)NclRealloc(theta,sizeof(double)*nlat);
			wts = (double*)NclRealloc(wts,sizeof(double)*nlat);
			lwork = 4 * nlat*(nlat+1)+2;
			work = (double*)NclRealloc(work,sizeof(double)*lwork);
			*lat = (float*)NclRealloc(*lat,sizeof(float)*nlat);
			goto TRY2;
		}
		k = 0;
		while((k<(*dimsizes_lat)[0])&&(i>=0)) {
			if(_NhlCmpDAny2(la2,rtod*theta[i] - 90,5,1.0e-20) >=0.0) {
				break;
			} else {
				(*lat)[k++] = rtod*theta[i] - 90.0;
				i--;	
			}
		}
		if((i >=0)&&(k<(*dimsizes_lat)[0])) {
			(*lat)[k] = rtod*theta[i] - 90.0;
		}
	
	} else {
		/* +j direction implies south to north*/
		i = 0;
		while(i<nlat) {
			if(_NhlCmpDAny2(la1,rtod*theta[i] - 90,5,1.0e-20) <= 0.0) {
				break;
			} else {
				i++;		
			}
		}
		if (i == nlat && try < 1 && nlat != (*dimsizes_lat)[0]) {
			NhlPError(NhlWARNING,NhlEUNKNOWN,
				  "g2GDSGAGrid: Invalid value Gaussian LatLon grid: setting N equal to Nj (See GRIB 2 Template 3.40)");
			try++;
			nlat = (*dimsizes_lat)[0];
			theta = (double*)NclRealloc(theta,sizeof(double)*nlat);
			wts = (double*)NclRealloc(wts,sizeof(double)*nlat);
			lwork = 4 * nlat*(nlat+1)+2;
			work = (double*)NclRealloc(work,sizeof(double)*lwork);
			*lat = (float*)NclRealloc(*lat,sizeof(float)*nlat);
			goto TRY2;
		}
		k = 0;
		while((i<nlat)&&(k<(*dimsizes_lat)[0])) {
			if(_NhlCmpDAny2(la2,rtod*theta[i] - 90,5,1.0e-20) <= 0.0) {
				break;
			} else {
				(*lat)[k++] = rtod*theta[i] - 90.0;
				i++;	
			}
		}
		if((i < nlat)&&(k<(*dimsizes_lat)[0])) {
			(*lat)[i] = rtod*theta[i] - 90.0;
		}

	}
	if (k < (*dimsizes_lat)[0] - 1) {
		NhlPError(NhlWARNING,NhlEUNKNOWN,
			  "GdsGAGrid: Gaussian latitude coordinate array is partially or wholly undefined");
	}

	NclFree(work);
	NclFree(wts);
	NclFree(theta);

	if(lon_att_list != NULL) {
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la1;
		Grib2PushAtt(lon_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo1;
		Grib2PushAtt(lon_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la2;
		Grib2PushAtt(lon_att_list,"La2",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo2;
		Grib2PushAtt(lon_att_list,"Lo2",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = di;
		Grib2PushAtt(lon_att_list,"Di",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = nlat/2.0;
		Grib2PushAtt(lon_att_list,"N",tmp_float,1,nclTypefloatClass); (*nlonatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("degrees_east");
		Grib2PushAtt(lon_att_list,"units",tmp_string,1,nclTypestringClass); (*nlonatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		if (is_thinned_lon) {
			*tmp_string = NrmStringToQuark("Gaussian Latitude/Longitude (Quasi-Regular)");
		} else {
			*tmp_string = NrmStringToQuark("Gaussian Latitude/Longitude");
		}
		Grib2PushAtt(lon_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlonatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("longitude");
		Grib2PushAtt(lon_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlonatts)++;
	}
	if(lat_att_list != NULL) {
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la1;
		Grib2PushAtt(lat_att_list,"La1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo1;
		Grib2PushAtt(lat_att_list,"Lo1",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = la2;
		Grib2PushAtt(lat_att_list,"La2",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = lo2;
		Grib2PushAtt(lat_att_list,"Lo2",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = di;
		Grib2PushAtt(lat_att_list,"Di",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_float= (float*)NclMalloc(sizeof(float));
		*tmp_float = nlat/2.0;
		Grib2PushAtt(lat_att_list,"N",tmp_float,1,nclTypefloatClass); (*nlatatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("degrees_north");
		Grib2PushAtt(lat_att_list,"units",tmp_string,1,nclTypestringClass); (*nlatatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		if (is_thinned_lon) {
			*tmp_string = NrmStringToQuark("Gaussian Latitude/Longitude (Quasi-Regular)");
		} else {
			*tmp_string = NrmStringToQuark("Gaussian Latitude/Longitude");
		}
		Grib2PushAtt(lat_att_list,"grid_type",tmp_string,1,nclTypestringClass); (*nlatatts)++;
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = NrmStringToQuark("latitude");
		Grib2PushAtt(lat_att_list,"long_name",tmp_string,1,nclTypestringClass); (*nlatatts)++;
	}
}


/* Grid template 50 */
void g2GDSSHGrid
#if NhlNeedProto
(
	Grib2ParamList* thevarrec, 
	float** lat, 
	int* n_dims_lat,
	ng_size_t** dimsizes_lat,
	float** lon,
	int* n_dims_lon,
	ng_size_t** dimsizes_lon,
	float** rot,
	int* n_dims_rot,
	ng_size_t **dimsizes_rot,
	Grib2AttInqRecList** lat_att_list, 
	int* nlatatts, 
	Grib2AttInqRecList** lon_att_list, 
	int* nlonatts,
	Grib2AttInqRecList** rot_att_list,
	int* nrotatts
)
#else
(thevarrec, lat, n_dims_lat, dimsizes_lat, lon, n_dims_lon, dimsizes_lon,
 rot, n_dims_rot, dimsizes_rot,
 lat_att_list,nlatatts,lon_att_list, nlonatts, rot_att_list, nrotatts)
GribParamList* thevarrec; 
float** lat; 
int* n_dims_lat;
ng_size_t** dimsizes_lat;
float** lon;
int* n_dims_lon;
ng_size_t** dimsizes_lon;
float** rot;
int* n_dims_rot;
ng_size_t **dimsizes_rot;
Grib2AttInqRecList** lat_att_list; 
int* nlatatts; 
Grib2AttInqRecList** lon_att_list; 
int* nlonatts;
Grib2AttInqRecList** rot_att_list;
int* nrotatts;
#endif
{
	G2_GDS *gds;
	g2SHTemplate *sh;

	*lat = NULL;
	*n_dims_lat = 0;
	*dimsizes_lat = NULL;
	*lon = NULL;
	*n_dims_lon= 0;
	*dimsizes_lon= NULL;
	if ((thevarrec->thelist == NULL)  ||  (thevarrec->ref_rec == NULL)) 
		return;

	gds = (G2_GDS *) thevarrec->ref_rec->gds;
	if (gds == NULL) {
		return;
	}
	sh = (g2SHTemplate *) gds->grid_template;

	if (sh->j_pent_res < 1)
		return;

	*n_dims_lat =  1;
	*dimsizes_lat = NclMalloc(sizeof(ng_size_t));
	*(*dimsizes_lat) = sh->k_pent_res + 1;
	*lon = NULL;
	*n_dims_lon= 1;
	*dimsizes_lon= NclMalloc(sizeof(ng_size_t));
	*(*dimsizes_lon) = sh->m_pent_res + 1;

	return;
	
}



/* Grid template 90 */
void g2GDSSVGrid
#if NhlNeedProto
(
	Grib2ParamList* thevarrec, 
	float** lat, 
	int* n_dims_lat,
	ng_size_t** dimsizes_lat,
	float** lon,
	int* n_dims_lon,
	ng_size_t** dimsizes_lon,
	float** rot,
	int* n_dims_rot,
	ng_size_t **dimsizes_rot,
	Grib2AttInqRecList** lat_att_list, 
	int* nlatatts, 
	Grib2AttInqRecList** lon_att_list, 
	int* nlonatts,
	Grib2AttInqRecList** rot_att_list,
	int* nrotatts
)
#else
(thevarrec, lat, n_dims_lat, dimsizes_lat, lon, n_dims_lon, dimsizes_lon,
 rot, n_dims_rot, dimsizes_rot,
 lat_att_list,nlatatts,lon_att_list, nlonatts, rot_att_list, nrotatts)
GribParamList* thevarrec; 
float** lat; 
int* n_dims_lat;
ng_size_t** dimsizes_lat;
float** lon;
int* n_dims_lon;
ng_size_t* dimsizes_lon;
float** rot;
int* n_dims_rot;
ng_size_t **dimsizes_rot;
GribAttInqRecList** lat_att_list; 
int* nlatatts; 
GribAttInqRecList** lon_att_list; 
int* nlonatts;
GribAttInqRecList** rot_att_list;
int* nrotatts;
#endif
{
	G2_GDS *gds;
	int nlon,nlat;
	g2SVTemplate *sv;

	/* 
	 * This can handle any grid that has the dimensions of the grid specified as 
	 * elements 7 and 8 of the g2clib grid template -- whatever the dims happen to be called
	 * currently includes templates 1,2,3, 31, 41, 42, 43, 90, and 110. 
	 */
	
	*lat = NULL;
	*lon = NULL;
	*rot = NULL;
	*n_dims_lat = 0;
	*dimsizes_lat = NULL;
	*n_dims_lon= 0;
	*dimsizes_lon = NULL;
	*n_dims_rot = 0;
	*dimsizes_rot = NULL;
	if((thevarrec->thelist == NULL)||(thevarrec->ref_rec == NULL)) 
		return;

	gds = (G2_GDS *) thevarrec->ref_rec->gds;
	if (gds == NULL) {
		return;
	}
	sv = (g2SVTemplate *) gds->grid_template;

	nlon = sv->npts_along_x_axis;
	nlat = sv->npts_along_y_axis;
	if (nlon <= 1 || nlat <= 1 ) {
		return;
	}

	*dimsizes_lat = (ng_size_t*)NclMalloc(sizeof(ng_size_t));
	*dimsizes_lon = (ng_size_t*)NclMalloc(sizeof(ng_size_t));
	*(*dimsizes_lon) = nlon;
	*(*dimsizes_lat) = nlat;
	*n_dims_lat = 1;
	*n_dims_lon = 1;

	return;
}


static void _g2NewGridCache
#if NhlNeedProto
(Grib2FileRecord *therec,int grid_index,int grid_number,int n_dims_lat,ng_size_t *dimsizes_lat,int n_dims_lon,ng_size_t *dimsizes_lon)
#else
(therec,grid_index,grid_number,n_dims_lat,dimsizes_lat,n_dims_lon,dimsizes_lon)
Grib2FileRecord *therec;
int grid_index;
int grid_number;
int n_dims_lat;
ng_size_t *dimsizes_lat;
int n_dims_lon;
ng_size_t *dimsizes_lon;
#endif
{
	NclGrib2CacheList *newlist;

	if(therec->grib_grid_cache == NULL) {
		therec->grib_grid_cache = NclMalloc(sizeof(NclGrib2CacheList));
		newlist = NULL;
	} else {
		newlist = therec->grib_grid_cache;
                therec->grib_grid_cache = NclMalloc(sizeof(NclGrib2CacheList));
	}
		
	therec->grib_grid_cache->grid_number = grid_number;
	therec->grib_grid_cache->grid_index = grid_index;
	therec->grib_grid_cache->int_missing_rec = NULL;
	therec->grib_grid_cache->float_missing_rec = NULL;
	if (grid_number == 50) {
		therec->grib_grid_cache->n_dims = 3;
		therec->grib_grid_cache->dimsizes[0] = 2;
		therec->grib_grid_cache->dimsizes[2] = *dimsizes_lon;
		therec->grib_grid_cache->dimsizes[1] = *dimsizes_lat;
	}
	else {
		therec->grib_grid_cache->n_dims = 2;
		if((n_dims_lon == 1) &&(n_dims_lat ==1)) {
			therec->grib_grid_cache->dimsizes[1] = *dimsizes_lon;
			therec->grib_grid_cache->dimsizes[0] = *dimsizes_lat;
		} else if((n_dims_lon ==2) &&(n_dims_lat ==2)
			  &&(dimsizes_lon[0] == dimsizes_lat[0])&&(dimsizes_lon[1] == dimsizes_lat[1])) {
			therec->grib_grid_cache->dimsizes[1] = dimsizes_lon[1];
			therec->grib_grid_cache->dimsizes[0] = dimsizes_lon[0];
		} else {
			if(n_dims_lat ==2) {
				therec->grib_grid_cache->dimsizes[1] = dimsizes_lat[1];
				therec->grib_grid_cache->dimsizes[0] = dimsizes_lat[0];
			} else if(n_dims_lon ==2) {
				therec->grib_grid_cache->dimsizes[1] = dimsizes_lon[1];
				therec->grib_grid_cache->dimsizes[0] = dimsizes_lon[0];
			}
		}
	}

	therec->grib_grid_cache->n_entries = 0;
	therec->grib_grid_cache->thelist = NULL;
	therec->grib_grid_cache->next = newlist;

	return;
}

static void g2Merge2
#if 	NhlNeedProto
(float *tmp_lvs,float *tmp_lvs1,int *tmp_n_lvs,float *lv_vals,float *lv_vals1,int n_lv,float** out_lvs0,float **out_lvs1)
#else
(tmp_lvs,tmp_lvs,tmp_n_lvs,lv_vals,lv_vals1,n_lv,out_lvs0,out_lvs1)
float *tmp_lvs;
float *tmp_lvs1;
int *tmp_n_lvs;
float *lv_vals;
float *lv_vals1;
int n_lv;
float **out_lvs0;
float **out_lvs1;
#endif
{
	int i,j,k;
	float *tmp_out_lvs = NULL;
	float *tmp_out_lvs1 = NULL;

	i = 0;	
	j = 0;
	k = 0;

	tmp_out_lvs = (float*)NclMalloc((unsigned)sizeof(float)*(*tmp_n_lvs + n_lv));
	tmp_out_lvs1 = (float*)NclMalloc((unsigned)sizeof(float)*(*tmp_n_lvs + n_lv));


		
	while((i < *tmp_n_lvs)&&(j< n_lv)) {
		if((tmp_lvs[i] == lv_vals[j])&&(tmp_lvs1[i] == lv_vals1[j])) {
			tmp_out_lvs[k] = tmp_lvs[i];
			tmp_out_lvs1[k] = tmp_lvs1[i];
			i++;
			j++;
			k++;
		} else if((tmp_lvs[i] < lv_vals[j])||((tmp_lvs[i] == lv_vals[j])&&(tmp_lvs1[i] != lv_vals1[j]))){
			tmp_out_lvs[k] = tmp_lvs[i];
			tmp_out_lvs1[k] = tmp_lvs1[i];
			k++;
			i++;
		} else {
			tmp_out_lvs[k] = lv_vals[j];
			tmp_out_lvs1[k] = lv_vals1[j];
			k++;
			j++;
		}
	}
	if(i< *tmp_n_lvs) {
		for( ; i < *tmp_n_lvs;i++) {
			tmp_out_lvs[k] = tmp_lvs[i];
			tmp_out_lvs1[k] = tmp_lvs1[i];
			k++;
		}	
	} else {
		for( ; j < n_lv ;j++) {
			tmp_out_lvs[k] = lv_vals[j];
			tmp_out_lvs1[k] = lv_vals1[j];
			k++;
		}	
	}
	

	NclFree(tmp_lvs);
	NclFree(tmp_lvs1);
	*tmp_n_lvs = k;	
	*out_lvs0 = tmp_out_lvs;
	*out_lvs1 = tmp_out_lvs1;

	return;
}

static float *g2Merge
#if 	NhlNeedProto
(float *tmp_lvs,int *tmp_n_lvs,float *lv_vals,int n_lv)
#else
(tmp_lvs,tmp_n_lvs,lv_vals,n_lv)
float *tmp_lvs;
int *tmp_n_lvs;
float *lv_vals;
int n_lv;
#endif
{
	int i,j,k;
	float *out_lvs = NULL;

	i = 0;	
	j = 0;
	k = 0;

	out_lvs = (float*)NclMalloc((unsigned)sizeof(float)*(*tmp_n_lvs + n_lv));

		
	while ((i < *tmp_n_lvs)&&(j< n_lv)) {
		if (tmp_lvs[i] == lv_vals[j]) {
			out_lvs[k] = tmp_lvs[i];
			i++;
			j++;
			k++;
		} else if(tmp_lvs[i] < lv_vals[j]) {
			out_lvs[k] = tmp_lvs[i];
			k++;
			i++;
		} else {
			out_lvs[k] = lv_vals[j];
			k++;
			j++;
		}
	}

	if (i< *tmp_n_lvs) {
		for( ; i < *tmp_n_lvs;i++) {
			out_lvs[k] = tmp_lvs[i];
			k++;
		}	
	} else {
		for( ; j < n_lv ;j++) {
			out_lvs[k] = lv_vals[j];
			k++;
		}	
	}
	

	NclFree(tmp_lvs);
	*tmp_n_lvs = k;	
	return(out_lvs);
}

static int *g2MergeFT
#if 	NhlNeedProto
(int *tmp_fts,int *tmp_n_fts,int *ft_vals,int n_ft)
#else
(tmp_fts,tmp_n_fts,ft_vals,n_ft)
int *tmp_fts;
int *tmp_n_fts;
int *ft_vals;
int n_ft;
#endif
{
	int i,j,k;
	int *out_fts = NULL;

	i = 0;	
	j = 0;
	k = 0;

	out_fts = (int*)NclMalloc((unsigned)sizeof(int)*(*tmp_n_fts + n_ft));

		
	while ((i < *tmp_n_fts)&&(j< n_ft)) {
		if (tmp_fts[i] == ft_vals[j]) {
			out_fts[k] = tmp_fts[i];
			i++;
			j++;
			k++;
		} else if(tmp_fts[i] < ft_vals[j]) {
			out_fts[k] = tmp_fts[i];
			k++;
			i++;
		} else {
			out_fts[k] = ft_vals[j];
			k++;
			j++;
		}
	}

	if (i< *tmp_n_fts) {
		for( ; i < *tmp_n_fts;i++) {
			out_fts[k] = tmp_fts[i];
			k++;
		}	
	} else {
		for( ; j < n_ft ;j++) {
			out_fts[k] = ft_vals[j];
			k++;
		}	
	}
	

	NclFree(tmp_fts);
	*tmp_n_fts = k;	
	return(out_fts);
}

static NrmQuark g2GetItQuark
#if NhlNeedProto
(G2_GIT *the_it)
#else
(the_it)
G2_GIT *the_it;
#endif
{
	int y = 0;
	unsigned short mn = 0;
	unsigned short d = 0;
	char buffer[100];

	HeisDiffDate(1,1,the_it->year,the_it->days_from_jan1,&d,&mn,&y);

	if (mn < 10) {
		sprintf(buffer,"0%d/",mn);
	} else {
		sprintf(buffer,"%d/",mn);
	}

	if (d < 10) {
		sprintf(&(buffer[strlen(buffer)]),"0%d/",d);
	} else {
		sprintf(&(buffer[strlen(buffer)]),"%d/",d);
	}
	sprintf(&(buffer[strlen(buffer)]),"%d ",y);

	if (((int)the_it->minute_of_day / 60) < 10) {
		sprintf(&(buffer[strlen(buffer)]),"(0%d:",(int)the_it->minute_of_day / 60);
	} else {
		sprintf(&(buffer[strlen(buffer)]),"(%d:",(int)the_it->minute_of_day / 60);
	}

	if (((int)the_it->minute_of_day % 60) < 10 ) {
		sprintf(&(buffer[strlen(buffer)]),"0%d)",(int)the_it->minute_of_day % 60);
	} else {
		sprintf(&(buffer[strlen(buffer)]),"%d)",(int)the_it->minute_of_day % 60);
	}

	return(NrmStringToQuark(buffer));
}

static NrmQuark g2GetEnsQuark
#if NhlNeedProto
(G2_ENS *ens)
#else
(ens)
G2_ENS *ens;
#endif
{
	char buf[256];

	switch (ens->type) {
	case 0: 
		sprintf(buf,"unperturbed high-resolution control forecast");
		break;
	case 1:
		sprintf(buf,"unperturbed low-resolution control forecast");
		break;
	case 2:
		sprintf(buf,"negatively perturbed forecast # %d",ens->id);
		break;
	case 3:
		sprintf(buf,"positively perturbed forecast # %d",ens->id);
		break;
	default:
		sprintf(buf,"type: %d, id: %d",ens->type,ens->id);

	}

	return NrmStringToQuark(buf);
}

static NclBasicDataTypes g2GribMapToNcl 
#if	NhlNeedProto
(void* the_type)
#else
(the_type)
	void *the_type;
#endif
{
	int int_or_float = *(int*)the_type;

	if(int_or_float) {
		return(NCL_int);
	} else {
		return(NCL_float);
	}
}

static void *g2GribMapFromNcl
#if	NhlNeedProto
(NclBasicDataTypes the_type)
#else
(the_type)
	NclBasicDataTypes the_type;
#endif
{
	int *tmp ;

	tmp = (int*)NclMalloc((unsigned)sizeof(int));
	
	switch(the_type) {
	case NCL_int:
		*tmp = 1;
		break;
	case NCL_float:
		*tmp = 0;
		break;
	default:
		*tmp = -1;
	}

	return ((void*)tmp);
}

static int g2LVNotEqual( Grib2RecordInqRecList *s_1, Grib2RecordInqRecList *s_2)
{

	if (s_1->rec_inq->traits.second_level_type != 255) {
		if (s_1->rec_inq->level0 == s_2->rec_inq->level0) {
			if (s_1->rec_inq->level1 == s_2->rec_inq->level1) {
				return(0);
			} else {
				return(s_1->rec_inq->level1 - s_2->rec_inq->level1 > 0 ? 1 : -1);
			}
		} else {
			return(s_1->rec_inq->level0 - s_2->rec_inq->level0 > 0 ? 1 : -1);
		}
	} else {
		if (s_1->rec_inq->level0 == s_2->rec_inq->level0) {
			return(0);
		} else {
			return(s_1->rec_inq->level0 - s_2->rec_inq->level0 > 0 ? 1 : -1);
		}
	} 
}

/* 
 * These are the codes in GRIB 2 - Table 4.4 - for time units arranged in order from 
 * short to long duration. The convert table below is the conversion from
 * the shortest duration (second) to each of the other units. (For periods longer
 * than a day there is inaccuracy because the periods vary depending on which which 
 * month and/or year we are talking about. For now use average based on 365.25 days per year.
 * This will need to be refined.
 */

static int Unit_Code_Order[] = { 13,0,1,10,11,12,2,3,4,5,6,7 };
static double Unit_Convert[] = { 1.0, 60.0, 3600.0, 10800.0, 21600.0, /* 1 sec - 6 hr */
				 43200.0,86400.0,2629800.0, 31557600.0, /* 12 hr - 1 yr */
				 315576000.0,946728000.0,3155760000.0};   /* 10 yr - 100 yr */

static int _g2GetShortestTimeUnit
#if	NhlNeedProto
(int unit1, int unit2)
#else
(unit1, unit2)
int unit1;
int unit2;
#endif
{
	int uix1, uix2;

	/* 
	 * These are the codes in ON388 - Table 4 - for time units arranged in order from 
	 * short to long duration. 
	 */

	if (unit1 < 0)
		return unit2;
	if (unit2 < 0)
		return unit1;
	for (uix1 = 0; uix1 < NhlNumber(Unit_Code_Order); uix1++) {
		if (unit1  == Unit_Code_Order[uix1])
			break;
	}
	for (uix2 = 0; uix2 < NhlNumber(Unit_Code_Order); uix2++) {
		if (unit2 == Unit_Code_Order[uix2])
			break;
	}
	if (uix1 >= NhlNumber(Unit_Code_Order) && uix2 >= NhlNumber(Unit_Code_Order)){
		return -1;
	}
	else if (uix1 >= NhlNumber(Unit_Code_Order)) { 
		return unit2;
	}
	else if (uix2 >= NhlNumber(Unit_Code_Order)) { 
		return unit1;
	}
	else if (uix1 < uix2) { 
		return unit1;
	}
	return unit2;
}

static void _g2SetCommonTimeUnit
#if	NhlNeedProto
(Grib2ParamList *node, Grib2RecordInqRec* grib_rec)
#else
(node,grib_rec)
Grib2ParamList *node;
Grib2RecordInqRec* grib_rec;
#endif
{
	int cix, fix;
	static int month_ix = 7;
	static NrmQuark var_name_q = NrmNULLQUARK;

	for (cix = 0; cix < NhlNumber(Unit_Code_Order); cix++) {
		if (node->forecast_time_units == Unit_Code_Order[cix])
			break;
	}
	for (fix = 0; fix < NhlNumber(Unit_Code_Order); fix++) {
		if ((int)grib_rec->forecast_time_units == Unit_Code_Order[fix])
			break;
	}
	if (fix >= NhlNumber(Unit_Code_Order)) {
		NhlPError(NhlWARNING,NhlEUNKNOWN,
			  "NclGRIB2: Unsupported time unit found for parameter (%s), continuing anyway.",
			  NrmQuarkToString(grib_rec->var_name_q));
	}
	else if (cix >= NhlNumber(Unit_Code_Order)) { 
		/* current time units are unsupported so use the new unit */
		node->forecast_time_units = (int)grib_rec->forecast_time_units;
	}
	else if (Unit_Code_Order[fix] < Unit_Code_Order[cix]) { 
		/* choose the shortest duration as the common unit */
		node->forecast_time_units = (int)grib_rec->forecast_time_units;
	}
	if (fix >= month_ix && grib_rec->var_name_q == var_name_q) {
		NhlPError(NhlWARNING,NhlEUNKNOWN,
			  "NclGRIB2: Variable time unit codes representing time durations of a month or more in variable (%s): requires approximation to convert to common unit",
			  NrmQuarkToString(grib_rec->var_name_q));
		var_name_q = grib_rec->var_name_q;
	}
		
	/* Set the variable_time_unit flag */
	node->variable_time_unit = True;

	return;
}

static int _g2GetConvertedTime
#if	NhlNeedProto
(int common_time_unit, int time_unit, int time_offset)
#else
(common_time_unit, time_unit, offset)
int common_time_unit;
int time_unit;
unsigned char *offset;
#endif
{
	int cix = 0,tix = 0;
	double c_factor = 1.0;
	if (common_time_unit != time_unit) {
		for (cix = 0; cix < NhlNumber(Unit_Code_Order); cix++) {
			if (common_time_unit == Unit_Code_Order[cix])
				break;
		}
		for (tix = 0; tix < NhlNumber(Unit_Code_Order); tix++) {
			if (time_unit == Unit_Code_Order[tix])
				break;
		}
		/* this condition must be met in order to do a valid conversion */
		if (cix < NhlNumber(Unit_Code_Order) && tix < NhlNumber(Unit_Code_Order)) { 
				c_factor = Unit_Convert[tix] / Unit_Convert[cix];
		}
	}
	if (cix > 6 || tix > 6) { /* fuzzy number -- unit > day */
		return ((int)(time_offset * c_factor + 0.5));
	}
	else { /* do it the way it's always been done */
		return ((int)(time_offset * c_factor));
	}
}


static int _g2GetLevels
# if NhlNeedProto
(float *l0, float *l1, int l0_type, int l1_type, int l0_val, int l0_scale_fac, int l1_val, int l1_scale_fac)
# else
	(l0, l1, l0_type, l1_type, l0_val, l0_scale_fac, l1_val, l1_scale_fac)
float *l0;
float *l1;
int l0_type;
int l1_type;
int l0_val;
int l0_scale_fac;
int l1_val;
int l1_scale_fac;
# endif
{

        *l0 = GRIB2_MISSING_LEVEL_VAL;
        *l1 = GRIB2_MISSING_LEVEL_VAL;

	if ((l0_type >= 0 && l0_type < 255) && l0_val != 255) {
		if (l0_scale_fac == 0) 
			*l0 = l0_val;
		else if (l0_scale_fac != -127)
			*l0 = l0_val * pow(0.1,l0_scale_fac);
	}
	if ((l1_type >= 0 && l1_type < 255) && l1_val != 255) {
		if (l1_scale_fac == 0) 
			*l1 = l1_val;
		else if (l1_scale_fac != -127)
			*l1 = l1_val * pow(0.1,l1_scale_fac);
	}


    return 0;
}


static int g2level_comp
#if	NhlNeedProto
(Const void *s1, Const void *s2)
#else
(s1, s2)
void *s1;
void *s2;
#endif
{
	Grib2RecordInqRecList *s_1 = *(Grib2RecordInqRecList**)s1;
	Grib2RecordInqRecList *s_2 = *(Grib2RecordInqRecList**)s2;

	if(s_1->rec_inq->traits.second_level_type != 255) {
		if(s_1->rec_inq->level0 == s_2->rec_inq->level0) {
			if(s_1->rec_inq->level1 == s_2->rec_inq->level1) {
				return (0);
			}
			else {
				return(s_1->rec_inq->level1 - s_2->rec_inq->level1 > 0 ? 1 : -1);
			}
		} else {
			return(s_1->rec_inq->level0 - s_2->rec_inq->level0 > 0 ? 1 : -1);
		}
	} else {
		if(s_1->rec_inq->level0 == s_2->rec_inq->level0) {
			return(0);
		}
		else {
			return(s_1->rec_inq->level0 - s_2->rec_inq->level0 > 0 ? 1 : -1);
		}	
	} 
}

static int g2date_comp
#if 	NhlNeedProto
(Const void *s1, Const void *s2)
#else
(s1, s2)
void *s1;
void *s2;
#endif
{
	Grib2RecordInqRecList *s_1 = *(Grib2RecordInqRecList**)s1;
	Grib2RecordInqRecList *s_2 = *(Grib2RecordInqRecList**)s2;
	short result = 0;

	result = s_1->rec_inq->initial_time.year - s_2->rec_inq->initial_time.year;
	if(!result) {
		result = s_1->rec_inq->initial_time.days_from_jan1 - s_2->rec_inq->initial_time.days_from_jan1;
		if(!result) {
			result = s_1->rec_inq->initial_time.minute_of_day - s_2->rec_inq->initial_time.minute_of_day;
			if(!result) {
				result = s_1->rec_inq->time_offset- s_2->rec_inq->time_offset;
			}
		}
		
	} 
	if (! result) {
		return(g2level_comp(s1,s2));
	}

	return result;
}

static int g2record_comp
#if 	NhlNeedProto
(Const void *s1, Const void *s2)
#else
(s1, s2)
void *s1;
void *s2;
#endif
{
	Grib2RecordInqRecList *s_1 = *(Grib2RecordInqRecList**)s1;
	Grib2RecordInqRecList *s_2 = *(Grib2RecordInqRecList**)s2;
	int result = 0;

	if (! s_1->rec_inq->is_ensemble) /* if one is an ensemble they both have to be */
		return g2date_comp(s1,s2);

	result =  s_1->rec_inq->ens.id - s_2->rec_inq->ens.id;
	if (! result) {
		result =  s_1->rec_inq->ens.type - s_2->rec_inq->ens.type;
	}
	if (! result) {
		/* the prob_type has to be the same for records to be in the same variable */
		if  (s_1->rec_inq->ens.prob_type > -1) {
			switch (s_1->rec_inq->ens.prob_type) {
				/* case 0,3 involve lower limit case 1,4 upper limit case 2 both */
			case 0:
			case 2:
			case 3:
				result = s_1->rec_inq->ens.lower_limit_scale - s_2->rec_inq->ens.lower_limit_scale;
				if (! result)
					result = s_1->rec_inq->ens.lower_limit_value - s_2->rec_inq->ens.lower_limit_value;
				if  (s_1->rec_inq->ens.prob_type != 2)
					break;
			case 1:
			case 4:
				result = s_1->rec_inq->ens.upper_limit_scale - s_2->rec_inq->ens.upper_limit_scale;
				if (! result)
					result = s_1->rec_inq->ens.upper_limit_value - s_2->rec_inq->ens.upper_limit_value;
			}
		}
	}

	if (! result) {
		return g2date_comp(s1,s2);
	}

	return result;
}

static void AppendStatProcInfoToVarName
#if 	NhlNeedProto
(Grib2ParamList *param,
 int stat_type_only)
#else
(param, stat_type_only)
Grib2ParamList *param;
int stat_type_only
#endif
{
	char buffer[128];
        int time_period;

	strcpy(buffer,NrmQuarkToString(param->var_info.var_name_quark));

	switch (param->traits.stat_proc_type) {
	case 0:
		strcat(buffer,"_avg");
		break;
	case 1:
		strcat(buffer,"_acc");
		break;
	case 2:
		strcat(buffer,"_max");
		break;
	case 3:
		strcat(buffer,"_min");
		break;
	case 4:
		strcat(buffer,"_dife");
		break;
	case 5:
		strcat(buffer,"_rms");
		break;
	case 6:
		strcat(buffer,"_std");
		break;
	case 7:
		strcat(buffer,"_cov");
		break;
	case 8:
		strcat(buffer,"_difb");
		break;
	case 9:
		strcat(buffer,"_rat");
		break;
	default:
		/* add an underscore only if time period is to be appended */
		if (! stat_type_only) 
			strcat(buffer,"_"); 
		break;
	}
	if (stat_type_only) {
		param->var_info.var_name_quark = NrmStringToQuark(buffer);
		return;
	}

	time_period = abs(param->time_period);

	switch (param->time_period_units) {
	case 0:
		sprintf(&(buffer[strlen(buffer)]),"%dmin",time_period);
		break;
	case 1:
		sprintf(&(buffer[strlen(buffer)]),"%dh",time_period);
		break;
	case 2:
		sprintf(&(buffer[strlen(buffer)]),"%dd",time_period);
		break;
	case 3:
		sprintf(&(buffer[strlen(buffer)]),"%dm",time_period);
		break;
	case 4:
		sprintf(&(buffer[strlen(buffer)]),"%dy",time_period);
		break;
	case 5:
		sprintf(&(buffer[strlen(buffer)]),"%dy",time_period * 10);
		break;
	case 6:
		sprintf(&(buffer[strlen(buffer)]),"%dy",time_period * 30);
		break;
	case 7:
		sprintf(&(buffer[strlen(buffer)]),"%dy",time_period * 100);
		break;
	case 10:
		sprintf(&(buffer[strlen(buffer)]),"%dh",time_period * 3);
		break;
	case 11:
		sprintf(&(buffer[strlen(buffer)]),"%dh",time_period * 6);
		break;
	case 12:
		sprintf(&(buffer[strlen(buffer)]),"%dh",time_period * 12);
		break;
	case 13:
		sprintf(&(buffer[strlen(buffer)]),"%dsec",time_period);
		break;
	default:
		sprintf(&(buffer[strlen(buffer)]),"%d",time_period);
		break;
	}
	param->var_info.var_name_quark = NrmStringToQuark(buffer);
	return;
}

static void SetTimePeriodString
#if 	NhlNeedProto
(char *buf, int period, int indicator)
#else
(buf,period,indicator)
char *buf;
int period;
int indicator;
#endif
{
	int lperiod = abs(period);

	switch (indicator) {
	case 0:
		sprintf(buf,"%d minutes",lperiod);
		return;
	case  1:
		sprintf(buf,"%d hours",lperiod);
		return;
	case  2:
		sprintf(buf,"%d days",lperiod);
		return;
	case  3:
		sprintf(buf,"%d months",lperiod);
		return;
	case  4:
		sprintf(buf,"%d years",lperiod);
		return;
	case  5:
		sprintf(buf,"%d decades",lperiod);
		return;
	case  6:
		sprintf(buf,"%d decades",lperiod * 3);
		return;
	case  7:
		sprintf(buf,"%d centuries",lperiod);
		return;
	case  10:
		sprintf(buf,"%d hours",lperiod * 3);
		return;
	case  11:
		sprintf(buf,"%d hours",lperiod * 6);
		return;
	case  12:
		sprintf(buf,"%d hours",lperiod * 12);
		return;
	case  13:
		sprintf(buf,"%d seconds",lperiod);
		return;
	default:
		sprintf(buf,"%d (unknown units)",lperiod);
		return;
	}

}


static NrmQuark ForecastTimeUnitsToQuark
#if 	NhlNeedProto
(int forecast_time_units)
#else
(forecast_time_units)
 int forecast_time_units;
#endif
{
	switch (forecast_time_units) {
	case 0:
		return (NrmStringToQuark("minutes"));
	case 1:
		return (NrmStringToQuark("hours"));
	case 2:
		return (NrmStringToQuark("days"));
	case 3:
		return (NrmStringToQuark("months"));
	case 4:
		return (NrmStringToQuark("years"));
	case 5:
		return (NrmStringToQuark("decades"));
	case 6:
		return (NrmStringToQuark("normals (30 years)"));
	case 7:
		return (NrmStringToQuark("centuries"));
	case 10:
		return (NrmStringToQuark("3 hours"));
	case 11:
		return (NrmStringToQuark("6 hours"));
	case 12:
		return (NrmStringToQuark("12 hours"));
	case 13:
		return (NrmStringToQuark("seconds"));
	default:
		return (NrmStringToQuark("unknown"));
	}
}

static void _g2SetAttributeLists
#if 	NhlNeedProto
(Grib2FileRecord *therec)
#else
(therec)
Grib2FileRecord *therec;
#endif
{
	Grib2ParamList *step = NULL;
	NclQuark *tmp_string = NULL;
	int *tmp_int = NULL;
	ng_size_t tmp_dimsizes = 1;
	Grib2RecordInqRec *grib_rec = NULL;
	Grib2AttInqRecList *att_list_ptr= NULL;
	int i;
	float *tmp_level = NULL;
	void *tmp_fill = NULL;
	g2codeTable *ct = NULL;
	char buf[512];
	ct = (g2codeTable *) NclMalloc(1 * sizeof(g2codeTable));
	if (ct == NULL) {
		NhlPError(NhlFATAL, NhlEUNKNOWN,
			  " Unable to allocate code table data, cannot continue.");
		return;
	}
	memset(ct,0,sizeof(g2codeTable));


	step = therec->var_list;
	
	while(step != NULL) {
		/* Handle long_name, units, center, sub_center, model and _FillValue */
		for (i = 0; i < step->n_entries; i++) {
			if (step->thelist[i].rec_inq != NULL) {
				grib_rec = step->thelist[i].rec_inq;
				break;
			}
		}
		if (! grib_rec)
			continue;

		/* Handle coordinate attributes,  level, initial_time, forecast_time */
		if (step->traits.aerosol_type != 65535) {
			att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
			att_list_ptr->next = step->theatts;
			att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
			att_list_ptr->att_inq->name = NrmStringToQuark("aerosol_type");
			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			if (Grib2ReadCodeTable(step->ref_rec->table_source, 4, 
					       "4.233.table",step->traits.aerosol_type,-1,ct) < NhlWARNING) {
				return;
			}
			if (ct->descrip) {
				*tmp_string = NrmStringToQuark(ct->descrip);
			}
			else {
				sprintf(buf,"%d",step->traits.aerosol_type);
				*tmp_string = NrmStringToQuark(buf);
			}
			att_list_ptr->att_inq->thevalue = (NclMultiDValData)
				_NclCreateVal(NULL, NULL,
					      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes, 
					      PERMANENT, NULL, nclTypestringClass);
			step->theatts = att_list_ptr;
			step->n_atts++;
		}
		if (step->yymmddhh_isatt) {
			att_list_ptr = (Grib2AttInqRecList *) NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
			att_list_ptr->next = step->theatts;
			att_list_ptr->att_inq = (Grib2AttInqRec *) NclMalloc((unsigned)sizeof(Grib2AttInqRec));
			att_list_ptr->att_inq->name = NrmStringToQuark("initial_time");
			att_list_ptr->att_inq->thevalue = (NclMultiDValData)step->yymmddhh;

			/* Don't want two references */
			step->yymmddhh = NULL;
			step->theatts = att_list_ptr;
			step->n_atts++;
		}
		/* 
		 * don't create this att for observational data -- pds_templates 20 and 30 (as far as I can tell)
		 */
		if (step->forecast_time_isatt && step->traits.pds_template != 20 && step->traits.pds_template != 30 ) {
			att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
			att_list_ptr->next = step->theatts;
			att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
			att_list_ptr->att_inq->name = NrmStringToQuark("forecast_time_units");
			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			*tmp_string = ForecastTimeUnitsToQuark(step->forecast_time_units);
			att_list_ptr->att_inq->thevalue = (NclMultiDValData)
				_NclCreateVal( NULL, NULL, Ncl_MultiDValData, 0, 
					       (void*)tmp_string, NULL, 1 , &tmp_dimsizes, PERMANENT, NULL, nclTypestringClass);
			step->theatts = att_list_ptr;
			step->n_atts++;

			att_list_ptr = (Grib2AttInqRecList *) NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
			att_list_ptr->next = step->theatts;
			att_list_ptr->att_inq = (Grib2AttInqRec *) NclMalloc((unsigned)sizeof(Grib2AttInqRec));
			att_list_ptr->att_inq->name = NrmStringToQuark("forecast_time");
			att_list_ptr->att_inq->thevalue = (NclMultiDValData)step->forecast_time;

			/* Don't want two references */
			step->forecast_time= NULL;
			step->theatts = att_list_ptr;
			step->n_atts++;
		}

		if (step->traits.stat_proc_type != -1) {
			/* the statistical process duration and number in average only implemented for temporal statistical processess */
			if (grib_rec->spatial_proc >= 0) {
				AppendStatProcInfoToVarName(step,True);
				att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
				att_list_ptr->next = step->theatts;
				att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
				att_list_ptr->att_inq->name = NrmStringToQuark("type_of_spatial_processing");
				tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
				if (Grib2ReadCodeTable(step->ref_rec->table_source, 4, 
						       "4.15.table",grib_rec->spatial_proc,-1,ct) < NhlWARNING) {
					return;
				}
				tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
				if (ct->descrip) {
					*tmp_string = NrmStringToQuark(ct->descrip);
				}
				else {
					sprintf(buf,"%d",grib_rec->spatial_proc);
					*tmp_string = NrmStringToQuark(buf);
				}
				att_list_ptr->att_inq->thevalue = (NclMultiDValData)
					_NclCreateVal(NULL, NULL,
						      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes, 
						      PERMANENT, NULL, nclTypestringClass);
				step->theatts = att_list_ptr;
				step->n_atts++;
			}
			else {
				if (step->traits.stat_proc_type > 191 && step->traits.stat_proc_type != 255 && step->n_grids > 0) {
					int rix = 0;
					att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
					att_list_ptr->next = step->theatts;
					att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
					att_list_ptr->att_inq->name = NrmStringToQuark("number_in_average");
					tmp_int = NclMalloc(step->n_entries * sizeof(int));
					for (rix = 0; rix < step->n_entries; rix++) {
						tmp_int[rix] = step->thelist[rix].rec_inq->n_grids;
					}
					tmp_dimsizes = step->n_entries;
					att_list_ptr->att_inq->thevalue = (NclMultiDValData)
						_NclCreateVal(NULL, NULL,
							      Ncl_MultiDValData, 0, (void *) tmp_int, NULL, 1, &tmp_dimsizes, 
							      PERMANENT, NULL, nclTypeintClass);
					tmp_dimsizes = 1;
					step->theatts = att_list_ptr;
					step->n_atts++;
				}
					
				att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
				att_list_ptr->next = step->theatts;
				att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
				att_list_ptr->att_inq->name = NrmStringToQuark("statistical_process_duration");
				if (step->traits.stat_proc_type > 191 && step->traits.stat_proc_type != 255) {
					switch (step->traits.stat_proc_type) {
					
					case 192:
						SetTimePeriodString(&buf[strlen(buf)],step->p2,step->time_period_units);
						sprintf(&buf[strlen(buf)]," (beginning at reference time at intervals of 1 year)");
						break;
					case 193:
						if (step->p1 == 0) {
							sprintf(buf,"not applicable (intervals of ");
						}
						else {
							SetTimePeriodString(buf,step->p1,step->time_period_units);
							sprintf(&buf[strlen(buf)]," (at intervals of ");
						}
						SetTimePeriodString(&buf[strlen(buf)],step->p2,step->time_period_units);
						sprintf(&buf[strlen(buf)],")");
						break;
					case 194:
						sprintf(buf,"not applicable (intervals of ");
						SetTimePeriodString(&buf[strlen(buf)],step->p2,step->time_period_units);
						sprintf(&buf[strlen(buf)],")");
						break;
					case 195:
					case 196:
					case 197:
					case 198:
					case 200:
					case 201:
					case 202:
					case 203:
					case 204:
					case 205:
					case 206:
					case 207:
						SetTimePeriodString(buf,step->p2 - step->p1,step->time_period_units);
						if (step->p1 == 0) {
							sprintf(&buf[strlen(buf)]," (beginnng at forecast time)");
						}
						else {
							sprintf(&buf[strlen(buf)]," (beginning ");
							SetTimePeriodString(&buf[strlen(buf)],step->p1,step->time_period_units);
							sprintf(&buf[strlen(buf)]," after forecast time)");
						}
					}
				}
				else if (step->forecast_time_isatt || ! step->forecast_time_iszero) {
					if (step->forecast_time_iszero) {
						sprintf(buf,"initial time to forecast time");
					}
					else {
						SetTimePeriodString(buf,step->time_period,step->time_period_units);
						sprintf(&buf[strlen(buf)]," (ending at forecast time)");
					}
					if ((int)(therec->options[GRIB_TIME_PERIOD_SUFFIX_OPT].values) == 0)
						AppendStatProcInfoToVarName(step,True);
					else if (! step->forecast_time_iszero)
						AppendStatProcInfoToVarName(step,False);
					else 
						AppendStatProcInfoToVarName(step,True);

				}				
				else {
					sprintf(buf,"initial time to forecast time");
					AppendStatProcInfoToVarName(step,True);
				}
				tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
				*tmp_string = NrmStringToQuark(buf);
				att_list_ptr->att_inq->thevalue = (NclMultiDValData)
					_NclCreateVal(NULL, NULL,
						      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes, 
						      PERMANENT, NULL, nclTypestringClass);
				step->theatts = att_list_ptr;
				step->n_atts++;
			}
			att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
			att_list_ptr->next = step->theatts;
			att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
			att_list_ptr->att_inq->name = NrmStringToQuark("type_of_statistical_processing");
			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			if (Grib2ReadCodeTable(step->ref_rec->table_source, 4, 
					       "4.10.table",step->traits.stat_proc_type, -1,ct) < NhlWARNING) {
				return;
			}
			if (ct->descrip) {
				*tmp_string = NrmStringToQuark(ct->descrip);
			}
			else {
				sprintf(buf,"%d",step->traits.stat_proc_type);
				*tmp_string = NrmStringToQuark(buf);
			}
			att_list_ptr->att_inq->thevalue = (NclMultiDValData)
				_NclCreateVal(NULL, NULL,
					      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes, 
					      PERMANENT, NULL, nclTypestringClass);
			step->theatts = att_list_ptr;
			step->n_atts++;
		}
		if (step->prob_type > -1) {
			att_list_ptr = (Grib2AttInqRecList *) NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
			att_list_ptr->next = step->theatts;
			att_list_ptr->att_inq = (Grib2AttInqRec *) NclMalloc((unsigned)sizeof(Grib2AttInqRec));
			att_list_ptr->att_inq->name = NrmStringToQuark("probability_type");
			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			switch(step->prob_type) {
			case 0:
			case 4:
				*tmp_string = NrmStringToQuark("Probability of event below limit");
				break;
			case 1:
			case 3:
				*tmp_string = NrmStringToQuark("Probability of event above limit");
				break;
			case 2:
				*tmp_string = NrmStringToQuark("Probability of event between upper and lower limits (range includes lower limit but not the upper limit)");
				break;
			default:
				*tmp_string = NrmStringToQuark("Unknown");
			}
			att_list_ptr->att_inq->thevalue = (NclMultiDValData)
				_NclCreateVal(NULL, NULL,
					      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes, 
					      PERMANENT, NULL, nclTypestringClass);
			step->theatts = att_list_ptr;
			step->n_atts++;
			if (step->ensemble_isatt) { /* proxy for probability_isatt since this dimension is "shared"  */
				att_list_ptr = (Grib2AttInqRecList *) NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
				att_list_ptr->next = step->theatts;
				att_list_ptr->att_inq = (Grib2AttInqRec *) NclMalloc((unsigned)sizeof(Grib2AttInqRec));
				att_list_ptr->att_inq->name = NrmStringToQuark("probability_limit_units");
				tmp_dimsizes = 1;
				tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
				*tmp_string = step->var_info.units_q;
				att_list_ptr->att_inq->thevalue = (NclMultiDValData)
					_NclCreateVal(NULL, NULL,
						      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes, 
						      PERMANENT, NULL, nclTypestringClass);
				step->theatts = att_list_ptr;
				step->n_atts++;
				att_list_ptr = (Grib2AttInqRecList *) NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
				att_list_ptr->next = step->theatts;
				att_list_ptr->att_inq = (Grib2AttInqRec *) NclMalloc((unsigned)sizeof(Grib2AttInqRec));
				if (step->probability) {
					att_list_ptr->att_inq->name = NrmStringToQuark("probability_limit");
					att_list_ptr->att_inq->thevalue = (NclMultiDValData)step->probability;
					/* Don't want two references */
					step->probability = NULL;
					step->theatts = att_list_ptr;
					step->n_atts++;
				}
				else if (step->lower_probs && step->upper_probs) {
					att_list_ptr->att_inq->name = NrmStringToQuark("upper_probability_limit");
					att_list_ptr->att_inq->thevalue = (NclMultiDValData)step->upper_probs;
					step->upper_probs = NULL;
					step->theatts = att_list_ptr;
					step->n_atts++;
					att_list_ptr = (Grib2AttInqRecList *) NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
					att_list_ptr->next = step->theatts;
					att_list_ptr->att_inq = (Grib2AttInqRec *) NclMalloc((unsigned)sizeof(Grib2AttInqRec));
					att_list_ptr->att_inq->name = NrmStringToQuark("lower_probability_limit");
					att_list_ptr->att_inq->thevalue = (NclMultiDValData)step->lower_probs;
					step->lower_probs = NULL;
					step->theatts = att_list_ptr;
					step->n_atts++;
				}

			}
		}
			

		if ((step->levels_isatt) && (!step->levels_has_two) && (step->traits.first_level_type != 255)) {
			if (grib_rec->level0 != GRIB2_MISSING_LEVEL_VAL) {
				att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
				att_list_ptr->next = step->theatts;
				att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
				att_list_ptr->att_inq->name = NrmStringToQuark("level");
				att_list_ptr->att_inq->thevalue = (NclMultiDValData)step->levels;

				/* Don't want two references */
				step->levels= NULL;
				step->theatts = att_list_ptr;
				step->n_atts++;
			}

		} else if ((step->levels_isatt) && (step->levels_has_two)) {
			if (grib_rec->level0 != GRIB2_MISSING_LEVEL_VAL &&
			    grib_rec->level1 != GRIB2_MISSING_LEVEL_VAL) {
				tmp_level = (float*)NclMalloc(sizeof(float) * 2);
				att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
				att_list_ptr->next = step->theatts;
				att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
				att_list_ptr->att_inq->name = NrmStringToQuark("level");
				tmp_level[0] = *(float*)step->levels0->multidval.val;
				tmp_level[1] = *(float*)step->levels1->multidval.val;
				tmp_dimsizes = 2;
				att_list_ptr->att_inq->thevalue = (NclMultiDValData)_NclCreateVal(
					NULL, NULL, Ncl_MultiDValData, 0, (void *) tmp_level, NULL, 1, 
					&tmp_dimsizes, PERMANENT, NULL, nclTypefloatClass);
				tmp_dimsizes = 1;

				/* Don't want two references */
				_NclDestroyObj((NclObj)step->levels0);
				_NclDestroyObj((NclObj)step->levels1);
				step->levels0= NULL;
				step->levels1= NULL;
				step->theatts = att_list_ptr;
				step->n_atts++;
			}

		}

		if (step->traits.first_level_type != 255) {
			att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
			att_list_ptr->next = step->theatts;
			att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
			if (!step->levels_has_two ||
			    step->traits.first_level_type == step->traits.second_level_type) {
				att_list_ptr->att_inq->name = NrmStringToQuark("level_type");
			}
			else if (step->traits.second_level_type != 255) {
				att_list_ptr->att_inq->name = NrmStringToQuark("first_level_type");
			}
			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			Grib2ReadCodeTable(step->ref_rec->table_source, 4, 
					   "4.5.table",step->traits.first_level_type,-1,ct);
			if (ct->descrip) {
				if (ct->units && strcmp("none",ct->units)) {
					sprintf(buf,"%s (%s)",ct->descrip,ct->units);
					*tmp_string = NrmStringToQuark(buf);
				}
				else {
					*tmp_string = NrmStringToQuark(ct->descrip);
				}
			}
			else {
				sprintf(buf,"%d",step->traits.first_level_type);
				*tmp_string = NrmStringToQuark(buf);
			}
			att_list_ptr->att_inq->thevalue = (NclMultiDValData)
				_NclCreateVal(NULL, NULL,
					      Ncl_MultiDValData, 0, (void*)tmp_string, NULL, 1, &tmp_dimsizes,
					      PERMANENT, NULL, nclTypestringClass);
				
			step->theatts = att_list_ptr;
			step->n_atts++;
		}
		if (step->levels_has_two && step->traits.second_level_type != 255 &&
		    step->traits.second_level_type != step->traits.first_level_type) {
			att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
			att_list_ptr->next = step->theatts;
			att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
			att_list_ptr->att_inq->name = NrmStringToQuark("second_level_type");
			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			Grib2ReadCodeTable(step->ref_rec->table_source, 4, 
					   "4.5.table",step->traits.second_level_type,-1,ct);
			if (ct->descrip) {
				if (ct->units && strcmp("none",ct->units)) {
					sprintf(buf,"%s (%s)",ct->descrip,ct->units);
					*tmp_string = NrmStringToQuark(buf);
				}
				else {
					*tmp_string = NrmStringToQuark(ct->descrip);
				}
			}
			else {
				sprintf(buf,"%d",step->traits.second_level_type);
				*tmp_string = NrmStringToQuark(buf);
			}
			att_list_ptr->att_inq->thevalue = (NclMultiDValData)
				_NclCreateVal(NULL, NULL,
					      Ncl_MultiDValData, 0, (void*)tmp_string, NULL, 1, &tmp_dimsizes,
					      PERMANENT, NULL, nclTypestringClass);
				
			step->theatts = att_list_ptr;
			step->n_atts++;
		}
		
	

/* param characterization array */
		att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
		att_list_ptr->next = step->theatts;
		att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
		att_list_ptr->att_inq->name = NrmStringToQuark("parameter_template_discipline_category_number");
		tmp_int = (int*)NclMalloc(4 * sizeof(int));
		tmp_int[0] = step->traits.pds_template;
		tmp_int[1] = step->traits.discipline;
		tmp_int[2] = step->traits.param_cat;
		tmp_int[3] = step->traits.param_number;
		tmp_dimsizes = 4;
		att_list_ptr->att_inq->thevalue = (NclMultiDValData)
			_NclCreateVal(NULL, NULL,
				      Ncl_MultiDValData, 0, (void *) tmp_int, NULL, 1, &tmp_dimsizes, 
				      PERMANENT, NULL, nclTypeintClass);
		tmp_dimsizes = 1;
		step->theatts = att_list_ptr;
		step->n_atts++;

/* param characterization string */

		att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
		att_list_ptr->next = step->theatts;
		att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
		att_list_ptr->att_inq->name = NrmStringToQuark("parameter_discipline_and_category");
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		if (Grib2ReadCodeTable(step->ref_rec->table_source, 0, 
				       "0.0.table",step->traits.discipline,-1,ct) < NhlWARNING) {
			return;
		}
		if (ct->descrip) {
			sprintf(buf,"%s, ",ct->descrip);
			
		}
		else {
			sprintf(buf,"%d, ",step->traits.discipline);
		}
		if (Grib2ReadCodeTable(step->ref_rec->table_source, 4, 
				       "4.2.table",step->traits.discipline,step->traits.param_cat,ct) < NhlWARNING) {
			return;
		}
		if (ct->descrip) {
			sprintf(&(buf[strlen(buf)]),"%s",ct->descrip);
			
		}
		else {
			sprintf(&(buf[strlen(buf)]),"%d",step->traits.param_cat);
		}
		*tmp_string = NrmStringToQuark(buf);

		att_list_ptr->att_inq->thevalue = (NclMultiDValData)
			_NclCreateVal(NULL, NULL,
				      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes, 
				      PERMANENT, NULL, nclTypestringClass);
		step->theatts = att_list_ptr;
		step->n_atts++;


/* grid type */

		att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
		att_list_ptr->next = step->theatts;
		att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
		att_list_ptr->att_inq->name = NrmStringToQuark("grid_type");
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		if (Grib2ReadCodeTable(step->ref_rec->table_source, 3, 
				       "3.1.table",grib_rec->grid_number,-1,ct) < NhlWARNING) {
			return;
		}
		if (step->gds->is_thinned_grid) {
			int method = ((NrmQuark) therec->options[GRIB_THINNED_GRID_INTERPOLATION_OPT].values) ==
				NrmStringToQuark("cubic") ? 1 : 0;
			if (ct->descrip) {
				sprintf(buf,"%s (quasi-regular grid expanded by %s interpolation)",
					ct->descrip, (method && ! step->has_bmap) ? "cubic" : "linear");
				*tmp_string = NrmStringToQuark(buf);
			}
			else {
				sprintf(buf,"%d (quasi-regular grid expanded by %s interpolation)",
					grib_rec->grid_number, (method && ! step->has_bmap) ? "cubic" : "linear");
				*tmp_string = NrmStringToQuark(buf);
			}
		}
		else if (ct->descrip) {
			*tmp_string = NrmStringToQuark(ct->descrip);
		}
		else {
			sprintf(buf,"%d",grib_rec->grid_number);
			*tmp_string = NrmStringToQuark(buf);
		}
		att_list_ptr->att_inq->thevalue = (NclMultiDValData)
			_NclCreateVal(NULL, NULL,
				      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes, 
				      PERMANENT, NULL, nclTypestringClass);
		step->theatts = att_list_ptr;
		step->n_atts++;


		/*
		 * if 2D coordinates, this adds the CF compliant attribute "coordinates", to point to the
		 * auxiliary coordinate variables
		 */

		if (step->aux_coords[0] != NrmNULLQUARK) {
			char buffer[80];

			att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
			att_list_ptr->next = step->theatts;
			att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
			att_list_ptr->att_inq->name = NrmStringToQuark("coordinates");
			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			sprintf(buffer,"%s %s",NrmQuarkToString(step->aux_coords[0]),
				NrmQuarkToString(step->aux_coords[1]));
			*tmp_string = NrmStringToQuark(buffer);		
			att_list_ptr->att_inq->thevalue = (NclMultiDValData)
				_NclCreateVal(NULL, NULL,
					      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes,
					      PERMANENT, NULL, nclTypestringClass);
			step->theatts = att_list_ptr;
			step->n_atts++;
		}

		att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
		att_list_ptr->next = step->theatts;
		att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
		att_list_ptr->att_inq->name = NrmStringToQuark(NCL_MISSING_VALUE_ATT);
		if (step->var_info.data_type == NCL_int) {
			tmp_fill = NclMalloc(sizeof(int));
			if (step->has_own_missing)
				*(int *) tmp_fill = step->missing.intval;
			else
				*(int *) tmp_fill = G2_DEFAULT_MISSING_INT;
			att_list_ptr->att_inq->thevalue = (NclMultiDValData)
				_NclCreateVal(NULL, NULL,
					      Ncl_MultiDValData, 0, (void *) tmp_fill, NULL, 1, &tmp_dimsizes,
					      PERMANENT, NULL, nclTypeintClass);
		} else {
			tmp_fill = NclMalloc(sizeof(float));
			if (step->has_own_missing)
				*(float *) tmp_fill = step->missing.floatval;
			else
				*(float *) tmp_fill = G2_DEFAULT_MISSING_FLOAT;
			att_list_ptr->att_inq->thevalue = (NclMultiDValData)
				_NclCreateVal(NULL, NULL,
					      Ncl_MultiDValData, 0, (void *) tmp_fill, NULL, 1, &tmp_dimsizes, 
					      PERMANENT, NULL, nclTypefloatClass);
		}

		step->theatts = att_list_ptr;
		step->n_atts++;
		
		/* units */
		att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
		att_list_ptr->next = step->theatts;
		att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
		att_list_ptr->att_inq->name = NrmStringToQuark("units");
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		if (step->prob_type <=  -1)
			*tmp_string = step->var_info.units_q;		
		else
			*tmp_string = NrmStringToQuark("percent");
		att_list_ptr->att_inq->thevalue = (NclMultiDValData)
			_NclCreateVal(NULL, NULL, 
				      Ncl_MultiDValData, 0, (void*)tmp_string, NULL, 1, &tmp_dimsizes, 
				      PERMANENT, NULL, nclTypestringClass);
		step->theatts = att_list_ptr;
		step->n_atts++;

		/* long name */
		att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
		att_list_ptr->next = step->theatts;
		att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
		att_list_ptr->att_inq->name = NrmStringToQuark("long_name");
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = step->var_info.long_name_q;		
		att_list_ptr->att_inq->thevalue = (NclMultiDValData)
			_NclCreateVal(NULL, NULL, 
				      Ncl_MultiDValData, 0, (void*)tmp_string, NULL, 1, &tmp_dimsizes, 
				      PERMANENT, NULL, nclTypestringClass);
		step->theatts = att_list_ptr;
		step->n_atts++;

		/* production status */
		att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
		att_list_ptr->next = step->theatts;
		att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
		att_list_ptr->att_inq->name = NrmStringToQuark("production_status");

		if (Grib2ReadCodeTable(grib_rec->table_source, 1, "1.3.table", grib_rec->traits.prod_status,-1,ct) < NhlWARNING) {
			return;
		}
		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		if (ct->descrip) {
			*tmp_string = NrmStringToQuark(ct->descrip);
		}
		else {
			sprintf(buf,"%d",grib_rec->traits.prod_status);
			*tmp_string = NrmStringToQuark(buf);
		}
		att_list_ptr->att_inq->thevalue = (NclMultiDValData)
			_NclCreateVal(NULL, NULL,
				      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes,
				      PERMANENT, NULL, nclTypestringClass);
		step->theatts = att_list_ptr;
		step->n_atts++;


		/* center */
		att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
		att_list_ptr->next = step->theatts;
		att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
		att_list_ptr->att_inq->name = NrmStringToQuark("center");

		tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*tmp_string = grib_rec->qcenter_name;
		att_list_ptr->att_inq->thevalue = (NclMultiDValData)
			_NclCreateVal(NULL, NULL,
				      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes,
				      PERMANENT, NULL, nclTypestringClass);
		step->theatts = att_list_ptr;
		step->n_atts++;
	

		step = step->next;

    }
    Grib2FreeCodeTableRec(ct);
	

    return;
}



static void _Grib2AddInternalVar
#if	NhlNeedProto
(Grib2FileRecord *therec,NclQuark name_q,int *dim_numbers, NclMultiDValData tmp_md, Grib2AttInqRecList *attlist,int natts)
#else
(therec,dim_name_q,tmp_md)
Grib2FileRecord *therec;
NclQuark dim_name_q;
NclMultiDValData *tmp_md;
Grib2AttInqRecList *attlist;
int natts;
#endif
{
	Grib2InternalVarList *vstep; 
	Grib2InternalVarRec *tmp;
	int i;

	tmp = (Grib2InternalVarRec*)NclMalloc(sizeof(Grib2InternalVarRec));
	tmp->var_info.data_type = tmp_md->multidval.data_type;
	tmp->var_info.var_name_quark = name_q;
	tmp->var_info.num_dimensions = tmp_md->multidval.n_dims;
	for (i = 0; i < tmp_md->multidval.n_dims; i++) {
/*
		tmp->var_info.dim_sizes[i] = tmp_md->multidval.dim_sizes[i];
*/
		tmp->var_info.file_dim_num[i] = dim_numbers[i];
	}

	tmp->value = tmp_md;
	vstep = (Grib2InternalVarList*)NclMalloc(sizeof(Grib2InternalVarList));
	vstep->next = therec->internal_var_list;
	vstep->int_var = tmp;
	vstep->int_var->n_atts = natts;
	vstep->int_var->theatts = attlist;
	therec->internal_var_list = vstep;
	therec->n_internal_vars++;

	return;
}


static void g2SetInitialTimeCoordinates
#if 	NhlNeedProto
(Grib2FileRecord *therec)
#else
(therec)
Grib2FileRecord *therec;
#endif
{
	Grib2DimInqRecList *step;
	Grib2InternalVarList *vstep,*nvstep;
	int i,j,k;

	step = therec->it_dims;
	for (i = 0; i < therec->n_it_dims; i++) {
		char buffer[128];
		char *cp;
		NrmQuark dimq,newdimq;
			

		dimq = step->dim_inq->dim_name;
		vstep = therec->internal_var_list;
		for (j = 0; j < therec->n_internal_vars; j++) {
			if (vstep->int_var->var_info.var_name_quark == dimq) {
				break;
			}
			vstep = vstep->next;
		}
		if (j == therec->n_internal_vars) {
			printf("var %s not found\n",NrmQuarkToString(dimq));
			continue;
		}
		cp = strrchr(NrmQuarkToString(dimq),'_');
		if (cp && ! strcmp(cp,"_hours")) {
			if ((NrmQuark)therec->options[GRIB_INITIAL_TIME_COORDINATE_TYPE_OPT].values == NrmStringToQuark("numeric"))
				continue;
			sprintf(buffer,"%s",NrmQuarkToString(dimq));
			cp = strrchr(buffer,'_');
			*cp = '\0';
			newdimq = NrmStringToQuark(buffer);
			nvstep = therec->internal_var_list;
			for (k = 0; k < therec->n_internal_vars; k++) {
				if (nvstep->int_var->var_info.var_name_quark == newdimq) {
					break;
				}
				nvstep = nvstep->next;
			}
			if (k == therec->n_internal_vars) {
				printf("var %s not found\n",NrmQuarkToString(newdimq));
				continue;
			}
			step->dim_inq->dim_name = newdimq;
		}
		else {
			if ((NrmQuark)therec->options[GRIB_INITIAL_TIME_COORDINATE_TYPE_OPT].values == NrmStringToQuark("string"))
				continue;
			sprintf(buffer,"%s_hours",NrmQuarkToString(dimq));
			newdimq = NrmStringToQuark(buffer);
			nvstep = therec->internal_var_list;
			for (k = 0; k < therec->n_internal_vars; k++) {
				if (nvstep->int_var->var_info.var_name_quark == newdimq) {
					break;
				}
				nvstep = nvstep->next;
			}
			if (k == therec->n_internal_vars) {
				printf("var %s not found\n",NrmQuarkToString(newdimq));
				continue;
			}
			step->dim_inq->dim_name = newdimq;
		}
		step = step->next;
	}

	return;
}

static double  *_g2DateStringsToEncodedDoubles
#if 	NhlNeedProto
(
NrmQuark *vals,
ng_size_t dimsize
	)
#else
(vals,dimsize)
NrmQuark *vals;
ng_size_t dimsize;
#endif
{
	int i;
	char *str;
	double *ddates;

	ddates = NclMalloc(dimsize * sizeof(double));
	if (!ddates) {
		NHLPERROR((NhlFATAL,ENOMEM,NULL));
		return NULL;
	}

	for (i = 0; i < dimsize; i++) {
		int y,m,d,h;
		float min;
		str = NrmQuarkToString(vals[i]);
		sscanf(str,"%2d/%2d/%4d (%2d:%2f)",&m,&d,&y,&h,&min);
		ddates[i] = y * 1e6 + m * 1e4 + d * 1e2 + h + min / 60.;
	}
				       
	return ddates;
}

static double  *_g2DateStringsToHours
#if 	NhlNeedProto
(
NrmQuark *vals,
ng_size_t dimsize
	)
#else
(vals,dimsize)
NrmQuark *vals;
ng_size_t dimsize;
#endif
{
	int i;
	char *str;
	double *dhours;

	dhours = NclMalloc(dimsize * sizeof(double));
	if (!dhours) {
		NHLPERROR((NhlFATAL,ENOMEM,NULL));
		return NULL;
	}

	for (i = 0; i < dimsize; i++) {
		int y,m,d,h,min;
		long jddiff;
		str = NrmQuarkToString(vals[i]);
		sscanf(str,"%2d/%2d/%4d (%2d:%2d)",&m,&d,&y,&h,&min);
		jddiff = HeisDayDiff(1,1,1800,d,m,y);
		dhours[i] = jddiff * 24 + h + min / 60.0;
	}
				       
	return dhours;
}



static void _g2CreateSupplementaryTimeVariables
#if 	NhlNeedProto
(Grib2FileRecord *therec)
#else
(therec)
Grib2FileRecord *therec;
#endif
{
	Grib2DimInqRecList *step;
	Grib2InternalVarList *vstep;
	int i,j;

	step = therec->it_dims;
	for (i = 0; i < therec->n_it_dims; i++) {
		ng_size_t dimsize;
		NrmQuark *vals;
		double *dates;
		double *hours;
		char buffer[128];
		NrmQuark  *qstr;
		Grib2AttInqRecList *tmp_att_list_ptr= NULL;
		NclMultiDValData mdval;
			

		NrmQuark dimq = step->dim_inq->dim_name;
		vstep = therec->internal_var_list;
		for (j = 0; j < therec->n_internal_vars; j++) {
			if (vstep->int_var->var_info.var_name_quark == dimq) {
				break;
			}
			vstep = vstep->next;
		}
		if (j == therec->n_internal_vars) {
			printf("var %s no found\n",NrmQuarkToString(dimq));
			continue;
		}
		dimsize = vstep->int_var->value->multidval.totalelements;
		vals = (NrmQuark *)vstep->int_var->value->multidval.val;
		
		dates = _g2DateStringsToEncodedDoubles(vals,dimsize);
		hours = _g2DateStringsToHours(vals,dimsize);
		if (! (dates && hours) )
			continue;
		mdval = _NclCreateVal(NULL,NULL,Ncl_MultiDValData,0,(void*)dates,
			              NULL,1,&dimsize,TEMPORARY,NULL,nclTypedoubleClass),
		sprintf(buffer,"%s_encoded",NrmQuarkToString(dimq));
		qstr = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*qstr = NrmStringToQuark
			("yyyymmddhh.hh_frac");
		Grib2PushAtt(&tmp_att_list_ptr,"units",qstr,1,nclTypestringClass); 
		qstr = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*qstr =  NrmStringToQuark("initial time encoded as double");
		Grib2PushAtt(&tmp_att_list_ptr,"long_name",qstr,1,nclTypestringClass); 
		_Grib2AddInternalVar(therec,NrmStringToQuark(buffer),
				    &step->dim_inq->dim_number,mdval,tmp_att_list_ptr,2);
		tmp_att_list_ptr = NULL;

		mdval = _NclCreateVal(NULL,NULL,Ncl_MultiDValData,0,(void*)hours,
			              NULL,1,&dimsize,TEMPORARY,NULL,nclTypedoubleClass),
		sprintf(buffer,"%s_hours",NrmQuarkToString(dimq));
		qstr = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*qstr = NrmStringToQuark
			("hours since 1800-01-01 00:00");
		Grib2PushAtt(&tmp_att_list_ptr,"units",qstr,1,nclTypestringClass); 
		qstr = (NclQuark*)NclMalloc(sizeof(NclQuark));
		*qstr =  NrmStringToQuark("initial time");
		Grib2PushAtt(&tmp_att_list_ptr,"long_name",qstr,1,nclTypestringClass); 
		_Grib2AddInternalVar(therec,NrmStringToQuark(buffer),
				    &step->dim_inq->dim_number,mdval,tmp_att_list_ptr,2);
		tmp_att_list_ptr = NULL;
		step = step->next;
	}

	g2SetInitialTimeCoordinates(therec);
	return;
}
static void _g2AddGenProcAtt(Grib2ParamList *step)
{
	Grib2RecordInqRec *grib_rec = NULL;
	Grib2AttInqRecList *att_list_ptr= NULL;
	NclQuark *tmp_string = NULL;
	g2codeTable *ct;
	ng_size_t tmp_dimsizes = 1;
	char buf[512];

	ct = (g2codeTable *) NclMalloc(1 * sizeof(g2codeTable));
	if (ct == NULL) {
		NhlPError(NhlFATAL, NhlEUNKNOWN,
			  " Unable to allocate code table data, cannot continue.");
		return;
	}
	memset(ct,0,sizeof(g2codeTable));

	/* type of generating processs */
	att_list_ptr = (Grib2AttInqRecList*)NclMalloc((unsigned)sizeof(Grib2AttInqRecList));
	att_list_ptr->next = step->theatts;
	att_list_ptr->att_inq = (Grib2AttInqRec*)NclMalloc((unsigned)sizeof(Grib2AttInqRec));
	att_list_ptr->att_inq->name = NrmStringToQuark("generating_process_type");

	{
		int error_id;
		NhlErrorTypes err_level;
		error_id = NhlGetErrorObjectId();
		NhlVAGetValues(error_id, NhlNerrLevel,&err_level,NULL);
		NhlVASetValues(error_id, NhlNerrLevel,NhlFATAL,NULL);
		if (Grib2ReadCodeTable(step->ref_rec->table_source, 4, "4.3.table", 
				       step->traits.gen_process_type,-1,ct) < NhlWARNING) {
			return;
		}
		NhlVASetValues(error_id, NhlNerrLevel,err_level,NULL);
	}
		
	tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
	if (ct->descrip) {
		*tmp_string = NrmStringToQuark(ct->descrip);
	}
	else {
		sprintf(buf,"%d",step->traits.gen_process_type);
		*tmp_string = NrmStringToQuark(buf);
	}
	att_list_ptr->att_inq->thevalue = (NclMultiDValData)
		_NclCreateVal(NULL, NULL,
			      Ncl_MultiDValData, 0, (void *) tmp_string, NULL, 1, &tmp_dimsizes,
			      PERMANENT, NULL, nclTypestringClass);
	step->theatts = att_list_ptr;
	step->n_atts++;
	Grib2FreeCodeTableRec(ct);

}

static void _g2MakeVarnamesUnique
#if 	NhlNeedProto
(Grib2FileRecord *therec)
#else
(therec)
Grib2FileRecord *therec;
#endif
{
	Grib2ParamList *step = NULL;
	char buffer[80];

	for (step = therec->var_list; step != NULL; step = step->next) {
		NclQuark qvname = step->var_info.var_name_quark;
		int nfound = 0;
		Grib2ParamList *tstep = step->next;
		int need_gen_proc_att = 0;
		
		for (tstep = step->next; tstep != NULL; tstep = tstep->next) {
			int i;

			if (tstep->var_info.var_name_quark != qvname)
				continue;
			nfound++;
			sprintf(buffer,"%s_%d",NrmQuarkToString(qvname),nfound);
			tstep->var_info.var_name_quark = NrmStringToQuark(buffer);
			if (tstep->traits.gen_process_type != step->traits.gen_process_type || need_gen_proc_att) {
				need_gen_proc_att = 1;
				_g2AddGenProcAtt(tstep);
			}

			for (i = 0; i < tstep->n_entries; i++) {
				Grib2RecordInqRec *rec = tstep->thelist[i].rec_inq;
				if (! rec) 
					continue;
				rec->var_name_q = tstep->var_info.var_name_quark;
			}
		}
		if (need_gen_proc_att) {
			_g2AddGenProcAtt(step);
		}

	}
}

static void _g2PrintRecordInfo
#if 	NhlNeedProto
(Grib2FileRecord *therec)
#else
(therec)
Grib2FileRecord *therec;
#endif
{
	Grib2ParamList *step = NULL;
	Grib2RecordInqRecList *tstep;
	int i,j;

	for (step = therec->var_list; step != NULL; step = step->next) {
		NclQuark qvname = step->var_info.var_name_quark;
		int cur_ix[5] = { 0,0,0,0,0};
		int n_dims = step->var_info.doff == 1 ? 
			step->var_info.num_dimensions - 2 : step->var_info.num_dimensions - 3;
		
/*
		for (i = 0; i < step->var_info.num_dimensions; i++) {
			printf("%d%s",step->var_info.dim_sizes[i],
			       i == step->var_info.num_dimensions - 1 ? ")\n" : ",");
		}
*/
				
		if (n_dims == 0) {
			printf("%s",NrmQuarkToString(qvname));
			tstep = &step->thelist[0];
			printf("%s \t",step->var_info.doff == 1 ? "(:,:)" : "(:,:,:)");
			if (! tstep->rec_inq)
				printf("missing record\n");
			else if (tstep->rec_inq->field_num > 0)
				printf("%d, %d\n",tstep->rec_inq->rec_num,tstep->rec_inq->field_num);
			else
				printf("%d\n",tstep->rec_inq->rec_num);

			continue;
		}
		for (i = 0; i < step->n_entries; i++) {
			printf("%s",NrmQuarkToString(qvname));
			printf("(");
			for (j = 0; j < n_dims; j++) {
				printf("%d,",cur_ix[j]);
			}
			printf("%s) \t",step->var_info.doff == 1 ? ":,:" : ":,:,:");
			cur_ix[n_dims-1]++;
			tstep = &step->thelist[i];
			if (! tstep->rec_inq)
				printf("missing record\n");
			else if (tstep->rec_inq->field_num > 0)
				printf("%d, %d\n",tstep->rec_inq->rec_num,tstep->rec_inq->field_num);
			else
				printf("%d\n",tstep->rec_inq->rec_num);
			for (j = n_dims -1; j > 0; j--) {
				if (cur_ix[j] == step->var_info.dim_sizes[j]) {
					cur_ix[j-1]++;
					cur_ix[j] = 0;
				}
			}
		}
	}
}

static G2_GDS *Grib2DupGDS
#if	NhlNeedProto
(G2_GDS *gds)
#else
(gds)
G2_GDS *gds;
#endif
{
	G2_GDS *new_gds;
	if (! gds) {
		return NULL;
	}
	new_gds = (G2_GDS *) NclMalloc(sizeof(G2_GDS));
	memcpy(new_gds,gds,sizeof(G2_GDS));
	
	if (gds->grid_def_name) {
		new_gds->grid_def_name = NclMalloc(strlen(gds->grid_def_name)+1);
		strcpy(new_gds->grid_def_name,gds->grid_def_name);
	}
	if (gds->interp_opt_name) {
		new_gds->interp_opt_name = NclMalloc(strlen(gds->interp_opt_name)+1);
		strcpy(new_gds->interp_opt_name,gds->interp_opt_name);
	}
	if (gds->grid_list_num_oct_opt != NULL) {
		new_gds->grid_list_num_oct_opt = NclMalloc(gds->grid_list_num_oct_num * sizeof(int));
		memcpy(new_gds->grid_list_num_oct_opt,gds->grid_list_num_oct_opt,
		       gds->grid_list_num_oct_num * sizeof(int));
	}
	if (gds->grid_template != NULL) {
		new_gds->grid_template = NclMalloc(gds->len_grid_template * sizeof(int));
		memcpy(new_gds->grid_template,gds->grid_template,
		       gds->len_grid_template * sizeof(int));
	}
	
	return (new_gds);
}

static void Grib2FreeGDS
#if	NhlNeedProto
(G2_GDS *gds)
#else
(gds)
G2_GDS *gds;
#endif
{
	if (! gds) {
		return;
	}
	if (gds->grid_def_name)
		NclFree(gds->grid_def_name);
	if (gds->interp_opt_name)
		NclFree(gds->interp_opt_name);

	if (gds->grid_list_num_oct_opt != NULL)
		NclFree(gds->grid_list_num_oct_opt);

	if (gds->grid_template != NULL) {
		NclFree(gds->grid_template);
	}

#if 0
	if (gds->res_comp != NULL)
		NclFree(gds->res_comp);

	if (gds->scan_mode != NULL)
		NclFree(gds->scan_mode);
#endif

	NclFree(gds);
}

static void _Grib2FreeGrib2InqRec
#if	NhlNeedProto
(Grib2RecordInqRec *grib_rec)
#else
(grib_rec)
Grib2RecordInqRec *grib_rec;
#endif
{

    if (grib_rec->table_source) {
        NclFree(grib_rec->table_source);
    }	    


    /* PDS record */

    /* GDS record */
    if (grib_rec->gds != NULL) {
	    Grib2FreeGDS(grib_rec->gds);
    }

    /* if var_name_q is not set then it's a missing record -- shared for all missing records */
    if (grib_rec->the_dat != NULL) {
	    if (grib_rec->var_name_q > NrmNULLQUARK)
		    _NclDestroyObj((NclObj) grib_rec->the_dat);
    }

    NclFree(grib_rec);

    return;
}



static void _Grib2FreeParamRec
#if	NhlNeedProto
(Grib2ParamList *vstep)
#else
(vstep)
Grib2ParamList *vstep;
#endif
{
	int i;
	Grib2AttInqRecList *astep= NULL,*tmp =NULL;
	if(vstep != NULL){
		if(vstep->it_vals != NULL) {
			NclFree(vstep->it_vals);
		}

		for (i = 0; i< vstep->n_entries; i++) {
			if (vstep->thelist[i].rec_inq != NULL) {
				_Grib2FreeGrib2InqRec(vstep->thelist[i].rec_inq);
			}
		}
		if (vstep->gds != NULL) {
			Grib2FreeGDS(vstep->gds);
		}			
 		if (vstep->ensemble != NULL) {
			_NclDestroyObj((NclObj)vstep->ensemble);
		}
 		if (vstep->ens_indexes != NULL) {
			_NclDestroyObj((NclObj)vstep->ens_indexes);
		}
 		if (vstep->probability != NULL) {
			_NclDestroyObj((NclObj)vstep->probability);
		}
 		if (vstep->lower_probs != NULL) {
			_NclDestroyObj((NclObj)vstep->lower_probs);
		}
 		if (vstep->upper_probs != NULL) {
			_NclDestroyObj((NclObj)vstep->upper_probs);
		}

		if (vstep->forecast_time != NULL) {
			_NclDestroyObj((NclObj)vstep->forecast_time);
		}

		if (vstep->yymmddhh!= NULL) {
			_NclDestroyObj((NclObj)vstep->yymmddhh);
		}

		if (vstep->levels!= NULL) {
			_NclDestroyObj((NclObj)vstep->levels);
		}

		if (vstep->levels0!= NULL) {
			_NclDestroyObj((NclObj)vstep->levels0);
		}

		if (vstep->levels1!= NULL) {
			_NclDestroyObj((NclObj)vstep->levels1);
		}

		astep = vstep->theatts;
		for (i = 0; i < vstep->n_atts; i++) {
			_NclDestroyObj((NclObj)astep->att_inq->thevalue);
			NclFree(astep->att_inq);	
			tmp = astep;
			astep = astep->next;
			NclFree(tmp);
		}

		NclFree(vstep->thelist);
		NclFree(vstep);
	}

	return;
}


static Grib2ParamList *_g2NewListNode
# if    NhlNeedProto
(Grib2RecordInqRec *grib_rec)
# else
(grib_rec)
    Grib2RecordInqRec* grib_rec;
# endif
{
	Grib2ParamList *tmp = NULL;
	Grib2RecordInqRecList *list = NULL;

	tmp = (Grib2ParamList *)NclMalloc((unsigned) sizeof(Grib2ParamList));
	tmp->next = NULL;
	list = (Grib2RecordInqRecList *) NclMalloc((unsigned) sizeof(Grib2RecordInqRecList));
	list->rec_inq = grib_rec;
	list->next = NULL;
	tmp->thelist = list;
	tmp->traits = grib_rec->traits;
	tmp->gds = Grib2DupGDS(grib_rec->gds);
	tmp->var_info.var_name_quark = grib_rec->var_name_q;
	tmp->var_info.data_type = g2GribMapToNcl((void *) &(grib_rec->int_or_float));
	tmp->param_number = grib_rec->traits.param_number;
	tmp->grid_number = grib_rec->grid_number;
	tmp->level_indicator = grib_rec->level_indicator;
	tmp->has_bmap = 0;
	tmp->has_own_missing = grib_rec->has_own_missing;
	tmp->missing = grib_rec->missing;
	tmp->gds->scan_mode_offset = -1;
	tmp->n_entries = 1;
	tmp->minimum_it = grib_rec->initial_time;
	tmp->forecast_time_iszero = (grib_rec->forecast_time == 0);
	tmp->time_period = grib_rec->time_period;
	tmp->forecast_time_units = grib_rec->forecast_time_units;
	tmp->time_period_units = grib_rec->time_period_units;
	tmp->variable_time_unit = False;
	tmp->prob_type = grib_rec->ens.prob_type;
	tmp->levels = NULL;
	tmp->levels0 = NULL;
	tmp->levels1 = NULL;
	tmp->levels_has_two = 0;
	tmp->yymmddhh = NULL;
	tmp->forecast_time = NULL;
	tmp->ensemble = NULL;
	tmp->ens_indexes = NULL;
	tmp->probability = NULL;
	tmp->lower_probs = NULL;
	tmp->upper_probs = NULL;
	tmp->n_atts = 0;

	return tmp;
}

static void _g2InsertNodeAfter
#if NhlNeedProto
(Grib2ParamList *node, Grib2ParamList *new_node)
#else
(node, new_node)
Grib2ParamList *node; 
Grib2ParamList *new_node;
#endif
{
	Grib2ParamList * tmp;

	tmp = node->next;
	node->next = new_node;
	new_node->next = tmp;

	return;
}

static Grib2RecordInqRec* _g2MakeMissingRec
#if NhlNeedProto
(void)
#else
()
#endif
{
	Grib2RecordInqRec* grib_rec = (Grib2RecordInqRec*)NclMalloc(sizeof(Grib2RecordInqRec));

        memset(grib_rec,0,sizeof(Grib2RecordInqRec));
	grib_rec->var_name_q = -1;
	grib_rec->grid_number = -1;
	grib_rec->forecast_time = -1;
	grib_rec->time_offset = -1;
	grib_rec->time_period = -1;
	grib_rec->level0 = GRIB2_MISSING_LEVEL_VAL;
	grib_rec->level1 = GRIB2_MISSING_LEVEL_VAL;
	grib_rec->forecast_time_units = 0;
	grib_rec->time_period_units = 0;
	grib_rec->gds = NULL;
	grib_rec->the_dat = NULL;
	grib_rec->interp_method = 0;
	return(grib_rec);
	
}

static void _g2AddRecordToNode
#if NhlNeedProto
(Grib2ParamList *node, Grib2RecordInqRec* grib_rec)
#else
(node, grib_rec)
Grib2ParamList *node;
Grib2RecordInqRec* grib_rec;
#endif
{
    Grib2RecordInqRecList * grib_rec_list = (Grib2RecordInqRecList *)
	    NclMalloc((unsigned) sizeof(Grib2RecordInqRecList));

    if ((grib_rec->initial_time.year < node->minimum_it.year)
            || ((grib_rec->initial_time.year == node->minimum_it.year)	
            && (grib_rec->initial_time.days_from_jan1 < node->minimum_it.days_from_jan1))
            || ((grib_rec->initial_time.year == node->minimum_it.year)
            && (grib_rec->initial_time.days_from_jan1 == node->minimum_it.days_from_jan1)
            && (grib_rec->initial_time.minute_of_day < node->minimum_it.minute_of_day))) {
		node->minimum_it = grib_rec->initial_time;
	}

    if (node->forecast_time_units != grib_rec->forecast_time_units) {
	    _g2SetCommonTimeUnit(node,grib_rec);
    }

    if (node->traits.stat_proc_type != -1) {
	    if (node->forecast_time_iszero && grib_rec->forecast_time != 0) {
		    node->forecast_time_iszero = 0;
		    node->time_period = grib_rec->time_period;
	    }
    }
    grib_rec_list->rec_inq = grib_rec;
    grib_rec_list->next = node->thelist;
    node->thelist = grib_rec_list;
    node->n_entries++;

    return;
}
#if 0
static int _g2IsDef
#if NhlNeedProto
(Grib2FileRecord *therec, int param_num)
#else
(therec, param_num)
Grib2FileRecord *therec;
int param_num;
#endif
{
    Grib2ParamList *step;

    if (therec != NULL) {
        step = therec->var_list;
        while(step != NULL) {
            if (step->param_number == param_num)
                return 1;
            step = step->next;
		}
	}

    return 0;
}
#endif
static int g2GridCompare
#if NhlNeedProto
(Grib2ParamList *step, Grib2RecordInqRec *grib_rec)
#else
(step, grib_rec)
Grib2ParamList *step;
Grib2RecordInqRec *grib_rec;
#endif
{
	Grib2RecordInqRec *compare_rec = step->thelist->rec_inq;
	int r1;

	if (step->grid_number != grib_rec->grid_number)
		return step->grid_number - grib_rec->grid_number;


	if (compare_rec->gds->len_grid_template != grib_rec->gds->len_grid_template)
		return compare_rec->gds->len_grid_template - grib_rec->gds->len_grid_template;

	r1 = memcmp(compare_rec->gds->grid_template, grib_rec->gds->grid_template, 
		    sizeof(int) * grib_rec->gds->len_grid_template);
	return r1;
}


static int _g2CompareRecord
#if NhlNeedProto
(Grib2ParamList *step, Grib2RecordInqRec *grib_rec)
#else
(step, grib_rec)
    Grib2ParamList *step;
    Grib2RecordInqRec *grib_rec;
#endif
{
    int gridcomp;
    int result;

    result = memcmp(&step->traits,&grib_rec->traits,sizeof(Grib2VarTraits));

    if (result < 0)
	    return -1;
    else if (result > 0)
	    return 1;

    result = step->prob_type - grib_rec->ens.prob_type;
    if (result < 0)
	    return -1;
    else if (result > 0)
	    return 1;

    gridcomp = g2GridCompare(step,grib_rec);
    if (gridcomp < 0)
	    return -1;
    else if (gridcomp > 0)
	    return 1;

    return 0;
}

static void _g2AdjustTimeOffset
#if	NhlNeedProto
(Grib2ParamList *g2plist,Grib2RecordInqRec *grec)
#else
(g2plist,grec)
Grib2ParamList *g2plist;
Grib2RecordInqRec *grec;
#endif
{
	if (grec->time_period == 0)
		return;
	if (g2plist->forecast_time_units == grec->time_period_units)
		grec->time_offset = grec->time_offset + grec->time_period;
	else {
		int period;
		period = _g2GetConvertedTime(g2plist->forecast_time_units,
					     grec->time_period_units,
					     grec->time_period);
		grec->time_offset = grec->time_offset + period;
	}
	/* the following does not seem to work for ARPEGE data at least -- not sure what the end of the overall time interval represents */
	if (grec->overall_interval_seconds > 0) {
		int end_time = _g2GetConvertedTime(g2plist->forecast_time_units,
						   13, /* the time indicator for seconds */
						   grec->overall_interval_seconds);
		/*grec->time_offset = MIN(grec->time_offset, end_time);*/
		/*grec->time_offset = MAX(grec->time_offset,grec->forecast_time);*/
	}
}

static NclMultiDValData  _Grib2GetInternalVar
#if	NhlNeedProto
(Grib2FileRecord * therec,NclQuark name_q, NclGrib2FVarRec **vrec)
#else
(therec,name_q)
Grib2FileRecord * therec;
NclQuark name_q;
NclGrib2FVarRec **vrec
#endif
{
	Grib2InternalVarList *vstep; 

	vstep = therec->internal_var_list;
	while(vstep != NULL ) {
		if(vstep->int_var->var_info.var_name_quark == name_q) {
			*vrec = &vstep->int_var->var_info;
			return(vstep->int_var->value);
		} else {
			vstep = vstep->next;
		}
	}

	*vrec = NULL;
	return(NULL);
}

int g2it_equal(G2_GIT *it1, G2_GIT* it2)
{
    if ((it1->year == it2->year) &&
        (it1->days_from_jan1 == it2->days_from_jan1)
            && (it1->minute_of_day == it2->minute_of_day))
    return 1;

    return 0;
}

int g2ens_equal(G2_ENS *ens1, G2_ENS *ens2)
{
    if (ens1->prob_type <= -1) {
	    if ((ens1->type == ens2->type)
		&& (ens1->id == ens2->id)) {
		    return 1;
	    }
    }
    else {
	    switch (ens1->prob_type) {
		    /* case 0,3 involve lower limit case 1,4 upper limit case 2 both */
	    case 0:
	    case 3:
		    if (ens1->lower_limit_scale == ens2->lower_limit_scale &&
			ens1->lower_limit_value == ens2->lower_limit_value) {
			    return 1;
		    }
	    case 1:
	    case 4:
		    if (ens1->upper_limit_scale == ens2->upper_limit_scale &&
			ens1->upper_limit_value == ens2->upper_limit_value) {
			    return 1;
		    }
	    case 2:
		    if (ens1->lower_limit_scale == ens2->lower_limit_scale &&
			ens1->lower_limit_value == ens2->lower_limit_value &&
			ens1->upper_limit_scale == ens2->upper_limit_scale &&
			ens1->upper_limit_value == ens2->upper_limit_value) {
			    return 1;
		    }

	    }
    }
		
    return 0;
}

int g2it_comp (G2_GIT *it1, G2_GIT* it2)
{
    int return_val;

    return_val = it1->year - it2->year;

    if (! return_val) {
        return_val = it1->days_from_jan1 - it2->days_from_jan1;
        if (! return_val) {
            return_val = it1->minute_of_day - it2->minute_of_day;
        }
    }
	
    return return_val;
}


static int g2GetLVList
# if    NhlNeedProto
(Grib2FileRecord *g2frec,
 Grib2ParamList *thevar, 
 Grib2RecordInqRecList *lstep, float** lv_vals, float** lv_vals1) 
# else
(g2frec,thevar, lstep, lv_vals, lv_vals1) 
Grib2FileRecord *g2frec;
 Grib2ParamList *thevar;
 Grib2RecordInqRecList *lstep;
 float** lv_vals; 
 float** lv_vals1; 
# endif
{
    int n_lvs = 1;
    int i;
    Grib2RecordInqRecList   *strt, *tmp;
    int warn = 0;

    *lv_vals1 = NULL;
    strt = lstep;
    while(strt->next != NULL) {
        if (!g2LVNotEqual(strt, strt->next)) {
		/*if ((int)(g2frec->options[GRIB_PRINT_RECORD_INFO_OPT].values) != 0) {*/
			if (! warn)
				NhlPError(NhlWARNING,NhlEUNKNOWN,"NclGRIB2: %s contains records that NCL cannot currently differentiate. One or more records will be ignored.",
					  NrmQuarkToString(thevar->var_info.var_name_quark),strt->rec_inq->rec_num, 
					  strt->next->rec_inq->rec_num,
					  strt->rec_inq->rec_num);
			warn = 1;
		/*}*/
		tmp = strt->next;
		strt->next = strt->next->next;
		thevar->n_entries--;
		_Grib2FreeGrib2InqRec(tmp->rec_inq);
		NclFree(tmp);
        } else {
		n_lvs++;
		strt = strt->next;
        }
    }

    strt = lstep;
    *lv_vals = (float *) NclMalloc((unsigned)sizeof(float) * n_lvs);

    if (strt->rec_inq->traits.second_level_type != 255) {
	    *lv_vals1 = (float *) NclMalloc((unsigned)sizeof(float) * n_lvs);
    }

    strt = lstep;
    if (*lv_vals1) {
	    for (i = 0; i < n_lvs; i++) {
		    (*lv_vals)[i] = strt->rec_inq->level0;
		    if (strt->rec_inq->traits.second_level_type != 255) {
			    (*lv_vals1)[i] = strt->rec_inq->level1;
		    }
		    strt = strt->next;
	    }
    }
    else {
	    for (i = 0; i < n_lvs; i++) {
		    (*lv_vals)[i] = strt->rec_inq->level0;
		    strt = strt->next;
	    }
    }
	
    return n_lvs;
}

static G2_GIT *g2MergeIT
#if 	NhlNeedProto
(G2_GIT *tmp_it_vals, int *tmp_n_it_vals, G2_GIT *it_vals,int n_it)
#else
(tmp_it_vals,tmp_n_it_vals,it_vals,n_it)
G2_GIT *tmp_it_vals;
int *tmp_n_it_vals;
G2_GIT *it_vals;
int n_it;
#endif
{
	int i,j,k;
	G2_GIT *out_it_vals = NULL;

	i = 0;	
	j = 0;
	k = 0;

	out_it_vals = (G2_GIT *) NclMalloc((unsigned) sizeof(G2_GIT) * (*tmp_n_it_vals + n_it));
		
	while((i < *tmp_n_it_vals)&&(j< n_it)) {
		if(! g2it_comp(&(tmp_it_vals[i]),&(it_vals[j]))) {
			out_it_vals[k] = tmp_it_vals[i];
			i++;
			j++;
			k++;
		} else if(g2it_comp(&(tmp_it_vals[i]),&(it_vals[j])) < 0) {
			out_it_vals[k] = tmp_it_vals[i];
			k++;
			i++;
		} else {
			out_it_vals[k] = it_vals[j];
			k++;
			j++;
		}
	}
	if(i< *tmp_n_it_vals) {
		for( ; i < *tmp_n_it_vals;i++) {
			out_it_vals[k] = tmp_it_vals[i];
			k++;
		}	
	} else {
		for( ; j < n_it ;j++) {
			out_it_vals[k] = it_vals[j];
			k++;
		}	
	}

	NclFree(tmp_it_vals);
	*tmp_n_it_vals = k;	

	return out_it_vals;
}


static G2_FTLIST *g2GetFTList
# if    NhlNeedProto
(Grib2FileRecord *g2frec,
 Grib2ParamList *thevar, 
 Grib2RecordInqRecList *step, 
 int* n_ft, int **ft_vals,
 int* total_valid_lv, float** valid_lv_vals, float** valid_lv_vals1)
# else
(g2frec,thevar, step, n_ft, ft_vals, total_valid_lv, valid_lv_vals, valid_lv_vals1)
 Grib2FileRecord *g2frec;
 Grib2ParamList *thevar;
 Grib2RecordInqRecList *fstep;
 int* n_ft;
 int **ft_vals;
 int* total_valid_lv;
 float** valid_lv_vals;
 float** valid_lv_vals1;
# endif
{
    int i;
    Grib2RecordInqRecList   *strt, 
                            *fnsh,
                            *fstep,
                            *last;
    int n_fts = 0;
    int current_offset;
    G2_FTLIST header;
    G2_FTLIST   *the_end;
    float *tmp_lvs = NULL;
    float *tmp_lvs1 = NULL;
    int tmp_n_lvs = 0;


    the_end = &header;
    the_end->next = NULL;
    strt = fstep = step;
    last = fstep;
    current_offset = strt->rec_inq->time_offset;

    while (fstep->next != NULL) {
        if (fstep->next->rec_inq->time_offset != current_offset) {
            fnsh = fstep;
            last = fstep;
            fstep = fstep->next;
            fnsh->next = NULL;
            the_end->next = (G2_FTLIST *) NclMalloc((unsigned)sizeof(G2_FTLIST));
            the_end = the_end->next;
            the_end->ft = current_offset;
            the_end->thelist = strt;
            the_end->next = NULL;
            the_end->lv_vals = NULL;
            the_end->lv_vals1 = NULL;
            the_end->n_lv = 0;
            the_end->n_lv = g2GetLVList(g2frec,thevar, strt, &the_end->lv_vals, &the_end->lv_vals1);

	    if (strt->rec_inq->traits.first_level_type != 255) {
		    if (strt->rec_inq->traits.second_level_type == 255) {
			    if (tmp_lvs == NULL) {
				    tmp_lvs = (float *) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
				    tmp_n_lvs = the_end->n_lv;
				    memcpy((void *) tmp_lvs, the_end->lv_vals, the_end->n_lv * sizeof(float));
			    } else {
				    tmp_lvs = g2Merge(tmp_lvs, &tmp_n_lvs, the_end->lv_vals, the_end->n_lv);
			    }
		    } else {
			    /* Handle multiple value coordinate levels */
			    if (tmp_lvs == NULL) {
				    tmp_lvs = (float *) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
				    tmp_lvs1 = (float *) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
				    tmp_n_lvs = the_end->n_lv;
				    memcpy((void*) tmp_lvs, the_end->lv_vals, the_end->n_lv * sizeof(float));
				    memcpy((void* )tmp_lvs1, the_end->lv_vals1, the_end->n_lv * sizeof(float));
			    } else {
				    g2Merge2(tmp_lvs, tmp_lvs1, &tmp_n_lvs, the_end->lv_vals, the_end->lv_vals1,
					     the_end->n_lv, &tmp_lvs, &tmp_lvs1);
			    }
		    }
	    }

            strt = fstep;
            current_offset = strt->rec_inq->time_offset;
            n_fts++;
        } else {
			fstep = fstep->next;
            /***
            if (last != NULL) {
                last->next = fstep->next;
                fstep = last->next;
                thevar->n_entries--;
            }
            ***/
        }
    }

    the_end->next = (G2_FTLIST *) NclMalloc((unsigned)sizeof(G2_FTLIST));
    the_end = the_end->next;
    the_end->ft = current_offset;
    the_end->thelist = strt;
    the_end->next = NULL;
    the_end->lv_vals = NULL;
    the_end->lv_vals1 = NULL;
    the_end->n_lv = 0;
    the_end->n_lv = g2GetLVList(g2frec,thevar, strt, &the_end->lv_vals, &the_end->lv_vals1);

    if (strt->rec_inq->traits.first_level_type != 255) {
	    if (strt->rec_inq->traits.second_level_type == 255) {
		    if (tmp_lvs != NULL) {
			    tmp_lvs = g2Merge(tmp_lvs, &tmp_n_lvs, the_end->lv_vals, the_end->n_lv);
		    } else {
			    tmp_lvs = (float *) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
			    tmp_n_lvs = the_end->n_lv;
			    memcpy((void*) tmp_lvs, the_end->lv_vals, the_end->n_lv * sizeof(float));
		    }
	    } else {
		    /* Handle multiple value coordinate levels */
		    if (tmp_lvs == NULL) {
			    tmp_lvs = (float *) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
			    tmp_lvs1 = (float *) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
			    tmp_n_lvs = the_end->n_lv;
			    memcpy((void *) tmp_lvs, the_end->lv_vals, the_end->n_lv * sizeof(float));
			    memcpy((void *) tmp_lvs1, the_end->lv_vals1, the_end->n_lv * sizeof(float));
		    } else {
			    g2Merge2(tmp_lvs, tmp_lvs1, &tmp_n_lvs, the_end->lv_vals, the_end->lv_vals1,
				     the_end->n_lv, &tmp_lvs, &tmp_lvs1);
		    }
	    }
    }
    n_fts++;
    *n_ft = n_fts;
    *ft_vals = NclMalloc((unsigned)sizeof(int) * n_fts);
    the_end = header.next;
    for (i = 0; i < n_fts; i++) {
        (*ft_vals)[i] = the_end->ft;
        the_end = the_end->next;
    }

    *total_valid_lv = tmp_n_lvs;
    *valid_lv_vals = tmp_lvs;
    *valid_lv_vals1 = tmp_lvs1;
    return header.next;
}


static G2_ITLIST *g2GetITList
#if 	NhlNeedProto
(Grib2FileRecord *g2frec,
 Grib2ParamList *thevar, 
 Grib2RecordInqRecList *step,
 int* n_it, 
 G2_GIT **it_vals,
 int* n_ft,
 int **ft_vals,
 int* total_valid_lv,
 float** valid_lv_vals, 
 float** valid_lv_vals1)
#else
(g2frec,thevar, step, n_it, it_vals, n_ft, ft_vals, total_valid_lv, valid_lv_vals, valid_lv_vals1)
 Grib2FileRecord *g2frec; 
 Grib2ParamList *thevar;
 Grib2RecordInqRecList *step;
 int* n_it;
 G2_GIT **it_vals;
 int* n_ft;
 int **ft_vals;
 int* total_valid_lv;
 float** valid_lv_vals;
 float** valid_lv_vals1;
#endif
{
    int i;
    Grib2RecordInqRecList   *strt,
                            *fnsh,
                            *istep,
                            *last;
    int n_its = 0;
    G2_ITLIST header;
    G2_ITLIST   *the_end;
    int tmp_n_ft;
    int *tmp_ft_vals = NULL;
    float *tmp_lvs = NULL;
    float *tmp_lvs1 = NULL;
    int tmp_n_lvs = 0;
    G2_GIT current_it;


    the_end = &header;
    the_end->next = NULL;
	
    strt = istep = step;

    last = istep;
    current_it = strt->rec_inq->initial_time;
    while (istep->next != NULL) {
        if ((istep->next->rec_inq->initial_time.year == current_it.year)
          &&(istep->next->rec_inq->initial_time.days_from_jan1 == current_it.days_from_jan1)
          &&(istep->next->rec_inq->initial_time.minute_of_day == current_it.minute_of_day)) {
            istep = istep->next;
            continue;
        }

        fnsh = istep;
        last = istep;
        istep = istep->next;
        fnsh->next = NULL;
        the_end->next = (G2_ITLIST *) NclMalloc((unsigned) sizeof(G2_ITLIST));
        the_end = the_end->next;
        the_end->it = current_it;
        the_end->next = NULL;
        the_end->ft_vals = NULL;
        the_end->lv_vals = NULL;
        the_end->lv_vals1 = NULL;
        the_end->thelist = g2GetFTList(g2frec,thevar, strt, &the_end->n_ft, &the_end->ft_vals,
                                &the_end->n_lv, &the_end->lv_vals, &the_end->lv_vals1);
        if (the_end->n_ft > 0) {
            if (tmp_ft_vals == NULL) {
                tmp_ft_vals = NclMalloc((unsigned) sizeof(int) * the_end->n_ft);
                tmp_n_ft = the_end->n_ft;
                memcpy((void*) tmp_ft_vals, the_end->ft_vals, the_end->n_ft * sizeof(int));
            } else {
                tmp_ft_vals = g2MergeFT(tmp_ft_vals, &tmp_n_ft, the_end->ft_vals, the_end->n_ft);
            }
        }

	if (strt->rec_inq->traits.first_level_type != 255) {
		if (strt->rec_inq->traits.second_level_type == 255) {
			if (tmp_lvs == NULL) {
				tmp_lvs = (float *) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
				tmp_n_lvs = the_end->n_lv;
				memcpy((void*) tmp_lvs, the_end->lv_vals, the_end->n_lv * sizeof(float));
			} else {
				tmp_lvs = g2Merge(tmp_lvs, &tmp_n_lvs, the_end->lv_vals, the_end->n_lv);
			}
		} else {
			/* Handle multiple value coordinate levels */
			if (tmp_lvs == NULL) {
				tmp_lvs = (float*) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
				tmp_lvs1 = (float*) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
				tmp_n_lvs = the_end->n_lv;
				memcpy((void*) tmp_lvs, the_end->lv_vals, the_end->n_lv * sizeof(float));
				memcpy((void*) tmp_lvs1,the_end->lv_vals1, the_end->n_lv * sizeof(float));
			} else {
				g2Merge2(tmp_lvs, tmp_lvs1, &tmp_n_lvs, the_end->lv_vals, the_end->lv_vals1,
					 the_end->n_lv, &tmp_lvs, &tmp_lvs1);
			}
		}
	}

        strt = istep;
        current_it = strt->rec_inq->initial_time;
        n_its++;
    }

    the_end->next =(G2_ITLIST *) NclMalloc((unsigned)sizeof(G2_ITLIST));
    the_end = the_end->next;
    the_end->it = current_it;
    the_end->next = NULL;
    the_end->lv_vals = NULL;
    the_end->lv_vals1 = NULL;
    the_end->n_lv = 0;
    the_end->thelist = g2GetFTList(g2frec,thevar, strt, &the_end->n_ft, &the_end->ft_vals,
                        &the_end->n_lv, &the_end->lv_vals, &the_end->lv_vals1);

    if (the_end->n_ft > 0) {
        if (tmp_ft_vals == NULL) {
            tmp_ft_vals = NclMalloc((unsigned)sizeof(int) * the_end->n_ft);
            tmp_n_ft = the_end->n_ft;
            memcpy((void*) tmp_ft_vals, the_end->ft_vals, the_end->n_ft * sizeof(int));
        } else {
            tmp_ft_vals = g2MergeFT(tmp_ft_vals, &tmp_n_ft, the_end->ft_vals, the_end->n_ft);
        }
    }

    if (strt->rec_inq->traits.first_level_type != 255) {
	    if (strt->rec_inq->traits.second_level_type == 255) {
		    if (tmp_lvs != NULL) {
			    tmp_lvs = g2Merge(tmp_lvs, &tmp_n_lvs, the_end->lv_vals, the_end->n_lv);
		    } else {
			    tmp_lvs = (float *) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
			    tmp_n_lvs = the_end->n_lv;
			    memcpy((void *) tmp_lvs, the_end->lv_vals, the_end->n_lv * sizeof(float));
		    }
	    } else {
		    /* Handle multiple value coordinate levels */
		    if (tmp_lvs == NULL) {
			    tmp_lvs = (float *) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
			    tmp_lvs1 = (float *) NclMalloc((unsigned)sizeof(float) * the_end->n_lv);
			    tmp_n_lvs = the_end->n_lv;
			    memcpy((void*) tmp_lvs, the_end->lv_vals, the_end->n_lv * sizeof(float));
			    memcpy((void*) tmp_lvs1, the_end->lv_vals1, the_end->n_lv * sizeof(float));
		    } else {
			    g2Merge2(tmp_lvs, tmp_lvs1, &tmp_n_lvs, the_end->lv_vals, the_end->lv_vals1,
				     the_end->n_lv, &tmp_lvs, &tmp_lvs1);
		    }
	    }
    }

    n_its++;
    *n_it = n_its;
    *it_vals = NclMalloc((unsigned)sizeof(G2_GIT) * n_its);
    the_end = header.next;
    for (i = 0; i < n_its; i++) {
        (*it_vals)[i] = the_end->it;
        the_end = the_end->next;
    }

    *ft_vals = tmp_ft_vals;
    *n_ft = tmp_n_ft;
    *total_valid_lv = tmp_n_lvs;
    *valid_lv_vals = tmp_lvs;
    *valid_lv_vals1 = tmp_lvs1;
    return header.next;
}

static NhlErrorTypes _g2DetermineDimensionAndGridInfo
# if NhlNeedProto
(Grib2FileRecord *therec, Grib2ParamList* step)
# else
(therec, step)
Grib2FileRecord *therec;
Grib2ParamList* step;
# endif
{
    Grib2RecordInqRecList   *rstep,
                            *strt,
                            *fnsh,
                            *free_rec;
    int n_ens = 0,
        i, j, k, m,
        icount = 0;
    G2_ENS  current_ens;
    G2_ENSLIST  header;
    G2_ENSLIST  *the_end = NULL,
                *free_ens = NULL;
    G2_ITLIST   *itstep = NULL,
                *free_it = NULL;
    G2_FTLIST   *ftstep = NULL,
                *free_ft = NULL;
    float *tmp_lv_vals = NULL;
    float *tmp_lv_vals1 = NULL;
    int n_tmp_lv_vals = 0;
    int *tmp_ft_vals = NULL;
    int n_tmp_ft_vals = 0;
    G2_GIT *tmp_it_vals = NULL;
    int n_tmp_it_vals = 0;
    NclQuark *it_vals_q = NULL;
    NclQuark *ens_vals_q = NULL;
    int *ens_indexes = NULL;
    int n_tmp_ens_vals = 0;
    G2_ENS *tmp_ens_vals = NULL;
/*    int total;*/
    ng_size_t total, tmp_dim_siz = 0;
    int doff;
    char *name;
    float *lprob, *uprob;
    NhlErrorTypes returnval = NhlNOERROR;


    doff = step->var_info.doff;
    the_end = &header;
    memset(&header, 0, sizeof(G2_ENSLIST));
    name = NrmQuarkToString(step->var_info.var_name_quark);

    if (step->n_entries > 1) {
	    strt = rstep  = step->thelist;
	    current_ens = rstep->rec_inq->ens;

	    while (rstep->next != NULL) {
		    if (g2ens_equal(&rstep->next->rec_inq->ens, &current_ens)) {
			    rstep = rstep->next;
			    continue;
		    }

		    current_ens = rstep->next->rec_inq->ens;
		    fnsh = rstep;
		    rstep = rstep->next;
		    fnsh->next = NULL;
		
		    the_end->next = (G2_ENSLIST *) NclMalloc((unsigned int) sizeof(G2_ENSLIST));
		    the_end = the_end->next;
		    the_end->next = NULL;
		    the_end->ens = strt->rec_inq->ens;
		    the_end->ens_ix = n_ens;
		    the_end->thelist = g2GetITList(therec,step, strt, &the_end->n_it, &the_end->it_vals,
						   &the_end->n_ft, &the_end->ft_vals, &the_end->n_lv, &the_end->lv_vals,
						   &the_end->lv_vals1);
		    strt = rstep;
		    n_ens++;
	    }

	    the_end->next = (G2_ENSLIST *) NclMalloc((unsigned)sizeof(G2_ENSLIST));
	    the_end = the_end->next;
	    the_end->next = NULL;
	    the_end->ens = strt->rec_inq->ens;
	    the_end->ens_ix = n_ens;
	    the_end->thelist = g2GetITList(therec,step, strt, &the_end->n_it, &the_end->it_vals,
					   &the_end->n_ft, &the_end->ft_vals, &the_end->n_lv, &the_end->lv_vals,
					   &the_end->lv_vals1);
	    n_ens++;
	    the_end = header.next;

	    n_tmp_ens_vals = n_ens;	
	    tmp_ens_vals = (G2_ENS *) NclMalloc(sizeof(G2_ENS) * n_ens);
	    i = 0;
	    while (the_end != NULL) {
		    tmp_ens_vals[i] = the_end->ens;
		    if ((the_end->n_lv > 0)&&(the_end->lv_vals1 == NULL) ) {
			    if(tmp_lv_vals == NULL) {
				    tmp_lv_vals = NclMalloc((unsigned)sizeof(float)*the_end->n_lv);
				    n_tmp_lv_vals = the_end->n_lv;
				    memcpy((void*)tmp_lv_vals,the_end->lv_vals,the_end->n_lv*sizeof(float));
			    } else 	{
				    tmp_lv_vals  = g2Merge(tmp_lv_vals,&n_tmp_lv_vals,the_end->lv_vals,the_end->n_lv);
			    }
		    } else {
			    if(tmp_lv_vals == NULL) {
				    tmp_lv_vals = NclMalloc((unsigned)sizeof(float)*the_end->n_lv);
				    tmp_lv_vals1 = NclMalloc((unsigned)sizeof(float)*the_end->n_lv);
				    n_tmp_lv_vals = the_end->n_lv;
				    memcpy((void*)tmp_lv_vals,the_end->lv_vals,the_end->n_lv*sizeof(float));
				    memcpy((void*)tmp_lv_vals1,the_end->lv_vals1,the_end->n_lv*sizeof(float));
			    } else 	{
				    g2Merge2(tmp_lv_vals, tmp_lv_vals1, &n_tmp_lv_vals,
					     the_end->lv_vals, the_end->lv_vals1, the_end->n_lv, &tmp_lv_vals,
					     &tmp_lv_vals1);
			    }
		    }

		    if (the_end->n_ft > 0) {
			    if(tmp_ft_vals == NULL) {
				    tmp_ft_vals = NclMalloc((unsigned)sizeof(int)*the_end->n_ft);
				    n_tmp_ft_vals = the_end->n_ft;
				    memcpy((void *) tmp_ft_vals, the_end->ft_vals, the_end->n_ft * sizeof(int));
			    } else {
				    tmp_ft_vals = g2MergeFT(tmp_ft_vals, &n_tmp_ft_vals, the_end->ft_vals,
							    the_end->n_ft);
			    }
		    }

		    if (the_end->n_it > 0) {
			    if(tmp_it_vals == NULL) {
				    tmp_it_vals = (G2_GIT *)NclMalloc((unsigned)sizeof(G2_GIT)*the_end->n_it);
				    n_tmp_it_vals = the_end->n_it;
				    memcpy((void*)tmp_it_vals,the_end->it_vals,the_end->n_it*sizeof(G2_GIT));

			    } 
			    else {
				    tmp_it_vals = g2MergeIT(tmp_it_vals, &n_tmp_it_vals, the_end->it_vals,
							    the_end->n_it);
			    }
		    }

		    the_end = the_end->next;
/*            (void) fprintf(stdout,"%s\n",NrmQuarkToString(it_vals_q[i]));*/
		    i++;
	    }
#if 0
	    if (n_tmp_lv_vals > 0) {
		    fprintf(stdout,"(");
		    for(j = 0; j< n_tmp_lv_vals-1; j++) {	
			    fprintf(stdout,"%d, ",tmp_lv_vals[j]);
		    }

		    fprintf(stdout,"%d)\n",tmp_lv_vals[j]);
	    }

	    if (n_tmp_ft_vals > 0) {
		    fprintf(stdout,"(");
		    for(j = 0; j< n_tmp_ft_vals-1; j++) {	
			    fprintf(stdout,"%d, ",tmp_ft_vals[j]);
		    }
		    fprintf(stdout,"%d)\n",tmp_ft_vals[j]);
	    }
#endif
		
    } else {
	    n_tmp_ens_vals = 1;
	    tmp_ens_vals = (G2_ENS *) NclMalloc((unsigned) sizeof(G2_ENS));
	    memset(tmp_ens_vals, 0, sizeof(G2_ENS));
	    header.next = (G2_ENSLIST *) NclMalloc((unsigned) sizeof(G2_ENSLIST));
	    memset(header.next, 0, sizeof(G2_ENSLIST));
	    memset(&(header.next->ens), 0, sizeof(G2_ENS));
	    the_end = header.next;
	    the_end->thelist = g2GetITList(therec,step, step->thelist, &n_tmp_it_vals, &tmp_it_vals,
					   &n_tmp_ft_vals, &tmp_ft_vals, 
					   &n_tmp_lv_vals, &tmp_lv_vals, &tmp_lv_vals1);
    }

    i = 0;
    step->var_info.num_dimensions = 0;

    if (step->prob_type > -1 && n_tmp_ens_vals > 0) {
	    lprob = NclMalloc(n_tmp_ens_vals * sizeof(float));
	    uprob = NclMalloc(n_tmp_ens_vals * sizeof(float));
	    for (j = 0; j < n_tmp_ens_vals; j++) {
		    lprob[j] = tmp_ens_vals[j].lower_limit_value *
			    pow(10.0,-(double)tmp_ens_vals[j].lower_limit_scale);
		    uprob[j] = tmp_ens_vals[j].upper_limit_value *
			    pow(10.0,-(double)tmp_ens_vals[j].upper_limit_scale);
	    }
	    step->lower_probs = NULL;
	    step->upper_probs = NULL;
	    step->probability = NULL;
	    if (tmp_ens_vals[0].prob_type == 0 || tmp_ens_vals[0].prob_type == 3) {
		    tmp_dim_siz = (ng_size_t) n_tmp_ens_vals;
		    step->probability = (NclOneDValCoordData)_NclCreateVal(
			    NULL,
			    NULL,
			    Ncl_OneDValCoordData,
			    0,
			    (void*)lprob,
			    NULL,
			    1,
			    &tmp_dim_siz,
			    TEMPORARY,
			    NULL,
			    nclTypefloatClass);
		    NclFree(uprob);
	    }
	    else if (tmp_ens_vals[0].prob_type == 1 ||  tmp_ens_vals[0].prob_type == 4) {
		    tmp_dim_siz = (ng_size_t) n_tmp_ens_vals;
		    step->probability = (NclOneDValCoordData)_NclCreateVal(
			    NULL,
			    NULL,
			    Ncl_OneDValCoordData,
			    0,
			    (void*)uprob,
			    NULL,
			    1,
			    &tmp_dim_siz,
			    TEMPORARY,
			    NULL,
			    nclTypefloatClass);
		    NclFree(lprob);
	    }
	    else if (tmp_ens_vals[0].prob_type == 2) {
		    tmp_dim_siz = (ng_size_t) n_tmp_ens_vals;
		    step->lower_probs = (NclMultiDValData)_NclCreateVal(
			    NULL,
			    NULL,
			    Ncl_MultiDValData,
			    0,
			    (void*)lprob,
			    NULL,
			    1,
			    &tmp_dim_siz,
			    TEMPORARY,
			    NULL,
			    nclTypefloatClass);
		    step->upper_probs = (NclMultiDValData)_NclCreateVal(
			    NULL,
			    NULL,
			    Ncl_MultiDValData,
			    0,
			    (void*)uprob,
			    NULL,
			    1,
			    &tmp_dim_siz,
			    TEMPORARY,
			    NULL,
			    nclTypefloatClass);
	    }
    }
    else if (n_tmp_ens_vals > 0) {
	    ens_vals_q = (NclQuark *) NclMalloc(sizeof(NclQuark) * n_tmp_ens_vals);
	    ens_indexes = (int *) NclMalloc(sizeof(int) * n_tmp_ens_vals);
	    for (j = 0; j < n_tmp_ens_vals; j++) {
		    ens_vals_q[j] = g2GetEnsQuark(&(tmp_ens_vals[j]));
		    ens_indexes[j] = j;
	    }
	    tmp_dim_siz = (ng_size_t) n_tmp_ens_vals;
	    step->ensemble = (NclMultiDValData)_NclCreateVal(
		    NULL,
		    NULL,
		    Ncl_MultiDValData,
		    0,
		    (void *) ens_vals_q,
		    NULL,
		    1,
		    &tmp_dim_siz,
		    TEMPORARY,
		    NULL,
		    nclTypestringClass);

	    step->ens_indexes = (NclOneDValCoordData)_NclCreateVal(
		    NULL,
		    NULL,
		    Ncl_OneDValCoordData,
		    0,
		    (void *) ens_indexes,
		    NULL,
		    1,
		    &tmp_dim_siz,
		    TEMPORARY,
		    NULL,
		    nclTypeintClass);

    }
	
    if (n_tmp_ens_vals > 1 || 
	   (n_tmp_ens_vals > 0 && 
	    step->thelist->rec_inq->is_ensemble && 
	    (therec->single_dims & GRIB_Ensemble_Dims))) {
	    step->var_info.dim_sizes[i] = n_tmp_ens_vals;
	    step->ensemble_isatt = 0;
	    i++;

    } else if (n_tmp_ens_vals == 1) {
	    step->ensemble_isatt = 1;
    } else {
	    step->ensemble_isatt = 0;
    }

    NclFree(tmp_ens_vals);
    if (n_tmp_it_vals > 0) {
	    it_vals_q = (NclQuark *) NclMalloc(sizeof(NclQuark) * n_tmp_it_vals);
	    for (j = 0; j < n_tmp_it_vals; j++)
		    it_vals_q[j] = g2GetItQuark(&(tmp_it_vals[j]));
    }
    if(n_tmp_it_vals > 1 || (n_tmp_it_vals > 0 && (therec->single_dims & GRIB_Initial_Time_Dims))) {
	    step->var_info.dim_sizes[i] = (ng_size_t) n_tmp_it_vals;
	    tmp_dim_siz = (ng_size_t) n_tmp_it_vals;
	    step->yymmddhh = (NclOneDValCoordData)_NclCreateVal(
		    NULL,
		    NULL,
		    Ncl_OneDValCoordData,
		    0,
		    (void*)it_vals_q,
		    NULL,
		    1,
		    &tmp_dim_siz,
		    TEMPORARY,
		    NULL,
		    nclTypestringClass);
	    step->it_vals = tmp_it_vals;
			
	    step->yymmddhh_isatt = 0;
	    i++;
    } else if(n_tmp_it_vals == 1) {
	    step->yymmddhh_isatt = 1;
	    tmp_dim_siz = (ng_size_t) n_tmp_it_vals;
	    step->yymmddhh = (NclOneDValCoordData)_NclCreateVal(
		    NULL,
		    NULL,
		    Ncl_OneDValCoordData,
		    0,
		    (void*)it_vals_q,
		    NULL,
		    1,
		    &tmp_dim_siz,
		    TEMPORARY,
		    NULL,
		    nclTypestringClass);
	    step->it_vals = tmp_it_vals;
    } else {
	    step->yymmddhh_isatt = 0;
	    step->yymmddhh = NULL;
	    step->it_vals = NULL;
/*
  fprintf(stdout,"n_it: %d\n",n_tmp_it_vals);
*/
    }
    if (n_tmp_ft_vals > 1 || (n_tmp_ft_vals > 0 && (therec->single_dims & GRIB_Forecast_Time_Dims))) {
	    step->var_info.dim_sizes[i] = (ng_size_t) n_tmp_ft_vals;
	    tmp_dim_siz = (ng_size_t) n_tmp_ft_vals;
	    step->forecast_time = (NclOneDValCoordData)_NclCreateVal(
		    NULL,
		    NULL,
		    Ncl_OneDValCoordData,
		    0,
		    (void*)tmp_ft_vals,
		    NULL,
		    1,
		    &tmp_dim_siz,
		    TEMPORARY,
		    NULL,
		    nclTypeintClass);
	    step->forecast_time_isatt = 0;
	    i++;
    } else if(n_tmp_ft_vals == 1) {
	    tmp_dim_siz = (ng_size_t) n_tmp_ft_vals;
	    step->forecast_time = (NclOneDValCoordData)_NclCreateVal(
		    NULL,
		    NULL,
		    Ncl_OneDValCoordData,
		    0,
		    (void*)tmp_ft_vals,
		    NULL,
		    1,
		    &tmp_dim_siz,
		    TEMPORARY,
		    NULL,
		    nclTypeintClass);
	    step->forecast_time_isatt = 1;
    } else {
	    step->forecast_time_isatt = 0;
/*
  fprintf(stdout,"n_ft: %d\n",n_tmp_ft_vals);
*/
    }
    if((tmp_lv_vals != NULL)&&(tmp_lv_vals1 == NULL)) {
	    if (n_tmp_lv_vals > 1 || (n_tmp_lv_vals > 0 && (therec->single_dims & GRIB_Level_Dims))) {
		    step->var_info.dim_sizes[i] = (ng_size_t) n_tmp_lv_vals;
		    tmp_dim_siz = (ng_size_t) n_tmp_lv_vals;
		    step->levels = (NclOneDValCoordData)_NclCreateVal(
			    NULL,
			    NULL,
			    Ncl_OneDValCoordData,
			    0,
			    (void*)tmp_lv_vals,
			    NULL,
			    1,
			    &tmp_dim_siz,
			    TEMPORARY,
			    NULL,
			    nclTypefloatClass);
		    step->levels0 = NULL;
		    step->levels1 = NULL;
		    i++;
		    step->levels_isatt = 0;
	    } else if (n_tmp_lv_vals == 1) {
		    step->levels_isatt = 1;
		    tmp_dim_siz = (ng_size_t) n_tmp_lv_vals;
		    step->levels = (NclOneDValCoordData)_NclCreateVal(
			    NULL,
			    NULL,
			    Ncl_OneDValCoordData,
			    0,
			    (void*)tmp_lv_vals,
			    NULL,
			    1,
			    &tmp_dim_siz,
			    TEMPORARY,
			    NULL,
			    nclTypefloatClass);
		    step->levels0 = NULL;
		    step->levels1 = NULL;
	    } else {
		    step->levels_isatt = 0;
/*
  fprintf(stdout,"n_lv: %d\n",n_tmp_lv_vals);
*/
	    }
    } else if((tmp_lv_vals != NULL)&&(tmp_lv_vals1 != NULL)) { 
	    if(n_tmp_lv_vals > 1 || (n_tmp_lv_vals > 0 && (therec->single_dims & GRIB_Level_Dims))) {
		    step->var_info.dim_sizes[i] = (ng_size_t) n_tmp_lv_vals;
		    tmp_dim_siz = (ng_size_t) n_tmp_lv_vals;
		    step->levels = NULL;
		    step->levels0 = (NclMultiDValData)_NclCreateVal(
			    NULL,
			    NULL,
			    Ncl_MultiDValData,
			    0,
			    (void*)tmp_lv_vals,
			    NULL,
			    1,
			    &tmp_dim_siz,
			    TEMPORARY,
			    NULL,
			    nclTypefloatClass);
		    step->levels1 = (NclMultiDValData)_NclCreateVal(
			    NULL,
			    NULL,
			    Ncl_MultiDValData,
			    0,
			    (void*)tmp_lv_vals1,
			    NULL,
			    1,
			    &tmp_dim_siz,
			    TEMPORARY,
			    NULL,
			    nclTypefloatClass);
		    step->levels_has_two = 1;
		    i++;
		    step->levels_isatt = 0;
	    } else if (n_tmp_lv_vals == 1) {
		    step->levels_isatt = 1;
		    tmp_dim_siz = (ng_size_t) n_tmp_lv_vals;
		    step->levels = NULL;
		    step->levels0 = (NclMultiDValData)_NclCreateVal(
			    NULL,
			    NULL,
			    Ncl_MultiDValData,
			    0,
			    (void*)tmp_lv_vals,
			    NULL,
			    1,
			    &tmp_dim_siz,
			    TEMPORARY,
			    NULL,
			    nclTypefloatClass);
		    step->levels1 = (NclMultiDValData)_NclCreateVal(
			    NULL,
			    NULL,
			    Ncl_MultiDValData,
			    0,
			    (void*)tmp_lv_vals1,
			    NULL,
			    1,
			    &tmp_dim_siz,
			    TEMPORARY,
			    NULL,
			    nclTypefloatClass);
		    step->levels_has_two = 1;
	    } else {
		    step->levels_isatt = 0;
/*
  fprintf(stdout,"n_lv: %d\n",n_tmp_lv_vals);
*/
	    }
    } else {
	    step->levels_isatt = 0;
    }
    step->var_info.num_dimensions = i + (doff+1);
    for (i = 0; i < step->var_info.num_dimensions; i++) {
	    /* initialize the file dim number to something out of range */
	    step->var_info.file_dim_num[i] = -1;
    }

    /*
     * Now call grid code to get coordinates
     *
     * Build single array of GribRecordInqRecList *'s
     */
    if (step->var_info.num_dimensions - (doff + 1) <= 0) {
	    if (header.next != NULL) {
		    free_ens = header.next;

		    if (free_ens->lv_vals != NULL) 
			    NclFree(free_ens->lv_vals);

		    if (free_ens->lv_vals1 != NULL) 
			    NclFree(free_ens->lv_vals1);

		    if (free_ens->ft_vals != NULL) 
			    NclFree(free_ens->ft_vals);

		    if (free_ens->it_vals != NULL) 
			    NclFree(free_ens->it_vals);

		    if (free_ens->thelist != NULL) {
			    if (free_ens->thelist->lv_vals != NULL)
				    NclFree(free_ens->thelist->lv_vals);

			    if (free_ens->thelist->lv_vals1 != NULL)
				    NclFree(free_ens->thelist->lv_vals1);

			    if (free_ens->thelist->ft_vals != NULL)
				    NclFree(free_ens->thelist->ft_vals);
			    if (free_ens->thelist->thelist != NULL) {
				    if (free_ens->thelist->thelist->lv_vals != NULL)
					    NclFree(free_ens->thelist->thelist->lv_vals);
				    if (free_ens->thelist->thelist->lv_vals1 != NULL)
					    NclFree(free_ens->thelist->thelist->lv_vals1);
				    NclFree(free_ens->thelist->thelist);
			    }
			    NclFree(free_ens->thelist);
		    }

		    NclFree(free_ens);
	    }

	    return returnval;
    }

    total = 1;
    for(i = 0; i < step->var_info.num_dimensions - (doff +1); i++) {
	    total *= step->var_info.dim_sizes[i];
    }
    strt = (Grib2RecordInqRecList*)NclMalloc((unsigned)sizeof(Grib2RecordInqRecList)*total);
    for (i = 0; i < total; i++) {
	    strt[i].rec_inq = (Grib2RecordInqRec*)10;
	    strt[i].next = NULL;
    }
    the_end = header.next;
    i = 0;
    icount = 0;

#define PRINT_MISSING(ens_ix,it_ix,ft_ix,lv_ix) \
	sprintf(buf,"%s->%s is missing",NrmQuarkToString(therec->file_path_q),name); \
	if (n_tmp_ens_vals > 1) \
		sprintf(&(buf[strlen(buf)])," ens: %d",ens_ix); \
	if (n_tmp_it_vals > 1) \
		sprintf(&(buf[strlen(buf)])," it: %s",NrmQuarkToString(it_vals_q[it_ix])); \
	if (n_tmp_ft_vals > 1) \
		sprintf(&(buf[strlen(buf)])," ft: %d",tmp_ft_vals[ft_ix]); \
	if (n_tmp_lv_vals > 1) { \
		if (! step->levels_has_two) \
			sprintf(&(buf[strlen(buf)])," lv: %f",tmp_lv_vals[lv_ix]); \
		else \
			sprintf(&(buf[strlen(buf)])," lv: (%f, %f)",tmp_lv_vals[lv_ix],tmp_lv_vals1[lv_ix]); \
        } \
	NhlPError(NhlWARNING,NhlEUNKNOWN,buf)
			
    while(the_end != NULL) {
	    char buf[256];
	    itstep = the_end->thelist;
	    j = 0;
	    while(itstep != NULL) {
		    ftstep = itstep->thelist;
		    if ((tmp_it_vals != NULL) && (! g2it_equal(&itstep->it,&(tmp_it_vals[j])))) {
			    for (k = 0; k < n_tmp_ft_vals; k++) {
				    for( m = 0 /* i already set */; m < n_tmp_lv_vals; i++,m++) {
					    strt[i].rec_inq = NULL;
					    PRINT_MISSING(the_end->ens_ix,j,k,m);
				    }
			    }
			    j++;
			    continue;
		    }
		    k = 0;
		    while (ftstep != NULL) {
			    rstep = ftstep->thelist;
			    if((tmp_ft_vals != NULL)&&(ftstep->ft != tmp_ft_vals[k])){
				    for( m = 0 /* i already set */; m < n_tmp_lv_vals; i++,m++) {
					    strt[i].rec_inq = NULL;
					    PRINT_MISSING(the_end->ens_ix,j,k,m);
				    }
				    k++;
				    continue;
			    }
			    m = 0;
			    if(!step->levels_has_two) {
				    while(rstep != NULL && (n_tmp_lv_vals == 0 || m < n_tmp_lv_vals)) {
					    if((tmp_lv_vals == NULL) ||(rstep->rec_inq->level0 == tmp_lv_vals[m])) {
						    strt[i].rec_inq = rstep->rec_inq;	
						    icount +=1;
						    free_rec = rstep;
						    rstep = rstep->next;
						    NclFree(free_rec);
						    m++;
					    } else {
						    strt[i].rec_inq = NULL;
						    PRINT_MISSING(the_end->ens_ix,j,k,m);
						    m++;
					    }
					    i++;
				    }
				    if((rstep == NULL)&&(m < n_tmp_lv_vals)) {
					    for( ;m < n_tmp_lv_vals; m++) {
						    strt[i].rec_inq = NULL;
						    PRINT_MISSING(the_end->ens_ix,j,k,m);
						    i++;
					    }
				    }
			    } else {
				    while(rstep != NULL && (n_tmp_lv_vals == 0 || m < n_tmp_lv_vals)) {
					    if((rstep->rec_inq->level0 == tmp_lv_vals[m])
					       &&(rstep->rec_inq->level1 == tmp_lv_vals1[m])) {
						    strt[i].rec_inq = rstep->rec_inq;	
						    icount +=1;
						    free_rec = rstep;
						    rstep = rstep->next;
						    NclFree(free_rec);
						    m++;
					    } else {
						    strt[i].rec_inq = NULL;
						    PRINT_MISSING(the_end->ens_ix,j,k,m);
						    m++;
					    }
					    i++;
				    }
				    if((rstep == NULL)&&(m < n_tmp_lv_vals)) {
					    for( ;m < n_tmp_lv_vals; m++) {
						    strt[i].rec_inq = NULL;
						    PRINT_MISSING(the_end->ens_ix,j,k,m);
						    i++;
					    }
				    }
			    }
			    free_ft = ftstep;
			    ftstep = ftstep->next;
			    if(free_ft->lv_vals != NULL) 
				    NclFree(free_ft->lv_vals);
			    if(free_ft->lv_vals1 != NULL) 
				    NclFree(free_ft->lv_vals1);
			    NclFree(free_ft);
			    k++;
		    }
		    while(k < n_tmp_ft_vals) {
			    for( m = 0 /* i already set */; m < n_tmp_lv_vals; i++,m++) {
				    strt[i].rec_inq = NULL;
				    PRINT_MISSING(the_end->ens_ix,j,k,m);
			    }
			    k++;
		    }
		    free_it = itstep;
		    itstep = itstep->next;
		    if (free_it->lv_vals != NULL)
			    NclFree(free_it->lv_vals);
		    if(free_it->lv_vals1 != NULL) 
			    NclFree(free_it->lv_vals1);
		    if(free_it->ft_vals != NULL) 
			    NclFree(free_it->ft_vals);
		    NclFree(free_it);
		    j++;
	    }
	    while (j < n_tmp_it_vals) {
		    for (k = 0; k < n_tmp_ft_vals; k++) {
			    for( m = 0 /* i already set */; m < n_tmp_lv_vals; i++,m++) {
				    strt[i].rec_inq = NULL;
				    PRINT_MISSING(the_end->ens_ix,j,k,m);
			    }
		    }
		    j++;
	    }
	    free_ens = the_end;
	    the_end = the_end->next;
	    if(free_ens->lv_vals != NULL) 
		    NclFree(free_ens->lv_vals);
	    if(free_ens->lv_vals1 != NULL) 
		    NclFree(free_ens->lv_vals1);
	    if(free_ens->ft_vals != NULL) 
		    NclFree(free_ens->ft_vals);
	    if(free_ens->it_vals != NULL) 
		    NclFree(free_ens->it_vals);
	    NclFree(free_ens);
    }
    while(i<total) strt[i++].rec_inq = NULL;
    step->thelist = strt;
    step->n_entries = total;
	
    return(returnval);
}



static void _g2SetFileDimsAndCoordVars
# if 	NhlNeedProto
(Grib2FileRecord *therec)
# else
(therec)
    Grib2FileRecord *therec;
# endif
{
    Grib2ParamList  *step,
                    *last,
                    *tmpstep;
    char buffer[1024];

    NclQuark    ygrid_q,
                lat_q;
    Grib2DimInqRecList  *dstep,
                        *ptr;
    Grib2DimInqRec  *tmp;
    NclQuark    *it_rhs,
                *it_lhs;
    int *rhs,
        *lhs;
    int *rhs1,
        *lhs1;

    float   *rhs_f, *rhs_f1,
            *lhs_f, *lhs_f1;
    int i,
        j,
        m;
    int current_dim = 0;

    NclMultiDValData    tmp_md;
    NclMultiDValData    tmp_md1;

    NclGrib2FVarRec *test;

    int n_dims_lat = 0;
    int n_dims_lon = 0;
    int n_dims_rot = 0;

    ng_size_t *dimsizes_lat = NULL;
    ng_size_t *dimsizes_lon = NULL;
    ng_size_t *dimsizes_rot = NULL;

    float   *tmp_lat = NULL;
    float   *tmp_lon = NULL;
    float   *tmp_rot = NULL;

    NhlErrorTypes is_err = NhlNOERROR;

    int tmp_file_dim_numbers[2];
    char name_buffer[1024];

    Grib2AttInqRecList  *att_list_ptr = NULL;
    Grib2AttInqRecList  *tmp_att_list_ptr = NULL;

    NclQuark    *tmp_string = NULL;

    float   *tmp_float = NULL;

    int nlonatts = 0;
    int nlatatts = 0;
    int nrotatts = 0;

    Grib2RecordInqRec   *g2inqrec = NULL;
    Grib2AttInqRecList  *lat_att_list_ptr = NULL;
    Grib2AttInqRecList  *lon_att_list_ptr = NULL;
    Grib2AttInqRecList  *rot_att_list_ptr = NULL;
    g2codeTable *ct = NULL;
    char *name_suffix = "";

    ct = (g2codeTable *) NclMalloc(1 * sizeof(g2codeTable));
    if (ct == NULL) {
	    NhlPError(NhlFATAL, NhlEUNKNOWN,
		      " Unable to allocate code table data, cannot continue.");
	    return;
    }
    memset(ct,0,sizeof(g2codeTable));


    therec->total_dims = 0;
    therec->n_scalar_dims = 0;
    therec->scalar_dims = NULL;
    therec->n_probability_dims = 0;
    therec->probability_dims = NULL;
    therec->n_ensemble_dims = 0;
    therec->ensemble_dims = NULL;
    therec->n_it_dims = 0;
    therec->it_dims = NULL;
    therec->n_ft_dims = 0;
    therec->ft_dims = NULL;
    therec->n_lv_dims = 0;
    therec->lv_dims = NULL;
    therec->n_grid_dims = 0;
    therec->grid_dims = NULL;
    therec->n_grids = 0;
    step = therec->var_list;
    last = NULL;

    while (step != NULL) {
        step->ref_rec = NULL;
	for(i = 0; i < step->n_entries; i++) {
		if(step->thelist[i].rec_inq != NULL) {
			g2inqrec = step->ref_rec = step->thelist[i].rec_inq;
			break;
		}
	}
	if (!g2inqrec) {
		NhlPError(NhlFATAL,NhlEUNKNOWN,"NclGRIB: Variable contains no GRIB records");
		is_err = NhlFATAL;
	}

        current_dim = 0;
        step->aux_coords[0] = step->aux_coords[1] = NrmNULLQUARK;
	if (step->prob_type > -1) {
		if(!step->ensemble_isatt) {
			dstep = therec->probability_dims;
			if (step->probability) { /* either a lower or an upper limit (but not both) */
				for(i = 0; i < therec->n_probability_dims; i++) {
					char *cp;
					strcpy(buffer,NrmQuarkToString(dstep->dim_inq->dim_name));
					cp = strchr(buffer,'_');
					if (cp) *cp = '\0';
					if (NrmStringToQuark(buffer) != step->var_info.param_q) {
						dstep = dstep->next;
					}
					else if(dstep->dim_inq->size == step->probability->multidval.dim_sizes[0]) {
						tmp_md = _Grib2GetInternalVar(therec,dstep->dim_inq->dim_name,&test);
						if(tmp_md != NULL) {
							lhs_f = (float*)tmp_md->multidval.val;
							rhs_f = (float*)step->probability->multidval.val;
							j = 0;
							while(j<dstep->dim_inq->size) {
								if(lhs_f[j] != rhs_f[j]) {
									break;
								} else {
									j++;
								}
							}
							if(j == dstep->dim_inq->size) {
								break;
							} else {
								dstep= dstep->next;
							}
						} else {
							dstep  = dstep->next;
						}
					} else {
						dstep = dstep->next;
					}
				}
				if (dstep) {
					step->var_info.file_dim_num[current_dim] = dstep->dim_inq->dim_number;
					_NclDestroyObj((NclObj)step->probability);
					step->probability = NULL;
				}
				else {

                                        /*
					 * Need a new dimension entry w name and number
					 */
					tmp = (Grib2DimInqRec*)NclMalloc((unsigned)sizeof(Grib2DimInqRec));
					tmp->dim_number = therec->total_dims;
					tmp->size = step->probability->multidval.dim_sizes[0];
					sprintf(buffer,"%s_probability%d",NrmQuarkToString(step->var_info.param_q),therec->n_probability_dims);
					tmp->dim_name = NrmStringToQuark(buffer);
					ptr = (Grib2DimInqRecList*)NclMalloc((unsigned)sizeof(Grib2DimInqRecList));
					ptr->dim_inq = tmp;
					ptr->next = therec->probability_dims;
					therec->probability_dims = ptr;
					therec->n_probability_dims++;
					step->var_info.file_dim_num[current_dim] = tmp->dim_number;

					tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
					*tmp_string = step->var_info.units_q;
					Grib2PushAtt(&tmp_att_list_ptr,"units",tmp_string,1,nclTypestringClass); 

					sprintf(buffer,"%s limits",NrmQuarkToString(step->var_info.long_name_q));
					tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
					*tmp_string = NrmStringToQuark(buffer);
					Grib2PushAtt(&tmp_att_list_ptr,"long_name",tmp_string,1,nclTypestringClass); 

					_Grib2AddInternalVar(therec,tmp->dim_name,&tmp->dim_number,
							    (NclMultiDValData)step->probability,tmp_att_list_ptr,2);
					tmp_att_list_ptr = NULL;
					step->probability = NULL;
					therec->total_dims++;
				}
				current_dim++;
			}
			else if (step->upper_probs && step->lower_probs) {
				for(i = 0; i < therec->n_probability_dims; i++) {
					if(dstep->dim_inq->size == step->upper_probs->multidval.dim_sizes[0]) {
						sprintf(name_buffer,"%s%s",NrmQuarkToString(dstep->dim_inq->dim_name),"_upper");
						tmp_md = _Grib2GetInternalVar(therec,NrmStringToQuark(name_buffer),&test);
						sprintf(name_buffer,"%s%s",NrmQuarkToString(dstep->dim_inq->dim_name),"_lower");
						tmp_md1 = _Grib2GetInternalVar(therec,NrmStringToQuark(name_buffer),&test);
						j = 0;
						if((tmp_md != NULL )&&(tmp_md1 != NULL) ) {
							lhs_f = (float*)tmp_md->multidval.val;
							rhs_f = (float*)step->upper_probs->multidval.val;
							lhs_f1 = (float*)tmp_md1->multidval.val;
							rhs_f1 = (float*)step->lower_probs->multidval.val;
							while(j<dstep->dim_inq->size) {
								if((lhs_f[j] != rhs_f[j])||(lhs_f1[j] != rhs_f1[j])) {
									break;
								} else {
									j++;
								}
							}
						}
						if(j == dstep->dim_inq->size) {
							break;
						} else {
							dstep= dstep->next;
						}
					} else {
						dstep = dstep->next;
					}
				}
				if (dstep) {
					step->var_info.file_dim_num[current_dim] = dstep->dim_inq->dim_number;
					_NclDestroyObj((NclObj)step->upper_probs);
					step->upper_probs = NULL;
					_NclDestroyObj((NclObj)step->lower_probs);
					step->lower_probs = NULL;
				}
				else {

                                        /*
					 * Need a new dimension entry w name and number
					 */
					tmp = (Grib2DimInqRec*)NclMalloc((unsigned)sizeof(Grib2DimInqRec));
					tmp->dim_number = therec->total_dims;
					tmp->size = step->upper_probs->multidval.dim_sizes[0];
					sprintf(buffer,"%s_probability%d",NrmQuarkToString(step->var_info.param_q),therec->n_probability_dims);
					tmp->dim_name = NrmStringToQuark(buffer);
					ptr = (Grib2DimInqRecList*)NclMalloc((unsigned)sizeof(Grib2DimInqRecList));
					ptr->dim_inq = tmp;
					ptr->next = therec->probability_dims;
					therec->probability_dims = ptr;
					therec->n_probability_dims++;
					step->var_info.file_dim_num[current_dim] = tmp->dim_number;

					tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
					*tmp_string = step->var_info.units_q;
					Grib2PushAtt(&tmp_att_list_ptr,"units",tmp_string,1,nclTypestringClass); 

					sprintf(buffer,"%s lower limits",NrmQuarkToString(step->var_info.long_name_q));
					tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
					*tmp_string = NrmStringToQuark(buffer);
					tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
					*tmp_string = NrmStringToQuark(buffer);
					Grib2PushAtt(&tmp_att_list_ptr,"long_name",tmp_string,1,nclTypestringClass); 
					sprintf(name_buffer,"%s%s",NrmQuarkToString(tmp->dim_name),"_lower");

					_Grib2AddInternalVar(therec,NrmStringToQuark(name_buffer),&tmp->dim_number,
							    (NclMultiDValData)step->lower_probs,tmp_att_list_ptr,2);
					tmp_att_list_ptr = NULL;
					step->lower_probs = NULL;

					tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
					*tmp_string = step->var_info.units_q;
					Grib2PushAtt(&tmp_att_list_ptr,"units",tmp_string,1,nclTypestringClass); 

					sprintf(buffer,"%s upper limits",NrmQuarkToString(step->var_info.long_name_q));
					tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
					*tmp_string = NrmStringToQuark(buffer);
					Grib2PushAtt(&tmp_att_list_ptr,"long_name",tmp_string,1,nclTypestringClass); 
					sprintf(name_buffer,"%s%s",NrmQuarkToString(tmp->dim_name),"_upper");

					_Grib2AddInternalVar(therec,NrmStringToQuark(name_buffer),&tmp->dim_number,
							    (NclMultiDValData)step->upper_probs,tmp_att_list_ptr,2);
					tmp_att_list_ptr = NULL;
					step->upper_probs = NULL;

					therec->total_dims++;
				}
				current_dim++;
			}
		}
	}
        else if (!step->ensemble_isatt) {
            dstep = therec->ensemble_dims;
            for (i = 0; i < therec->n_ensemble_dims; i++) {
                if (dstep->dim_inq->size == step->ensemble->multidval.dim_sizes[0]) {
	                tmp_md = _Grib2GetInternalVar(therec, dstep->dim_inq->dim_name, &test);
                    if (tmp_md != NULL) {
                        lhs = (int *) tmp_md->multidval.val;
                        rhs = (int *) step->ens_indexes->multidval.val;
                        j = 0;
                        while (j < dstep->dim_inq->size) {
                            if (lhs[j] != rhs[j]) {
                                break;
                            } else {
                                j++;
                            }
                        }

                        if (j == dstep->dim_inq->size) {
                            break;
                        } else {
                            dstep= dstep->next;
                        }
                    } else {
                        dstep  = dstep->next;
                    }
                } else {
                    dstep = dstep->next;
                }
            }

            if (dstep == NULL) {
                /* Need a new dimension entry w name and number */
                tmp = (Grib2DimInqRec *) NclMalloc((unsigned) sizeof(Grib2DimInqRec));
                tmp->gds = NULL;
                tmp->dim_number = therec->total_dims;
                tmp->size = step->ensemble->multidval.dim_sizes[0];
                sprintf(buffer, "ensemble%d", therec->n_ensemble_dims);
                tmp->dim_name = NrmStringToQuark(buffer);
                tmp->grid_number = -1;
                therec->total_dims++;
                ptr = (Grib2DimInqRecList *) NclMalloc((unsigned) sizeof(Grib2DimInqRecList));
                ptr->dim_inq = tmp;
                ptr->next = therec->ensemble_dims;
                therec->ensemble_dims = ptr;
                step->var_info.file_dim_num[current_dim] = tmp->dim_number;

                tmp_string = (NclQuark *) NclMalloc(sizeof(NclQuark));
                *tmp_string = NrmStringToQuark("non-dim");
                Grib2PushAtt(&tmp_att_list_ptr, "units", tmp_string, 1, nclTypestringClass); 

                tmp_string = (NclQuark *) NclMalloc(sizeof(NclQuark));
                *tmp_string = NrmStringToQuark("ensemble indexes");
                Grib2PushAtt(&tmp_att_list_ptr, "long_name", tmp_string, 1, nclTypestringClass); 

                _Grib2AddInternalVar(therec,tmp->dim_name,&tmp->dim_number,
                        (NclMultiDValData)step->ens_indexes, tmp_att_list_ptr, 2);
                tmp_att_list_ptr = NULL;
                step->ens_indexes = NULL;

                tmp_string = (NclQuark *) NclMalloc(sizeof(NclQuark));
                *tmp_string = NrmStringToQuark("ensemble elements description");
                Grib2PushAtt(&tmp_att_list_ptr, "long_name", tmp_string, 1, nclTypestringClass); 

                sprintf(&(buffer[strlen(buffer)]), "_info");
                _Grib2AddInternalVar(therec, NrmStringToQuark(buffer), &tmp->dim_number,
                        (NclMultiDValData) step->ensemble, tmp_att_list_ptr, 1);
                tmp_att_list_ptr = NULL;
                step->ensemble = NULL;
                therec->n_ensemble_dims++;
            } else {
                step->var_info.file_dim_num[current_dim] = dstep->dim_inq->dim_number;
                _NclDestroyObj((NclObj) step->ens_indexes);
                step->ens_indexes = NULL;
                _NclDestroyObj((NclObj) step->ensemble);
                step->ensemble = NULL;
            }
            current_dim++;
        }

	if (!step->yymmddhh_isatt) {
		dstep = therec->it_dims;
		for (i = 0; i < therec->n_it_dims; i++) {
			if (dstep->dim_inq->size == step->yymmddhh->multidval.dim_sizes[0]) {
				tmp_md = _Grib2GetInternalVar(therec, dstep->dim_inq->dim_name, &test);
				if (tmp_md != NULL) {
					it_lhs = (NclQuark *) tmp_md->multidval.val;
	
					it_rhs = (NclQuark *) step->yymmddhh->multidval.val;
					j = 0;
					while (j < dstep->dim_inq->size) {
						if (it_lhs[j] != it_rhs[j])
							break;
						else
							j++;
					}

					if (j == dstep->dim_inq->size)
						break;
					else
						dstep= dstep->next;
				} else {
					dstep = dstep->next;
				}
			} else {
				dstep = dstep->next;
			}
		}

		/* All pointers to coordate will end up in dim list not in param list */
		if (dstep == NULL) {
			/* Need a new dimension entry w name and number */
			tmp = (Grib2DimInqRec *) NclMalloc((unsigned) sizeof(Grib2DimInqRec));
			tmp->gds = NULL;
			tmp->dim_number = therec->total_dims;
			tmp->size = step->yymmddhh->multidval.dim_sizes[0];
			sprintf(buffer, "initial_time%d", therec->n_it_dims);
			tmp->dim_name = NrmStringToQuark(buffer);
			tmp->grid_number = -1;
			therec->total_dims++;
			ptr = (Grib2DimInqRecList *) NclMalloc((unsigned) sizeof(Grib2DimInqRecList));
			ptr->dim_inq = tmp;
			ptr->next = therec->it_dims;
			therec->it_dims = ptr;
			therec->n_it_dims++;
			step->var_info.file_dim_num[current_dim] = tmp->dim_number;
				
			tmp_string = (NclQuark *) NclMalloc(sizeof(NclQuark));
			*tmp_string = NrmStringToQuark("mm/dd/yyyy (hh:mm)");
			Grib2PushAtt(&tmp_att_list_ptr, "units", tmp_string, 1, nclTypestringClass); 

			tmp_string = (NclQuark *) NclMalloc(sizeof(NclQuark));
			*tmp_string = NrmStringToQuark("Initial time of first record");
			Grib2PushAtt(&tmp_att_list_ptr, "long_name", tmp_string, 1, nclTypestringClass); 

			_Grib2AddInternalVar(therec,tmp->dim_name, &tmp->dim_number,
					     (NclMultiDValData) step->yymmddhh, tmp_att_list_ptr, 2);
			tmp_att_list_ptr = NULL;
			step->yymmddhh = NULL;
		} else {
			step->var_info.file_dim_num[current_dim] = dstep->dim_inq->dim_number;
			_NclDestroyObj((NclObj) step->yymmddhh);
			step->yymmddhh = NULL;
		}
		current_dim++;
	}

        if (!step->forecast_time_isatt) {
			dstep = therec->ft_dims;
			for (i = 0; i < therec->n_ft_dims; i++) {
				if (dstep->dim_inq->grid_number == step->forecast_time_units &&
				    dstep->dim_inq->size == step->forecast_time->multidval.dim_sizes[0]) {
					tmp_md = _Grib2GetInternalVar(therec, dstep->dim_inq->dim_name, &test);
					if (tmp_md != NULL) {
						lhs = (int *) tmp_md->multidval.val;
						rhs = (int *) step->forecast_time->multidval.val;
						j = 0;
						while (j < dstep->dim_inq->size) {
							if (lhs[j] != rhs[j]) {
								break;
							} else {
								j++;
							}
						}
						if (j == dstep->dim_inq->size) {
							break;
						} else {
							dstep= dstep->next;
						}
					} else {
						dstep  = dstep->next;
					}
				} else {
					dstep = dstep->next;
				}
			}

			if (dstep == NULL) {
                /* Need a new dimension entry name and number */
				tmp = (Grib2DimInqRec *) NclMalloc((unsigned) sizeof(Grib2DimInqRec));
				tmp->gds = NULL;
				tmp->dim_number = therec->total_dims;
				tmp->size = step->forecast_time->multidval.dim_sizes[0];
				sprintf(buffer, "forecast_time%d", therec->n_ft_dims);
				tmp->dim_name = NrmStringToQuark(buffer);
				/* funky - but storing the forecast time unit in the grid_number member -- need a union I guess*/
				tmp->grid_number = step->forecast_time_units;
				therec->total_dims++;
				ptr = (Grib2DimInqRecList *) NclMalloc((unsigned) sizeof(Grib2DimInqRecList));
				ptr->dim_inq = tmp;
				ptr->next = therec->ft_dims;
				therec->ft_dims = ptr;
				therec->n_ft_dims++;
				step->var_info.file_dim_num[current_dim] = tmp->dim_number;

				tmp_string = (NclQuark *) NclMalloc(sizeof(NclQuark));
				*tmp_string = NrmStringToQuark("hours");
				Grib2PushAtt(&tmp_att_list_ptr, "units", tmp_string, 1, nclTypestringClass); 

				tmp_string = (NclQuark *) NclMalloc(sizeof(NclQuark));
				*tmp_string = NrmStringToQuark("Forecast offset from initial time");
				Grib2PushAtt(&tmp_att_list_ptr, "long_name", tmp_string, 1, nclTypestringClass); 

				_Grib2AddInternalVar(therec, tmp->dim_name, &tmp->dim_number,
                        (NclMultiDValData) step->forecast_time, tmp_att_list_ptr, 2);
				tmp_att_list_ptr = NULL;
				step->forecast_time = NULL;
			} else {
				step->var_info.file_dim_num[current_dim] = dstep->dim_inq->dim_number;
				_NclDestroyObj((NclObj) step->forecast_time);
				step->forecast_time = NULL;
			}
			current_dim++;
        }

	if ((!step->levels_isatt) && (step->levels != NULL)) {
		dstep = therec->lv_dims;
		for (i = 0; i < therec->n_lv_dims; i++) {
			/* using the grid_number member to store the level indicator */
			if (dstep->dim_inq->grid_number == step->level_indicator &&
			    dstep->dim_inq->size == step->levels->multidval.dim_sizes[0]) {
				tmp_md = _Grib2GetInternalVar(therec, dstep->dim_inq->dim_name, &test);
				if (tmp_md != NULL ) {
					lhs = (int *) tmp_md->multidval.val;
					rhs = (int *) step->levels->multidval.val;
					j = 0;
					while (j < dstep->dim_inq->size) {
						if (lhs[j] != rhs[j]) {
							break;
						} else {
							j++;
						}
					}
					if (j == dstep->dim_inq->size) {
						break;
					} else {
						dstep= dstep->next;
					}
				} else {
					dstep= dstep->next;
				}
			} else {
				dstep = dstep->next;
			}
		}

		if (dstep == NULL) {
			/* Need a new dimension entry name and number */
			Grib2ReadCodeTable(g2inqrec->table_source, 4, 
					   "4.5.table",g2inqrec->level_indicator,-1,ct);
			tmp = (Grib2DimInqRec *) NclMalloc((unsigned)sizeof(Grib2DimInqRec));
			tmp->gds = NULL;
			tmp->dim_number = therec->total_dims;
			/* use the  grid_number for the level indicator */
			tmp->grid_number = step->level_indicator;
			tmp->size = step->levels->multidval.dim_sizes[0];
			if (ct->shname) {
				sprintf(buffer, "lv_%s%d", ct->shname,therec->n_lv_dims);
			}
			else {
				sprintf(buffer, "levels%d", therec->n_lv_dims);
			}
			tmp->dim_name = NrmStringToQuark(buffer);
			therec->total_dims++;
			ptr = (Grib2DimInqRecList *) NclMalloc((unsigned) sizeof(Grib2DimInqRecList));
			ptr->dim_inq = tmp;
			ptr->next = therec->lv_dims;
			therec->lv_dims = ptr;
			therec->n_lv_dims++;
			step->var_info.file_dim_num[current_dim] = tmp->dim_number;
			att_list_ptr = NULL;

			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			if(ct->units) {
				*tmp_string = NrmStringToQuark(ct->units);
			} else {
				*tmp_string = NrmStringToQuark("unknown");
			}
			Grib2PushAtt(&att_list_ptr,"units",tmp_string,1,nclTypestringClass); 

			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			if(ct->descrip) {
				*tmp_string = NrmStringToQuark(ct->descrip);
			} else {
				*tmp_string = NrmStringToQuark("unknown");
			}
			Grib2PushAtt(&att_list_ptr,"long_name",tmp_string,1,nclTypestringClass); 

			_Grib2AddInternalVar(therec,tmp->dim_name, &tmp->dim_number,
					     (NclMultiDValData) step->levels, att_list_ptr, 2);
			step->levels = NULL;
		} else {
			step->var_info.file_dim_num[current_dim] = dstep->dim_inq->dim_number;
			_NclDestroyObj((NclObj)step->levels);
			step->levels = NULL;
		}
		current_dim++;
	} else if ((!step->levels_isatt) && (step->levels0 != NULL) && (step->levels1 != NULL)) {
		dstep = therec->lv_dims;
		for (i = 0; i < therec->n_lv_dims; i++) {
			/* using the grid_number member to store the level indicator */
			if (dstep->dim_inq->grid_number == step->level_indicator &&
			    dstep->dim_inq->size == step->levels0->multidval.dim_sizes[0]) {
				sprintf(name_buffer,"%s%s",NrmQuarkToString(dstep->dim_inq->dim_name),"_l0");
				tmp_md = _Grib2GetInternalVar(therec,NrmStringToQuark(name_buffer),&test);
				sprintf(name_buffer,"%s%s",NrmQuarkToString(dstep->dim_inq->dim_name),"_l1");
				tmp_md1 = _Grib2GetInternalVar(therec,NrmStringToQuark(name_buffer),&test);
				if ((tmp_md != NULL )&&(tmp_md1 != NULL) ) {
					lhs = (int*)tmp_md->multidval.val;
					rhs = (int*)step->levels0->multidval.val;
					lhs1 = (int*)tmp_md1->multidval.val;
					rhs1 = (int*)step->levels1->multidval.val;
					j = 0;
					while (j<dstep->dim_inq->size) {
						if ((lhs[j] != rhs[j])||(lhs1[j] != rhs1[j])) {
							break;
						} else {
							j++;
						}
					}
					if (j == dstep->dim_inq->size) {
						break;
					} else {
						dstep= dstep->next;
					}
				} else {
					dstep= dstep->next;
				}
			} else {
				dstep = dstep->next;
			}
		}
		if (dstep == NULL) {
			/* Need a new dimension entry w name and number */
			Grib2ReadCodeTable(g2inqrec->table_source, 4, 
					   "4.5.table",g2inqrec->level_indicator,-1,ct);
			tmp = (Grib2DimInqRec*)NclMalloc((unsigned)sizeof(Grib2DimInqRec));
			tmp->gds = NULL;
			tmp->dim_number = therec->total_dims;
			tmp->grid_number = step->level_indicator;
			tmp->size = step->levels0->multidval.dim_sizes[0];
			if (ct->shname) {
				sprintf(buffer, "lv_%s%d", ct->shname,therec->n_lv_dims);
			}
			else {
				sprintf(buffer, "levels%d", therec->n_lv_dims);
			}
			tmp->dim_name = NrmStringToQuark(buffer);
			therec->total_dims++;
			ptr = (Grib2DimInqRecList*)NclMalloc((unsigned)sizeof(Grib2DimInqRecList));
			ptr->dim_inq = tmp;
			ptr->next = therec->lv_dims;
			therec->lv_dims = ptr;
			therec->n_lv_dims++;
			step->var_info.file_dim_num[current_dim] = tmp->dim_number;
			sprintf(name_buffer,"%s%s",buffer,"_l0");
			
			att_list_ptr = NULL;
			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			if(ct->units) {
				*tmp_string = NrmStringToQuark(ct->units);
			} else {
				*tmp_string = NrmStringToQuark("unknown");
			}
			Grib2PushAtt(&att_list_ptr,"units",tmp_string,1,nclTypestringClass); 

			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			if(ct->descrip) {
				*tmp_string = NrmStringToQuark(ct->descrip);
			} else {
				*tmp_string = NrmStringToQuark("unknown");
			}
			Grib2PushAtt(&att_list_ptr,"long_name",tmp_string,1,nclTypestringClass); 

			_Grib2AddInternalVar(therec,NrmStringToQuark(name_buffer),
					     &tmp->dim_number,(NclMultiDValData)step->levels0,att_list_ptr,2);

			sprintf(name_buffer,"%s%s",buffer,"_l1");
			att_list_ptr = NULL;
			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			if(ct->units) {
				*tmp_string = NrmStringToQuark(ct->units);
			} else {
				*tmp_string = NrmStringToQuark("unknown");
			}
			Grib2PushAtt(&att_list_ptr,"units",tmp_string,1,nclTypestringClass); 

			tmp_string = (NclQuark*)NclMalloc(sizeof(NclQuark));
			if(ct->descrip) {
				*tmp_string = NrmStringToQuark(ct->descrip);
			} else {
				*tmp_string = NrmStringToQuark("unknown");
			}
			Grib2PushAtt(&att_list_ptr,"long_name",tmp_string,1,nclTypestringClass); 

			_Grib2AddInternalVar(therec,NrmStringToQuark(name_buffer),
					     &tmp->dim_number,(NclMultiDValData)step->levels1,att_list_ptr,2);


			att_list_ptr = NULL;
			step->levels0 = NULL;
			step->levels1 = NULL;
		} else {
			step->var_info.file_dim_num[current_dim] = dstep->dim_inq->dim_number;
			_NclDestroyObj((NclObj)step->levels0);
			_NclDestroyObj((NclObj)step->levels1);
			step->levels0 = NULL;
			step->levels1 = NULL;
		}
		current_dim++;
        }

        /*
         * Now its time to get the grid coordinates  and define grid variables
         * First switch on whether record has GDS or not
         * if not :
         *  check to see if dimensions are defined
         *  if not: check get_grid field
         *      then get_grid.
         *      if its more that 1D then
         *          define gridx_### and gridy_### and then add variables gridlat_###
         *          and gridlon_### if gridx and gridy are 
         *      else
         *          define lat_### and lon_### and add them as variables of the same
         *          name (real coordinate variables)
         */
        (void) sprintf(buffer,"ygrid_%d",step->grid_number);
        ygrid_q = NrmStringToQuark(buffer);
        (void) sprintf(buffer,"lat_%d",step->grid_number);
        lat_q = NrmStringToQuark(buffer);

	dstep = therec->grid_dims;
	while (dstep != NULL) {
		if (dstep->dim_inq->grid_number != step->grid_number) {
			dstep = dstep->next;
			continue;
		}
		if (dstep->dim_inq->gds->len_grid_template !=
		    step->gds->len_grid_template) {
			dstep = dstep->next;
			continue;
		}
		if (memcmp(dstep->dim_inq->gds->grid_template, 
			   step->gds->grid_template, 
			   sizeof(int) * dstep->dim_inq->gds->len_grid_template)) {
			dstep = dstep->next;
			continue;
		}
		break;
	}		    
    
	if (dstep) {
		Grib2InternalVarList	*iv;
		int dnum1, dnum2;
		int count = 0;
		step->grid_index = dstep->dim_inq->grid_index;
		step->gds->is_thinned_grid = dstep->dim_inq->gds->is_thinned_grid;
		step->gds->scan_mode_offset = dstep->dim_inq->gds->scan_mode_offset;
		if(dstep->dim_inq->grid_number==50) {
			step->var_info.dim_sizes[current_dim+1] = dstep->dim_inq->size;
			dnum1 = step->var_info.file_dim_num[current_dim+1] = dstep->dim_inq->dim_number;
			step->var_info.dim_sizes[current_dim+2] = dstep->next->dim_inq->size;
			dnum2 = step->var_info.file_dim_num[current_dim+2] = dstep->next->dim_inq->dim_number;
			step->var_info.dim_sizes[current_dim] = 2;
			step->var_info.file_dim_num[current_dim] = dstep->dim_inq->dim_number-1;
			step->var_info.doff = 2;
		} else {
			step->var_info.dim_sizes[current_dim] = dstep->dim_inq->size;
			dnum1 = step->var_info.file_dim_num[current_dim] = dstep->dim_inq->dim_number;
			step->var_info.dim_sizes[current_dim+1] = dstep->next->dim_inq->size;
			dnum2 = step->var_info.file_dim_num[current_dim+1] = dstep->next->dim_inq->dim_number;
			step->var_info.doff = 1;
		}
		/* find the auxiliary coordinate variables if they exist */
		for (iv = therec->internal_var_list; iv != NULL; iv = iv->next) {
			if (iv->int_var->var_info.num_dimensions != 2)
				continue;
			if ( !(iv->int_var->var_info.file_dim_num[0] == dnum1 &&
			       iv->int_var->var_info.file_dim_num[1] == dnum2)) 
				continue;
			if (strstr(NrmQuarkToString(iv->int_var->var_info.var_name_quark),"rot"))
				continue;
			step->aux_coords[count] = iv->int_var->var_info.var_name_quark;
			count++;
			if (count == 2) {
				break;
			}
		}
		/* adjust the variable name */
		switch (step->grid_number) {
		case 0:
			name_suffix = "LL";
			break;
		case 10:
			name_suffix = "ME";
			break;

		case 20:
			name_suffix = "ST";
			break;

		case 30:
			name_suffix = "LC";
			break;

		case 40:
			name_suffix = "GA";
			break;

		case 50:
			name_suffix = "SH";
			break;
		case 90:
			name_suffix = "SV";
			break;
		default:
			break;
		}
		sprintf(buffer,"%s_G%s%d",NrmQuarkToString(step->var_info.var_name_quark),name_suffix,step->grid_index);
		step->var_info.var_name_quark = NrmStringToQuark(buffer);
	}
	else {
		nlonatts = 0;
		nlatatts = 0;
		nrotatts = 0;
		lat_att_list_ptr = NULL;
		lon_att_list_ptr = NULL;
		rot_att_list_ptr = NULL;
		tmp_rot = NULL;
		tmp_lon = NULL;
		tmp_lat = NULL;
		n_dims_lat = 0;
		n_dims_lon = 0;
		dimsizes_lat = NULL;
		dimsizes_lon = NULL;

		/*
		 * Grid has not been defined
		 */
		switch (step->grid_number) {
		case 0:
			g2GDSCEGrid(step, &tmp_lat, &n_dims_lat, &dimsizes_lat, &tmp_lon,
				    &n_dims_lon,&dimsizes_lon, &tmp_rot, &n_dims_rot, &dimsizes_rot,
				    &lat_att_list_ptr, &nlatatts, &lon_att_list_ptr, &nlonatts,
				    &rot_att_list_ptr, &nrotatts);
			name_suffix = "LL";
			break;

		case 1:
			g2GDSRLLGrid(step, &tmp_lat, &n_dims_lat, &dimsizes_lat, &tmp_lon,
				    &n_dims_lon,&dimsizes_lon, &tmp_rot, &n_dims_rot, &dimsizes_rot,
				    &lat_att_list_ptr, &nlatatts, &lon_att_list_ptr, &nlonatts,
				    &rot_att_list_ptr, &nrotatts);
			name_suffix = "RLL";
			break;
		case 32769:
			g2GDSArakawaRLLGrid(step, &tmp_lat, &n_dims_lat, &dimsizes_lat, &tmp_lon,
					    &n_dims_lon,&dimsizes_lon, &tmp_rot, &n_dims_rot, &dimsizes_rot,
					    &lat_att_list_ptr, &nlatatts, &lon_att_list_ptr, &nlonatts,
					    &rot_att_list_ptr, &nrotatts);
			name_suffix = "RLL";
			break;
		case 10:
			/* Mercator (Template 3.10) */
			g2GDSMEGrid(step, &tmp_lat, &n_dims_lat, &dimsizes_lat, &tmp_lon,
				    &n_dims_lon,&dimsizes_lon, &tmp_rot, &n_dims_rot, &dimsizes_rot,
				    &lat_att_list_ptr, &nlatatts, &lon_att_list_ptr, &nlonatts,
				    &rot_att_list_ptr, &nrotatts);
			name_suffix = "ME";
			break;

		case 20:
			/* Polar Stereographic Projection (North or South) */
			/* Template 3.20 */

			g2GDSSTGrid(step, &tmp_lat, &n_dims_lat, &dimsizes_lat, &tmp_lon,
				    &n_dims_lon,&dimsizes_lon, &tmp_rot, &n_dims_rot, &dimsizes_rot,
				    &lat_att_list_ptr, &nlatatts, &lon_att_list_ptr, &nlonatts,
				    &rot_att_list_ptr, &nrotatts);
			name_suffix = "ST";
			break;

		case 30:
			/* Lambert Conformal (secant, tangent, conical or biploar */
			/*  Template 3.30 */
			g2GDSLCGrid(step, &tmp_lat, &n_dims_lat, &dimsizes_lat, &tmp_lon,
				    &n_dims_lon,&dimsizes_lon, &tmp_rot, &n_dims_rot, &dimsizes_rot,
				    &lat_att_list_ptr, &nlatatts, &lon_att_list_ptr, &nlonatts,
				    &rot_att_list_ptr, &nrotatts);
			name_suffix = "LC";
			break;

		case 40:
			/* Gaussian Latitude/Longitude   Template 3.40 */
			g2GDSGAGrid(step, &tmp_lat, &n_dims_lat, &dimsizes_lat, &tmp_lon,
				    &n_dims_lon,&dimsizes_lon, &tmp_rot, &n_dims_rot, &dimsizes_rot,
				    &lat_att_list_ptr, &nlatatts, &lon_att_list_ptr, &nlonatts,
				    &rot_att_list_ptr, &nrotatts);
			name_suffix = "GA";
			break;

		case 50:
			/* Spherical Harmonic coefficients */
			g2GDSSHGrid(step, &tmp_lat, &n_dims_lat, &dimsizes_lat, &tmp_lon,
				    &n_dims_lon,&dimsizes_lon, &tmp_rot, &n_dims_rot, &dimsizes_rot,
				    &lat_att_list_ptr, &nlatatts, &lon_att_list_ptr, &nlonatts,
				    &rot_att_list_ptr, &nrotatts);
			name_suffix = "SH";


			if (n_dims_lat == 0) {
				is_err = NhlFATAL;
			}
			break;

		case 90:
			g2GDSSVGrid(step, &tmp_lat, &n_dims_lat, &dimsizes_lat, &tmp_lon,
					 &n_dims_lon,&dimsizes_lon, &tmp_rot, &n_dims_rot, &dimsizes_rot,
				    &lat_att_list_ptr, &nlatatts, &lon_att_list_ptr, &nlonatts,
				    &rot_att_list_ptr, &nrotatts);
			name_suffix = "SV";
			if (n_dims_lat == 0) {
				is_err = NhlFATAL;
			}
			else {
				NhlPError(NhlWARNING,NhlEUNKNOWN,
					  "NCL does not yet fully support GRIB2 space view perspective grid (template %d), no coordinate variables will be supplied for this grid",
					  step->grid_number);
				is_err = NhlWARNING;
			}
			break;
		case 2:
		case 3:
		case 31:
		case 41:
		case 42:
		case 43:
		case 110:
		case 204:
			/* 
			 * We do not have code to produce coordinates for these grids, but at least
			 * we can know the data dimensions and can therefore provide the data.
			 */
			g2GDSDimsOnlyGrid(step, &tmp_lat, &n_dims_lat, &dimsizes_lat, &tmp_lon,
					 &n_dims_lon,&dimsizes_lon, &tmp_rot, &n_dims_rot, &dimsizes_rot,
				    &lat_att_list_ptr, &nlatatts, &lon_att_list_ptr, &nlonatts,
				    &rot_att_list_ptr, &nrotatts);
			
			if (n_dims_lat == 0) {
				is_err = NhlFATAL;
			}
			else if (step->grid_number == 204) {
				/* this is the curvilinear orthogonal grid -- in NCL-speak this requires 2D coordinates, but there is no specific projection 
				   that allows coordinates to be calculated. A user either needs to get the coordinates from some external source or they must
				   be present in the file as variables in their own right. So there is no error here. */
				break;
			}
			else {
				NhlPError(NhlWARNING,NhlEUNKNOWN,
					  "NCL does not yet fully support GRIB2 Grid template %d, no coordinate variables will be supplied for this grid",
					  step->grid_number);
				is_err = NhlWARNING;
			}
			
			break;
			
		default:
			is_err = NhlFATAL;
		}
		if (is_err < NhlWARNING) {
			NhlPError(NhlFATAL,NhlEUNKNOWN,
				  "NclGRIB2: Deleting reference to parameter; unable to decode grid template 3.%d",step->grid_number);
			is_err = NhlNOERROR;
			if(last != NULL) {
				last->next = step->next;
			} else {
				therec->var_list = step->next;
			}
			tmpstep = step;
			step = step->next;
			_Grib2FreeParamRec(tmpstep);
			therec->n_vars--;
			continue;
		}
		
	
		step->grid_index = therec->n_grids;
		_g2NewGridCache(therec,step->grid_index, step->grid_number, 
				   n_dims_lat, dimsizes_lat, n_dims_lon,dimsizes_lon);

		/*
		 * Grids always need to be inserted into the grid_dim list in the
		 * right order. First lon is pushed then lat so that dstep->dim_inq
		 * always points to lat and dstep->next->dim_inq point to lon
		 */
		m = 0;
		while ((m < step->n_entries) && (step->thelist[m].rec_inq == NULL))
			m++;

		/* add info about the Grid to the variable name */
		sprintf(buffer, "%s_G%s%d",NrmQuarkToString(step->var_info.var_name_quark),name_suffix,step->grid_index);
		step->var_info.var_name_quark = NrmStringToQuark(buffer);

		if ((n_dims_lon == 1) && (n_dims_lat == 1)) {
			if (step->grid_number == 50) {
				step->var_info.dim_sizes[current_dim] = 2;
				step->var_info.dim_sizes[current_dim + 1] = dimsizes_lat[0];
				step->var_info.dim_sizes[current_dim + 2] = dimsizes_lon[0];
				step->var_info.file_dim_num[current_dim] = therec->total_dims;
				step->var_info.file_dim_num[current_dim + 1] = therec->total_dims + 1;
				step->var_info.file_dim_num[current_dim + 2] = therec->total_dims + 2;
				/* create the "real_imaginary" dimension */
				sprintf(buffer, "real_imaginary");
				tmp = (Grib2DimInqRec*)NclMalloc((unsigned) sizeof(Grib2DimInqRec));
				tmp->dim_number = therec->total_dims;
				tmp->size = 2;
				tmp->dim_name = NrmStringToQuark(buffer);
				tmp->grid_number = step->grid_number;
				tmp->grid_index = therec->n_grids;
				tmp->gds = Grib2DupGDS(step->gds);
				ptr = (Grib2DimInqRecList*)NclMalloc((unsigned)sizeof(Grib2DimInqRecList));
				ptr->dim_inq= tmp;
				ptr->next = therec->grid_dims;
				therec->grid_dims = ptr;
				therec->n_grid_dims++;
				step->var_info.doff = 2;
			} else {
				step->var_info.dim_sizes[current_dim] = dimsizes_lat[0];
				step->var_info.dim_sizes[current_dim+1] = dimsizes_lon[0];
				step->var_info.file_dim_num[current_dim] = therec->total_dims;
				step->var_info.file_dim_num[current_dim+1] = therec->total_dims + 1;
				step->var_info.doff = 1;
			}

			/*
			 * x (longitude) first
			 */
			if (tmp_lon) {
				sprintf(buffer,"lon_%d",therec->n_grids);
			}
			else {
				sprintf(buffer, "xgrid_%d", therec->n_grids);
			}

			tmp = (Grib2DimInqRec*)NclMalloc((unsigned)sizeof(Grib2DimInqRec));
			tmp->dim_number = therec->total_dims + step->var_info.doff;
			tmp->size = dimsizes_lon[0];
			tmp->dim_name = NrmStringToQuark(buffer);
			tmp->grid_number = step->grid_number;
			tmp->grid_index = therec->n_grids;
			tmp->gds = Grib2DupGDS(step->gds);
			ptr = (Grib2DimInqRecList *) NclMalloc((unsigned) sizeof(Grib2DimInqRecList));
			ptr->dim_inq= tmp;
			ptr->next = therec->grid_dims;
			therec->grid_dims = ptr;
			therec->n_grid_dims++;

			if (tmp_lon != NULL) {	
				_Grib2AddInternalVar(therec, tmp->dim_name, &tmp->dim_number,
						     (NclMultiDValData)_NclCreateVal(
							     NULL,
							     NULL,
							     Ncl_MultiDValData,
							     0,
							     (void *) tmp_lon,
							     NULL,
							     n_dims_lon,
							     dimsizes_lon,
							     TEMPORARY,
							     NULL,
							     nclTypefloatClass),
						     lon_att_list_ptr,nlonatts);
			}

			NclFree(dimsizes_lon);

			if (tmp_lat) {
				sprintf(buffer,"lat_%d",therec->n_grids);
			}
			else {
				sprintf(buffer, "ygrid_%d", therec->n_grids);
			}
			tmp = (Grib2DimInqRec*)NclMalloc((unsigned)sizeof(Grib2DimInqRec));
			tmp->dim_number = therec->total_dims + (step->var_info.doff - 1);
			tmp->size = dimsizes_lat[0];
			tmp->dim_name = NrmStringToQuark(buffer);
			tmp->grid_number = step->grid_number;
			tmp->grid_index = therec->n_grids;
			tmp->gds = Grib2DupGDS(step->gds);

			ptr = (Grib2DimInqRecList*)NclMalloc((unsigned)sizeof(Grib2DimInqRecList));
			ptr->dim_inq= tmp;
			ptr->next = therec->grid_dims;
			therec->grid_dims = ptr;
			therec->n_grid_dims++;

			if(tmp_lat != NULL) {
				_Grib2AddInternalVar(therec,tmp->dim_name,&tmp->dim_number,
						     (NclMultiDValData)_NclCreateVal(
							     NULL,
							     NULL,
							     Ncl_MultiDValData,
							     0,
							     (void*)tmp_lat,
							     NULL,
							     n_dims_lat,
							     dimsizes_lat,
							     TEMPORARY,
							     NULL,
							     nclTypefloatClass),
						     lat_att_list_ptr,nlatatts);
			}
			NclFree(dimsizes_lat);
			therec->total_dims += step->var_info.doff+1;
			therec->n_grids++;

		} else if ((n_dims_lon == 2) && (n_dims_lat == 2)
			   && (dimsizes_lat[0] == dimsizes_lon[0])
			   && (dimsizes_lat[1] == dimsizes_lon[1])) {
			step->var_info.dim_sizes[current_dim] = dimsizes_lat[0];
			step->var_info.dim_sizes[current_dim + 1] = dimsizes_lon[1];
			step->var_info.file_dim_num[current_dim] = therec->total_dims;
			step->var_info.file_dim_num[current_dim + 1] = therec->total_dims + 1;
			step->var_info.doff=1;

			sprintf(buffer, "xgrid_%d", therec->n_grids);

			tmp = (Grib2DimInqRec*)NclMalloc((unsigned)sizeof(Grib2DimInqRec));
			tmp->dim_number = therec->total_dims + 1;
			tmp->size = dimsizes_lon[1];
			tmp->dim_name = NrmStringToQuark(buffer);
			tmp->grid_number = step->grid_number;
			tmp->grid_index = therec->n_grids;
			tmp->gds = Grib2DupGDS(step->gds);

			ptr = (Grib2DimInqRecList*)NclMalloc((unsigned)sizeof(Grib2DimInqRecList));
			ptr->dim_inq= tmp;
			ptr->next = therec->grid_dims;
			therec->grid_dims = ptr;
			therec->n_grid_dims++;
			sprintf(buffer,"gridlon_%d",therec->n_grids);
			step->aux_coords[1] = NrmStringToQuark(buffer);
			tmp_file_dim_numbers[0] = therec->total_dims;
			tmp_file_dim_numbers[1] = therec->total_dims+ 1;

			tmp_float = NclMalloc((unsigned)sizeof(float)*4);
			tmp_float[0] = tmp_lon[0];
			tmp_float[1] = tmp_lon[dimsizes_lon[1]-1];
			tmp_float[3] = tmp_lon[(dimsizes_lon[0]-1) * dimsizes_lon[1]];
			tmp_float[2] = tmp_lon[(dimsizes_lon[0] * dimsizes_lon[1])-1];
			Grib2PushAtt(&lon_att_list_ptr,"corners",tmp_float,4,nclTypefloatClass); nlonatts++;

			_Grib2AddInternalVar(therec,NrmStringToQuark(buffer),tmp_file_dim_numbers,
					     (NclMultiDValData)_NclCreateVal(
						     NULL,
						     NULL,
						     Ncl_MultiDValData,
						     0,
						     (void*)tmp_lon,
						     NULL,
						     n_dims_lon,
						     dimsizes_lon,
						     TEMPORARY,
						     NULL,
						     nclTypefloatClass),
					     lon_att_list_ptr,nlonatts);
			NclFree(dimsizes_lon);
			sprintf(buffer,"ygrid_%d",therec->n_grids);

			tmp = (Grib2DimInqRec*)NclMalloc((unsigned)sizeof(Grib2DimInqRec));
			tmp->dim_number = therec->total_dims;
			tmp->size = dimsizes_lat[0];
			tmp->dim_name = NrmStringToQuark(buffer);
			tmp->grid_number = step->grid_number;
			tmp->grid_index = therec->n_grids;
			tmp->gds = Grib2DupGDS(step->gds);

			ptr = (Grib2DimInqRecList*)NclMalloc((unsigned)sizeof(Grib2DimInqRecList));
			ptr->dim_inq= tmp;
			ptr->next = therec->grid_dims;
			therec->grid_dims = ptr;
			therec->n_grid_dims++;
			sprintf(buffer,"gridlat_%d",therec->n_grids);
			step->aux_coords[0] = NrmStringToQuark(buffer);
			tmp_float = NclMalloc((unsigned)sizeof(float)*4);
			tmp_float[0] = tmp_lat[0];
			tmp_float[1] = tmp_lat[dimsizes_lat[1]-1];
			tmp_float[3] = tmp_lat[(dimsizes_lat[0]-1) * dimsizes_lat[1]];
			tmp_float[2] = tmp_lat[(dimsizes_lat[0] * dimsizes_lat[1])-1];
			Grib2PushAtt(&lat_att_list_ptr,"corners",tmp_float,4,nclTypefloatClass); nlatatts++;

			_Grib2AddInternalVar(therec,
					     NrmStringToQuark(buffer),
					     tmp_file_dim_numbers,
					     (NclMultiDValData)_NclCreateVal(
						     NULL,
						     NULL,
						     Ncl_MultiDValData,
						     0,
						     (void*)tmp_lat,
						     NULL,
						     n_dims_lat,
						     dimsizes_lat,
						     TEMPORARY,
						     NULL,
						     nclTypefloatClass),
					     lat_att_list_ptr,
					     nlatatts);
			therec->total_dims += 2;
			if (tmp_rot != NULL) {
				/* the rotation array is assumed to be the same size as the lat and lon arrays */
				sprintf(buffer,"gridrot_%d",therec->n_grids);
				_Grib2AddInternalVar(therec,
						     NrmStringToQuark(buffer),
						     tmp_file_dim_numbers,
						     (NclMultiDValData)_NclCreateVal(
							     NULL,
							     NULL,
							     Ncl_MultiDValData,
							     0,
							     (void*)tmp_rot,
							     NULL,
							     n_dims_lat,
							     dimsizes_lat,
							     TEMPORARY,
							     NULL,
							     nclTypefloatClass),
						     rot_att_list_ptr,
						     nrotatts);
			}
			NclFree(dimsizes_lat);
			therec->n_grids++;
		} else {
			NhlPError(NhlFATAL,NhlEUNKNOWN,
				  "NclGRIB2: Couldn't handle dimension information returned by grid decoding");
			is_err = NhlFATAL;
		}
	}
	if(is_err > NhlFATAL) {
		last = step;	
		step = step->next;
	} else {
		NhlPError(NhlFATAL,NhlEUNKNOWN,
			  "NclGRIB2: Deleting reference to parameter because of decoding error");
		is_err = NhlNOERROR;
		if(last != NULL) {
			last->next = step->next;
		} else {
			therec->var_list = step->next;
		}
		tmpstep = step;
		step = step->next;
		_Grib2FreeParamRec(tmpstep);
		therec->n_vars--;
	}
    }

    _g2CreateSupplementaryTimeVariables(therec);
    Grib2FreeCodeTableRec(ct);

    return;
}



static void Grib2PrintRecords
# if    NhlNeedProto
(G2Rec **g2rec)
# else
(g2rec)
    G2Rec   **g2rec;
# endif
{
    int i = 0,
        j = 0,
        nr = 0;
    int k;

    nr = g2rec[0]->numrecs;

    fprintf(stdout, "\nGRIB2 Records (total: %d)\n\n", nr);
    
    for (i = 0; i < nr; i++) {
        fprintf(stdout, "GRIB v2 record # %d (of %d)\n\n", i + 1, nr);

        fprintf(stdout, "Offset: %ld\n", (long) g2rec[i]->offset);
        fprintf(stdout, "Record Size: %d\n\n", g2rec[i]->rec_size);

        fprintf(stdout, "Section 0\n");
        fprintf(stdout, "\t section ID: %d\n", g2rec[i]->sec0.secid);
        fprintf(stdout, "\t discipline: %d\n", g2rec[i]->sec0.discipline);
        fprintf(stdout, "\t edition: %d\n", g2rec[i]->sec0.edition);
        fprintf(stdout, "\t msglen: %d\n", g2rec[i]->sec0.msglen);
        fprintf(stdout, "\n");
    
        fprintf(stdout, "Section 1\n");
        fprintf(stdout, "\t section ID: %d\n", g2rec[i]->sec1.secid);
        fprintf(stdout, "\t center ID: %d\n", g2rec[i]->sec1.centerID);
        fprintf(stdout, "\t center name: %s\n", g2rec[i]->sec1.center_name);
        if (g2rec[i]->sec1.subcenterID >= 0)
            fprintf(stdout, "\t subcenter ID: %d\n", g2rec[i]->sec1.subcenterID);
        if (g2rec[i]->sec1.subcenter_name != NULL)
            fprintf(stdout, "\t subcenter name: %s\n", g2rec[i]->sec1.subcenter_name);
        fprintf(stdout, "\t master table version: %d\n", g2rec[i]->sec1.master_table_ver);
        fprintf(stdout, "\t local table version: %d\n", g2rec[i]->sec1.local_table_ver);
        fprintf(stdout, "\t ref time: %d\n", g2rec[i]->sec1.ref_time);
        fprintf(stdout, "\t sig ref time: %s\n", g2rec[i]->sec1.sig_ref_time);

        fprintf(stdout, "\t\t date: %d %d %d : %d %d %d\n",
            g2rec[i]->sec1.date_time.year, g2rec[i]->sec1.date_time.mon,
            g2rec[i]->sec1.date_time.day, g2rec[i]->sec1.date_time.hour,
            g2rec[i]->sec1.date_time.min, g2rec[i]->sec1.date_time.sec);
        fprintf(stdout, "\t\t data date: %ld\n", g2rec[i]->sec1.date_time.dataDate);
        fprintf(stdout, "\t\t data time: %d\n", g2rec[i]->sec1.date_time.dataTime);

        fprintf(stdout, "\t production status: %d\n", g2rec[i]->sec1.prod_status);
        fprintf(stdout, "\t proc production status: %s\n", g2rec[i]->sec1.proc_prod_status);
        fprintf(stdout, "\t data type: %d\n", g2rec[i]->sec1.data_type);
        if (g2rec[i]->sec1.proc_data_type != NULL)
            fprintf(stdout, "\t proc data type: %s\n", g2rec[i]->sec1.proc_data_type);
        fprintf(stdout, "\n");

        fprintf(stdout, "Number of repeated fields: %d\n", g2rec[i]->num_rptd);
        for (j = 0; j < g2rec[i]->num_rptd; j++) {
            fprintf(stdout, "Section 2\n");
            fprintf(stdout, "\t section ID: %d\n", g2rec[i]->sec2[j]->secid);
            fprintf(stdout, "\t local len: %d\n", g2rec[i]->sec2[j]->locallen);
            fprintf(stdout, "\n");

            fprintf(stdout, "Section 3\n");
            fprintf(stdout, "\t section ID: %d\n", g2rec[i]->sec3[j]->secid);
            fprintf(stdout, "\t grid def src: %d\n", g2rec[i]->sec3[j]->grid_def_src);
            fprintf(stdout, "\t grid def name: %s\n", g2rec[i]->sec3[j]->grid_def_name);
            fprintf(stdout, "\t num grid data pts: %d\n", g2rec[i]->sec3[j]->num_grid_data_pts);
            fprintf(stdout, "\t num oct opt: %d\n", g2rec[i]->sec3[j]->num_oct_opt);
            fprintf(stdout, "\t interp opt num pts: %d\n", g2rec[i]->sec3[j]->interp_opt_num_pts);
            fprintf(stdout, "\t interp opt name: %s\n", g2rec[i]->sec3[j]->interp_opt_name);
            fprintf(stdout, "\t grid def templ num: %d\n", g2rec[i]->sec3[j]->grid_def_templ_num);
            fprintf(stdout, "\t length of grid template: %d\n", g2rec[i]->sec3[j]->len_grid_template);
	    for (k = 0; k < g2rec[i]->sec3[j]->len_grid_template - 4; k+=5) {
		    fprintf(stdout, "\t grid template[%d] %d %d %d %d %d\n", k, 
			    g2rec[i]->sec3[j]->grid_template[k],
			    g2rec[i]->sec3[j]->grid_template[k+1],
			    g2rec[i]->sec3[j]->grid_template[k+2],
			    g2rec[i]->sec3[j]->grid_template[k+3],
			    g2rec[i]->sec3[j]->grid_template[k+4]);
	    }
	    if (k < g2rec[i]->sec3[j]->len_grid_template) {
		    fprintf(stdout, "\t grid template[%d]", k);
		    for (; k < g2rec[i]->sec3[j]->len_grid_template; k++) {
			    fprintf(stdout, " %d",g2rec[i]->sec3[j]->grid_template[k]);
		    } 
		    fprintf(stdout, "\n");
	    }
#if 0		    
	    

            fprintf(stdout, "\t Shape of Earth info:\n");
            fprintf(stdout, "\t\t shape of earth: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->shapeOfEarth);
/*
            fprintf(stdout, "\t\t earth shape: %s\n",
                g2rec[i]->sec3[j]->shape_of_earth->earthShape);
*/
            fprintf(stdout, "\t\t scale factor rad sph earth: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->scale_factor_rad_sph_earth);
            fprintf(stdout, "\t\t scaled val rad sph earth: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->scaled_val_rad_sph_earth);
            fprintf(stdout, "\t\t scale factor major exis obl sph earth: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->scale_factor_maj_axis_obl_sph_earth);
            fprintf(stdout, "\t\t scaled val maj axis obl sph earth: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->scaled_val_maj_axis_obl_sph_earth);
            fprintf(stdout, "\t\t scale factor min axis obl sph earth: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->scale_factor_min_axis_obl_sph_earth);
            fprintf(stdout, "\t\t scaled val min axis obl sph earth: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->scaled_val_min_axis_obl_sph_earth);
            fprintf(stdout, "\t\t npts along parallel: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->npts_along_parallel);
            fprintf(stdout, "\t\t npts along meridian: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->npts_along_meridian);
            fprintf(stdout, "\t\t anl init prod domain: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->angl_init_prod_domain);
            fprintf(stdout, "\t\t subdiv basic angle: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->subdiv_basic_angle);
            fprintf(stdout, "\t\t lat first gridpt: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->lat_first_gridpt);
            fprintf(stdout, "\t\t lat last gridpt: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->lat_last_gridpt);
            fprintf(stdout, "\t\t lon first gridpt: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->lon_first_gridpt);
            fprintf(stdout, "\t\t lon last gridpt: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->lon_last_gridpt);
            fprintf(stdout, "\t\t idir incr: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->idir_incr);
            fprintf(stdout, "\t\t jdir incr: %d\n",
                g2rec[i]->sec3[j]->shape_of_earth->jdir_incr);
            fprintf(stdout, "\t\t idir incr scaled: %.3f\n",
                g2rec[i]->sec3[j]->shape_of_earth->idir_incr_scaled);
            fprintf(stdout, "\t\t jdir incr scaled: %.3f\n",
                g2rec[i]->sec3[j]->shape_of_earth->jdir_incr_scaled);

            fprintf(stdout, "\t Resolution Component Flags info:\n");
            fprintf(stdout, "\t\t i dir: %hd\n", g2rec[i]->sec3[j]->res_comp->idir_given);
            fprintf(stdout, "\t\t j dir: %hd\n", g2rec[i]->sec3[j]->res_comp->jdir_given);
            fprintf(stdout, "\t\t uv vectors: %hd\n", g2rec[i]->sec3[j]->res_comp->uv_vectors);

            fprintf(stdout, "\t Scan Mode Flags\n");
            fprintf(stdout, "\t\t first rowcol idir: %hd\n",
                g2rec[i]->sec3[j]->scan_mode->idir);
            fprintf(stdout, "\t\t first rowcol jdir: %hd\n",
                g2rec[i]->sec3[j]->scan_mode->jdir);
            fprintf(stdout, "\t\t adj ijdir consec: %hd\n",
                g2rec[i]->sec3[j]->scan_mode->adj_ijdir_consec);
            fprintf(stdout, "\t\t scan dir: %hd\n",
                g2rec[i]->sec3[j]->scan_mode->scan_dir);

            fprintf(stdout, "\t lat first gridpt: %3.0f\n", g2rec[i]->sec3[j]->lat_first_gridpt);
            fprintf(stdout, "\t lon first gridpt: %3.0f\n", g2rec[i]->sec3[j]->lon_first_gridpt);
            fprintf(stdout, "\t lat last gridpt: %3.0f\n", g2rec[i]->sec3[j]->lat_last_gridpt);
            fprintf(stdout, "\t lon last gridpt: %3.0f\n", g2rec[i]->sec3[j]->lon_last_gridpt);
#endif
            fprintf(stdout, "\t grid list num oct num: %d\n",
                g2rec[i]->sec3[j]->grid_list_num_oct_num);
            fprintf(stdout, "\n");

            fprintf(stdout, "Section 4:\n");
            fprintf(stdout, "\t section ID: %d\n", g2rec[i]->sec4[j]->secid);
            fprintf(stdout, "\t pds num: %d\n", g2rec[i]->sec4[j]->pds_num);
            fprintf(stdout, "\t prod def name: %s\n", g2rec[i]->sec4[j]->prod_def_name);

            fprintf(stdout, "\t Product Parameters:\n");
            fprintf(stdout, "\t\t param cat: %d\n",
                g2rec[i]->sec4[j]->prod_params->param_cat);
            fprintf(stdout, "\t\t param cat name: %s\n",
                g2rec[i]->sec4[j]->prod_params->param_cat_name);
            fprintf(stdout, "\t\t param num: %d\n",
                g2rec[i]->sec4[j]->prod_params->param_num);
            fprintf(stdout, "\t\t param name: %s\n",
                g2rec[i]->sec4[j]->prod_params->param_name);
            if (g2rec[i]->sec4[j]->prod_params->short_name != NULL)
                fprintf(stdout, "\t\t param short name: %s\n",
                    g2rec[i]->sec4[j]->prod_params->short_name);
            if (g2rec[i]->sec4[j]->prod_params->units != NULL)
                fprintf(stdout, "\t\t param units: %s\n",
                    g2rec[i]->sec4[j]->prod_params->units);
            fprintf(stdout, "\t\t gen process: %d\n",
                g2rec[i]->sec4[j]->prod_params->gen_process);
            fprintf(stdout, "\t\t gen process name: %s\n",
                g2rec[i]->sec4[j]->prod_params->gen_proc_name);
            fprintf(stdout, "\t\t bkgd gen process: %d\n",
                g2rec[i]->sec4[j]->prod_params->bkgd_gen_process);
            fprintf(stdout, "\t\t gen process ID: %d\n",
                g2rec[i]->sec4[j]->prod_params->gen_processID);
            fprintf(stdout, "\t\t hrs after reftime cutoff: %d\n",
                g2rec[i]->sec4[j]->prod_params->hrs_after_reftime_cutoff);
            fprintf(stdout, "\t\t min after reftime cutoff: %d\n",
                g2rec[i]->sec4[j]->prod_params->min_after_reftime_cutoff);
            fprintf(stdout, "\t\t time range unit id: %d\n",
                g2rec[i]->sec4[j]->prod_params->time_range_unit_id);
            fprintf(stdout, "\t\t time range unit: %s\n",
                g2rec[i]->sec4[j]->prod_params->time_range_unit);
            fprintf(stdout, "\t\t forecast time: %d\n",
                g2rec[i]->sec4[j]->prod_params->forecast_time);
            fprintf(stdout, "\t\t typeof first fixed sfc: %d\n",
                g2rec[i]->sec4[j]->prod_params->typeof_first_fixed_sfc);
            fprintf(stdout, "\t\t first fixed sfc: %s\n",
                g2rec[i]->sec4[j]->prod_params->first_fixed_sfc);
            fprintf(stdout, "\t\t first fixed sfc units: %s\n",
                g2rec[i]->sec4[j]->prod_params->units_first_fixed_sfc);
            fprintf(stdout, "\t\t scale factor first fixed sfc: %d\n",
                g2rec[i]->sec4[j]->prod_params->scale_factor_first_fixed_sfc);
            fprintf(stdout, "\t\t scaled val first fixed sfc: %d\n",
                g2rec[i]->sec4[j]->prod_params->scaled_val_first_fixed_sfc);

            fprintf(stdout, "\t\t typeof second fixed sfc: %d\n",
                g2rec[i]->sec4[j]->prod_params->typeof_second_fixed_sfc);
            fprintf(stdout, "\t\t second fixed sfc: %s\n",
                g2rec[i]->sec4[j]->prod_params->second_fixed_sfc);
            if (g2rec[i]->sec4[j]->prod_params->typeof_second_fixed_sfc != 255) {
                fprintf(stdout, "\t\t second fixed sfc units: %s\n",
                        g2rec[i]->sec4[j]->prod_params->units_second_fixed_sfc);
                fprintf(stdout, "\t\t scale factor second fixed sfc: %d\n",
                        g2rec[i]->sec4[j]->prod_params->scale_factor_second_fixed_sfc);
                fprintf(stdout, "\t\t scaled val second fixed sfc: %d\n",
                        g2rec[i]->sec4[j]->prod_params->scaled_val_second_fixed_sfc);
            }


            switch (g2rec[i]->sec4[j]->pds_num) {
                case 0:
                    /*
                     * Analysis or forecast at a horizontal level or in a
                     * horizontal layer at a point in time.
                     */
                    break;

                case 1:
                    /*
                     * Individual ensemble forecast, control and perturbed, at a
                     * horizontal level or in a horizontal layer at a point in time.
                     */
                    break;

                case 2:
                    /*
                     * Derived forecasts based on all ensemble members at a
                     * horizontal level or in a horizontal layer at a point
                     * in time.
                     */
                    break;

                case 3:
                    /*
                     * Derived forecasts based on a cluster of ensemble members
                     * over a rectangular area at a horizontal level or in a
                     * horizontal layer at a point in time.
                     */
                    break;

                case 4:
                    /*
                     * Derived forecasts based on a cluster of ensemble members
                     * over a circular area at a horizontal level or i
                     *  a horizontal layer at a point in time.
                     */
                    break;

                case 5:
                    /*
                     * Probability forecasts at a horizontal level or in a
                     * horizontal layer at a point in time.
                     */
                    break;

                case 6:
                    /*
                     * Percentile forecasts at a horizontal level or in a
                     * horizontal layer at a point in time.
                     */
                    break;

                case 7:
                    /*
                     * Analysis or forecast error at a horizontal level or
                     * in a horizontal layer at a point in time.
                     */
                    break;

                case 8:
                    /*
                     * Average, accumulation, extreme values or other
                     * statistically processed values at a horizontal level
                     * or in a horizontal layer in a continuous or
                     * non-continuous time interval.
                     */
                    break;

                case 9:
                    /*
                     * Probability forecasts at a horizontal level or in a
                     * horizontal layer in a continuous or non-continuous
                     * time interval.
                     */
                    break;

                case 10:
                    /*
                     * Percentile forecasts at a horizontal level or in a
                     * horizontal layer in a continuous or non-continuous time 
                     * interval.
                     */
                    break;

                case 11:
                    /*
                     * Individual ensemble forecast, control and perturbed, at a
                     * horizontal level or in a horizontal layer, in a continuous
                     * or non-continuous time interval.
                     */

                    /* EPS info */
                    fprintf(stdout, "\t\t level: %d\n",
                        g2rec[i]->sec4[j]->prod_params->level);
                    fprintf(stdout, "\t\t level: %d\n",
                        g2rec[i]->sec4[j]->prod_params->level);
                    fprintf(stdout, "\t\t typeof ensemblefx: %d\n",
                        g2rec[i]->sec4[j]->prod_params->typeof_ensemble_fx);
                    fprintf(stdout, "\t\t ensemble fx type: %s\n",
                        g2rec[i]->sec4[j]->prod_params->ensemble_fx_type);
                    fprintf(stdout, "\t\t perturb num: %d\n",
                        g2rec[i]->sec4[j]->prod_params->perturb_num);
                    fprintf(stdout, "\t\t num fx ensemble: %d\n",
                        g2rec[i]->sec4[j]->prod_params->num_fx_ensemble);
                    fprintf(stdout, "\t\t year end overall time interval: %d\n",
                        g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.year);
                    fprintf(stdout, "\t\t mon end overall time interval: %d\n",
                        g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.mon);
                    fprintf(stdout, "\t\t day end overall time interval: %d\n",
                        g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.day);
                    fprintf(stdout, "\t\t hour end overall time interval: %d\n",
                        g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.hour);
                    fprintf(stdout, "\t\t min end overall time interval: %d\n",
                        g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.min);
                    fprintf(stdout, "\t\t sec end overall time interval: %d\n",
                        g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.sec);
                    fprintf(stdout, "\t\t num timerange spec time interval calc: %d\n",
                        g2rec[i]->sec4[j]->prod_params->num_timerange_spec_time_interval_calc);
                    fprintf(stdout, "\t\t total num missing data vals: %d\n",
                        g2rec[i]->sec4[j]->prod_params->total_num_missing_data_vals);

                    fprintf(stdout, "\t\t typeof stat proc: %d\n",
                        g2rec[i]->sec4[j]->prod_params->typeof_stat_proc);
                    fprintf(stdout, "\t\t stat proc: %s\n",
                        g2rec[i]->sec4[j]->prod_params->stat_proc);
                    fprintf(stdout, "\t\t typeof incr betw fields: %d\n",
                        g2rec[i]->sec4[j]->prod_params->typeof_incr_betw_fields);
                    fprintf(stdout, "\t\t incr betw fields: %s\n",
                        g2rec[i]->sec4[j]->prod_params->incr_betw_fields);
                    fprintf(stdout, "\t\t ind time range unit stat proc done: %d\n",
                        g2rec[i]->sec4[j]->prod_params->ind_time_range_unit_stat_proc_done);
                    fprintf(stdout, "\t\t itr unit: %s\n",
                        g2rec[i]->sec4[j]->prod_params->itr_unit);
                    fprintf(stdout, "\t\t len time range unit stat proc done: %d\n",
                        g2rec[i]->sec4[j]->prod_params->len_time_range_unit_stat_proc_done);
                    fprintf(stdout, "\t\t ind time unit incr succ fields: %d\n",
                        g2rec[i]->sec4[j]->prod_params->ind_time_unit_incr_succ_fields);
                    fprintf(stdout, "\t\t itr succ unit: %s\n",
                        g2rec[i]->sec4[j]->prod_params->itr_succ_unit);
                    fprintf(stdout, "\t\t time incr betw fields: %d\n",
                        g2rec[i]->sec4[j]->prod_params->time_incr_betw_fields);
                    break;

                case 12:
                    /*
                     * Derived forecasts based on all ensemble members at a
                     * horizontal level or in a horizontal layer, in a continuous
                     * or non-continuous time interval.
                     */
                    break;

                case 13:
                    /*
                     * Derived forecasts based on a cluster of ensemble members
                     * over a rectangular area at a horizontal level or in a
                     * horizontal layer, in a continuous or non-continuous time interval.
                     */
                    break;

                case 14:
                    /*
                     * Derived forecasts based on a cluster of ensemble members over
                     * a circular area at a horizontal level or in a horizontal
                     * layer, in a continuous or non-continuous time interval.
                     */
                    break;

                case 20:
                    /*
                     * Radar product.
                     */
                    break;

                case 30:
                    /*
                     * Satellite product.
                     */
                    break;

                case 254:
                    /*
                     * CCITT IA5 character string.
                     */
                    break;

                case 1000:
                    /*
                     * Cross-section of analysis and forecast at a point in time.
                     */
                    break;

                case 1001:
                    /*
                     * Cross-section of averaged or otherwise statistically
                     * processed analysis or forecast over a range of time.
                     */
                    break;

                case 1002:
                    /*
                     * Cross-section of analysis and forecast, averaged or
                     * otherwise statistically-processed over latitude or longitude.
                     */
                    break;

                case 1100:
                    /*
                     * Hovmoller-type grid with no averaging or other statistical
                     * processing.
                     */
                    break;

                case 1101:
                    /*
                     * Hovmoller-type grid with averaging or other statistical processing.
                     */
                    break;

                case 65535:
                    /* Missing */
                    break;

                default:
                    /* Reserved */
                    if (g2rec[i]->sec4[j]->pds_num >= 1102
                            || g2rec[i]->sec4[j]->pds_num <= 32767)
                        /* Reserved by WMO */
                        ;;
                        
                    /* FALLTHROUGH */

                    if (g2rec[i]->sec4[j]->pds_num >= 32768
                            || g2rec[i]->sec4[j]->pds_num <= 65534)
                        /* Reserved for Local Use */
                        ;;
                        break;
            }





            fprintf(stdout, "\t num coord: %d\n", g2rec[i]->sec4[j]->num_coord);
            fprintf(stdout, "\n");

            fprintf(stdout, "Section 5:\n");
            fprintf(stdout, "\t section ID: %d\n", g2rec[i]->sec5[j]->secid);
            fprintf(stdout, "\t drt templ num: %d\n", g2rec[i]->sec5[j]->drt_templ_num);
            fprintf(stdout, "\t Data Representation:\n");
            fprintf(stdout, "\t\t ref val: %.3f\n",
                g2rec[i]->sec5[j]->data_repr->refVal);
            fprintf(stdout, "\t\t bin scale factor: %d\n",
                g2rec[i]->sec5[j]->data_repr->bin_scale_factor);
            fprintf(stdout, "\t\t dec scale factor: %d\n",
                g2rec[i]->sec5[j]->data_repr->dec_scale_factor);
            fprintf(stdout, "\t\t nbits packed val: %hd\n",
                g2rec[i]->sec5[j]->data_repr->nbits_packed_val);
            fprintf(stdout, "\t\t typeof field vals: %hd\n",
                g2rec[i]->sec5[j]->data_repr->typeof_field_vals);
            fprintf(stdout, "\t\t field vals: %s\n",
                g2rec[i]->sec5[j]->data_repr->field_vals);
            fprintf(stdout, "\t\t ndpts: %d\n",
                g2rec[i]->sec5[j]->ndpts);
            fprintf(stdout, "\n");

            fprintf(stdout, "Section 6:\n");
            fprintf(stdout, "\t section ID: %d\n", g2rec[i]->sec6[j]->secid);
            fprintf(stdout, "\t unpacked: %d\n", g2rec[i]->sec6[j]->unpacked);
            fprintf(stdout, "\t expanded: %d\n", g2rec[i]->sec6[j]->expanded);
            fprintf(stdout, "\t bmap ind: %d\n", g2rec[i]->sec6[j]->bmap_ind);
            fprintf(stdout, "\n");

            fprintf(stdout, "Section 7:\n");
            fprintf(stdout, "\t section ID: %d\n", g2rec[i]->sec7[j]->secid);
            fprintf(stdout, "\n\n\n");
        }
    }
}

static void Grib2FreeCodeTableRec
# if NhlNeedProto
(g2codeTable *ct)
# else
(ct)
    g2codeTable *ct;
# endif
{

    if (ct->descrip != NULL)
        NclFree(ct->descrip);

    if (ct->shname != NULL)
        NclFree(ct->shname);

    if (ct->units != NULL)
        NclFree(ct->units);

    NclFree(ct);
}


static NhlErrorTypes Grib2ReadCodeTable
# if NhlNeedProto
(char *center, int secid, char *table, int oct, int cat, g2codeTable *ct)
# else
(center, secid, table, oct, cat, ct)
    char    *center;
    int secid;
    char    *table;
    int oct;
    int cat;
    g2codetable *ct;
# endif
{
    FILE    *fp = NULL;
    char    *ctf = NULL;

    char    s[256],
            *sp;

    char    *rol = NULL;
    char    *sep = ":";

    int len;

    int where = 0;
    NhlErrorTypes err = 0;


    /* default values */
    ct->oct = -1;
    ct->cat = -1;

    if (ct->descrip != NULL)
        NclFree(ct->descrip);
    ct->descrip = NULL;

    if (ct->shname != NULL)
        NclFree(ct->shname);
    ct->shname = NULL;

    if (ct->units != NULL)
        NclFree(ct->units);
    ct->units = NULL;


    /*
     * construct pathname:
     *  codetable dir + center + section + table
     *  special case: reading the "centers" table -- don't know "center" yet
     */
    if (secid == -1) {
        /* reading table of centers */
        ctf = NclMalloc(g2_codetable_dirname_len + strlen(table) + 2);
        if (ctf == NULL) {
            NhlPError(NhlFATAL, NhlEUNKNOWN, "Could not allocate memory for code table.");
            return err = NhlFATAL;
        }

        (void) sprintf(ctf, "%s/%s", grib2_codetable_dir, table);
    } else {
        ctf = NclMalloc(g2_codetable_dirname_len + strlen(center) + 5 + strlen(table));
        if (ctf == NULL) {
            NhlPError(NhlFATAL, NhlEUNKNOWN, "Could not allocate memory for code table.");
            return err = NhlFATAL;
        }

        (void) sprintf(ctf, "%s/%s/%d/%s", grib2_codetable_dir, center, secid, table);
    }

    fp = fopen(ctf, "r");
    if (fp == (FILE *) NULL) {
	/* Note that in some cases it is not fatal that a table was not found, but
	 *  if the centers table was not found it definitely is FATAL.
	 */
	if (secid == -1) {
		NhlPError(NhlFATAL, NhlEUNKNOWN,
			  " NclGRIB2: codetable directory \"%s\" invalid",grib2_codetable_dir);
		return err = NhlFATAL;
	}
        NhlPError(NhlWARNING, NhlEUNKNOWN,
            " NclGRIB2: codetable file \"%s\" not a valid GRIB2 code table.",ctf);
        NclFree(ctf);
        return err = NhlWARNING;
    } else {
        while (fgets(s, 256, fp)) {
	    int go_to_outer_loop = 0;
	    where = 0;
            sp = &s[0];
    	    len = strlen(s);
            if (len < 2)
	    	    continue;
            s[len - 1] = '\0';
            while (isspace(*sp))
                ++sp;
            if (*sp != '#') {
                rol = strtok(s, sep);
                if (rol == NULL)
                    continue;

                /* first field */
                ct->oct = (int) strtol(rol, (char **) NULL, 10);
                if (ct->oct == oct) {
                    while (rol != NULL) {
                        switch (where) {
                            case 0:
                                /* ct->oct already set */
                                ++where;
                                break;

                            case 1:
                                /* Category */
 	                        ct->cat = (int) strtol(rol, (char **) NULL, 10);
				if (cat > -1 && ct->cat != cat) 
					go_to_outer_loop = 1;
				else 
					++where;
                                break;

                            case 2:
                                /* Description */
                                len = strlen(rol);
                                ct->descrip = NclMalloc(len * sizeof(char) + 1);
                                if (ct->descrip == NULL) {
                                    NhlPError(NhlFATAL, NhlEUNKNOWN,
                                        "Could not allocate memory for code table entry.");
                                    (void) fclose(fp);
                                    NclFree(ctf);
                                    return err = NhlFATAL;
                                }

                                strncpy(ct->descrip, rol, len);
                                ct->descrip[len] = '\0';
                                ++where;
                                break;
                  
                            case 3:
                                /* Units */
                                len = strlen(rol);
                                ct->units = NclMalloc(len * sizeof(char) + 1);
                                if (ct->units == NULL) {
                                    NhlPError(NhlFATAL, NhlEUNKNOWN,
                                        "Could not allocate memory for code table entry.");
                                    (void) fclose(fp);
                                    NclFree(ctf);
                                    return err = NhlFATAL;
                                }

                                strncpy(ct->units, rol, len);
                                ct->units[len] = '\0';
                                ++where;
                                break;

                            case 4:
                                /* "Short Name" */
                                len = strlen(rol);
                                ct->shname = NclMalloc(len * sizeof(char) + 1);
                                if (ct->shname == NULL) {
                                    NhlPError(NhlFATAL, NhlEUNKNOWN,
                                        "Could not allocate memory for code table entry.");
                                    (void) fclose(fp);
                                    NclFree(ctf);
                                    return err = NhlFATAL;
                                }       

                                strncpy(ct->shname, rol, len);
                                ct->shname[len] = '\0';
                                ++where;
                                break;

                            default:
                                NhlPError(NhlWARNING, NhlEUNKNOWN,
                                    "Could not decode code table file %s", ctf);
                                (void) fclose(fp);
                                NclFree(ctf);
                                return err = NhlWARNING;
                                break;
                        }
			if (go_to_outer_loop)
				break;
                        rol = strtok(NULL, sep);
                    }
		    if (go_to_outer_loop)
			    continue;

                    (void) fclose(fp);
                    NclFree(ctf);
                    return err = NhlNOERROR;
                } else {
                    continue;
                }
            }
        }

        /* didn't find entry in the table */
        NhlPError(NhlWARNING, NhlEUNKNOWN,
		  " Entry (%d) not found in code table file %s", oct, ctf);
        ct->oct = -1;
        ct->cat = -1;
        ct->descrip = NULL;
        ct->shname = NULL;
        ct->units = NULL;
	(void) fclose(fp);
        NclFree(ctf);
        return err = NhlWARNING;
    }

    (void) fclose(fp);
    NclFree(ctf);
    return err = NhlNOERROR;
}


static int g2InitializeOptions 
# if    NhlNeedProto
(Grib2FileRecord *g2tmp)
# else
(tmp)
    Grib2FileRecord *g2tmp;
# endif /* NhlNeedProto */
{
    Grib2Options *g2options;

    g2tmp->n_options = GRIB_NUM_OPTIONS;
	
    g2options = NclMalloc(g2tmp->n_options * sizeof(Grib2Options));
    if (! g2options) {
        NhlPError(NhlFATAL, ENOMEM, NULL);
        NclFree(g2options);
        return 0;
    }

    g2options[GRIB_THINNED_GRID_INTERPOLATION_OPT].data_type = NCL_string;
    g2options[GRIB_THINNED_GRID_INTERPOLATION_OPT].n_values = 1;
    g2options[GRIB_THINNED_GRID_INTERPOLATION_OPT].values = (void *) NrmStringToQuark("cubic");

    g2options[GRIB_INITIAL_TIME_COORDINATE_TYPE_OPT].data_type = NCL_string;
    g2options[GRIB_INITIAL_TIME_COORDINATE_TYPE_OPT].n_values = 1;
    g2options[GRIB_INITIAL_TIME_COORDINATE_TYPE_OPT].values = (void *) NrmStringToQuark("numeric");

    g2options[GRIB_DEFAULT_NCEP_PTABLE_OPT].data_type = NCL_string;
    g2options[GRIB_DEFAULT_NCEP_PTABLE_OPT].n_values = 1;
    g2options[GRIB_DEFAULT_NCEP_PTABLE_OPT].values = (void *) NrmStringToQuark("operational");

    g2options[GRIB_PRINT_RECORD_INFO_OPT].data_type = NCL_logical;
    g2options[GRIB_PRINT_RECORD_INFO_OPT].n_values = 1;
    g2options[GRIB_PRINT_RECORD_INFO_OPT].values = (void *) 0;

    g2options[GRIB_SINGLE_ELEMENT_DIMENSIONS_OPT].data_type = NCL_string;
    g2options[GRIB_SINGLE_ELEMENT_DIMENSIONS_OPT].n_values = 1;
    g2options[GRIB_SINGLE_ELEMENT_DIMENSIONS_OPT].values = (void *) NrmStringToQuark("none");

    g2options[GRIB_TIME_PERIOD_SUFFIX_OPT].data_type = NCL_logical;
    g2options[GRIB_TIME_PERIOD_SUFFIX_OPT].n_values = 1;
    g2options[GRIB_TIME_PERIOD_SUFFIX_OPT].values = (void *) 1;

    g2options[GRIB_CACHE_SIZE_OPT].data_type = NCL_int;
    g2options[GRIB_CACHE_SIZE_OPT].n_values = 1;
    g2options[GRIB_CACHE_SIZE_OPT].values = (void *) 0;

    g2tmp->options = g2options;
    return 1;
}



static void *Grib2InitializeFileRec
#if	NhlNeedProto
(NclFileFormat *format)
#else
(format)
NclFileFormat *format;
#endif
{
    Grib2FileRecord *g2rec = NULL;

    g2rec = (Grib2FileRecord *) NclCalloc(1, sizeof(Grib2FileRecord));
    if (! g2rec) {
        NhlPError(NhlFATAL, ENOMEM, NULL);
        return NULL;
    }

    g2InitializeOptions(g2rec);
    *format = _NclGRIB2;
    return (void *) g2rec;
}

static void *Grib2CreateFile
# if    NhlNeedProto
(void *rec, NclQuark path)
# else
(rec, path)
    void    *rec;
    NclQuark path;
# endif /* NhlNeedProto */
{
    NhlPError(NhlFATAL, NhlEUNKNOWN,
        "GRIB v2 files can only be read, not created using NCL");
    return NULL;
}


static void Grib2FreeGrib2Rec
# if    NhlNeedProto
(G2Rec  **rec)
# else
(rec)
    G2Rec   **rec;
# endif /* NhlNeedProto */
{
    int i = 0,
        j = 0,
        nr = 0,
        nrp = 0;

    G2Sec2  **sec2_p;
    G2Sec3  **sec3_p;
    G2Sec4  **sec4_p;
    G2Sec5  **sec5_p;
    G2Sec6  **sec6_p;
    G2Sec7  **sec7_p;


    nr = rec[0]->numrecs;

    for (i = 0; i < nr; i++) {
	    nrp = rec[i]->num_rptd;
	    if (rec[i]->table_source_name) {
		    NclFree(rec[i]->table_source_name);
	    }

	    if (rec[i]->sec1.center_name) {
		    NclFree(rec[i]->sec1.center_name);
	    }
	    if (rec[i]->sec1.proc_data_type) {
		    NclFree(rec[i]->sec1.proc_data_type);
	    }
	    if (rec[i]->sec1.proc_prod_status) {
		    NclFree(rec[i]->sec1.proc_prod_status);
	    }
	    if (rec[i]->sec1.sig_ref_time) {
		    NclFree(rec[i]->sec1.sig_ref_time);
	    }

	    
        sec2_p = rec[i]->sec2;
        sec3_p = rec[i]->sec3;
        sec4_p = rec[i]->sec4;
        sec5_p = rec[i]->sec5;
        sec6_p = rec[i]->sec6;
        sec7_p = rec[i]->sec7;

        for (j = 0; j < nrp; j++) {
            /* Section 2 */
            if (sec2_p[j]->local != NULL)
                NclFree(sec2_p[j]->local);
            NclFree(sec2_p[j]);

            /* Section 3 */
#if 0
            if (sec3_p[j]->grid_list_num_oct_opt != NULL)
                NclFree(sec3_p[j]->grid_list_num_oct_opt);
            if (sec3_p[j]->shape_of_earth != NULL) {
/*
                if (sec3_p[j]->shape_of_earth->earthShape != NULL)
                    NclFree(sec3_p[j]->shape_of_earth->earthShape);
*/
        
                NclFree(sec3_p[j]->shape_of_earth);
            }

            if (sec3_p[j]->res_comp != NULL)
                NclFree(sec3_p[j]->res_comp);

            if (sec3_p[j]->scan_mode != NULL)
                NclFree(sec3_p[j]->scan_mode);

            NclFree(sec3_p[j]->grid_def_name);
            NclFree(sec3_p[j]->interp_opt_name);

            NclFree(sec3_p[j]);
#endif
            /* Section 4 */
            if (sec4_p[j]->prod_params != NULL) {
                if (sec4_p[j]->prod_params->param_cat_name != NULL)
                    NclFree(sec4_p[j]->prod_params->param_cat_name);
                if (sec4_p[j]->prod_params->param_name != NULL)
                    NclFree(sec4_p[j]->prod_params->param_name);
                if (sec4_p[j]->prod_params->short_name != NULL)
                    NclFree(sec4_p[j]->prod_params->short_name);
                if (sec4_p[j]->prod_params->units != NULL)
                    NclFree(sec4_p[j]->prod_params->units);
                if (sec4_p[j]->prod_params->gen_proc_name != NULL)
                    NclFree(sec4_p[j]->prod_params->gen_proc_name);
                if (sec4_p[j]->prod_params->time_range_unit != NULL)
                    NclFree(sec4_p[j]->prod_params->time_range_unit);
                if (sec4_p[j]->prod_params->first_fixed_sfc != NULL)
                    NclFree(sec4_p[j]->prod_params->first_fixed_sfc);
                if (sec4_p[j]->prod_params->units_first_fixed_sfc != NULL)
                    NclFree(sec4_p[j]->prod_params->units_first_fixed_sfc);
                if (sec4_p[j]->prod_params->ensemble_fx_type != NULL)
                    NclFree(sec4_p[j]->prod_params->ensemble_fx_type);
                if (sec4_p[j]->prod_params->second_fixed_sfc != NULL)
                    NclFree(sec4_p[j]->prod_params->second_fixed_sfc);
                if (sec4_p[j]->prod_params->units_second_fixed_sfc != NULL)
                    NclFree(sec4_p[j]->prod_params->units_second_fixed_sfc);

                NclFree(sec4_p[j]->prod_params);
            }

            if (sec4_p[j]->prod_def_name != NULL)
                NclFree(sec4_p[j]->prod_def_name);
            NclFree(sec4_p[j]);

            /* Section 5 */
            if (sec5_p[j]->data_repr->field_vals != NULL)
                NclFree(sec5_p[j]->data_repr->field_vals);

            if (sec5_p[j]->data_repr != NULL)
                NclFree(sec5_p[j]->data_repr);

            if (sec5_p[j]->drt_desc != NULL)
                NclFree(sec5_p[j]->drt_desc);
            NclFree(sec5_p[j]);

            /* Section 6 */
            if (sec6_p[j]->bmap != NULL)
                NclFree(sec6_p[j]->bmap);

            if (sec6_p[j]->bmap_desc != NULL)
                NclFree(sec6_p[j]->bmap_desc);
            NclFree(sec6_p[j]);

            /* Section 7 */
            NclFree(sec7_p[j]);
        }
        
        NclFree(sec2_p);
        NclFree(sec3_p);
        NclFree(sec4_p);
        NclFree(sec5_p);
        NclFree(sec6_p);
        NclFree(sec7_p);

        NclFree(rec[i]);
    }
    NclFree(rec);

    return;
}

/*
 * Note on _g2AdjustTimeAndStatProcVars function.
 * This code adjusts the time offset of each record to make them comparable as to
 * statistical variable time duration. Then it tries to figure out whether new 
 * variables need to be created for statistical variable records where the time
 * periods are differect. There are a couple of gnarly details that make this
 * not straightforward. 1) Distinguishing variables where the statistical time
 * period increases in a regular way for each forcast time (i.e. the stat is
 * always calculated from the initial time to the forecast time) from variables
 * where there are different accumulation periods that are fixed for each forecast
 * time. The second type need to be broken apart into separate variables depending
 * on the statistical period. 2) Handling initialization records where both the 
 * forecast time and the statistical period are zero. These should not be broken
 * out into a variable of their own when breaking records up into separate variables
 * depending on the statistical duration. However in the case where there is more
 * than one duration it is arbitrary which variable to attach them to. This being
 * the case, for convenience they are attached to the stat variable with the 
 * longest duration.
 */

static Grib2ParamList  *
_g2AdjustTimeAndStatProcVars
#if    NhlNeedProto
(
    Grib2FileRecord *g2frec,
    Grib2ParamList  *g2plist
)
#else
(g2frec,g2plist)
Grib2FileRecord *g2frec;
Grib2ParamList  *g2plist;
#endif /* NhlNeedProto */
{
	Grib2RecordInqRecList *g2rlist, *prevrlist, *g2rlist_max_period = NULL;
	int time_period_count;
	int *time_periods;
	int max_count;
	int max_period;
	int i;
	Grib2VarTraits *trp = &g2plist->traits;
	Grib2ParamList  *newplist, *tplist, *ttplist;
        int zero_offset_index;
	int common_time_perod_unit;
	
	g2rlist = g2plist->thelist;

	if (trp->stat_proc_type > 191 && trp->stat_proc_type != 255) {
		g2plist->p1 = g2rlist->rec_inq->p1;
		g2plist->p2 = g2rlist->rec_inq->p2;
		g2plist->n_grids = g2rlist->rec_inq->n_grids;
		g2plist->time_period = g2rlist->rec_inq->time_period;
                g2plist->forecast_time_units = g2rlist->rec_inq->forecast_time_units;
                g2plist->time_period_units = g2rlist->rec_inq->time_period_units;
                g2plist->variable_time_unit = 0;
		while (g2rlist != NULL) {
			if (g2plist->p1 != g2rlist->rec_inq->p1)
				printf("p1 diff\n");
			if (g2plist->p2 != g2rlist->rec_inq->p2)
				printf("p1 diff\n");
			if (g2plist->forecast_time_units != g2rlist->rec_inq->forecast_time_units)
				printf("forecast units diff\n");
			g2rlist = g2rlist->next;
		}
		return NULL;
	}

	if (trp->stat_proc_type == -1 || g2plist->forecast_time_iszero) {
		while (g2rlist != NULL) {
			if (! g2plist->variable_time_unit) {
				g2rlist->rec_inq->time_offset = g2rlist->rec_inq->forecast_time;
			}
			else {
				g2rlist->rec_inq->time_offset = _g2GetConvertedTime(
					g2plist->forecast_time_units,
					g2rlist->rec_inq->forecast_time_units,
					g2rlist->rec_inq->forecast_time);
			}
			_g2AdjustTimeOffset(g2plist,g2rlist->rec_inq);
			g2rlist = g2rlist->next;
		}
		return NULL;
	}

	time_period_count = 0;
	max_count = 10;
	time_periods = NclMalloc(max_count * sizeof(int));
	for (i = 0; i < max_count; i++) {
		time_periods[i] = -9999999;
	}
	max_period = -9999999;

	/* 
	 * get the shortest time period units
	 */

	common_time_perod_unit = -1;
	zero_offset_index = -1;
	g2rlist = g2plist->thelist;
	while (g2rlist != NULL) {
		common_time_perod_unit = _g2GetShortestTimeUnit(common_time_perod_unit,g2rlist->rec_inq->time_period_units);
		g2rlist = g2rlist->next;
	}		
		
	g2rlist = g2plist->thelist;
	while (g2rlist != NULL) {
		int time_period;
		if (! g2plist->variable_time_unit) {
			g2rlist->rec_inq->time_offset = g2rlist->rec_inq->forecast_time;
		}
		else {
			g2rlist->rec_inq->time_offset = _g2GetConvertedTime(
				g2plist->forecast_time_units,
				g2rlist->rec_inq->forecast_time_units,
				g2rlist->rec_inq->forecast_time);
		}
		_g2AdjustTimeOffset(g2plist,g2rlist->rec_inq);
		time_period = _g2GetConvertedTime(common_time_perod_unit,
						  g2rlist->rec_inq->time_period_units,
						  g2rlist->rec_inq->time_period);
		for (i = 0; i < time_period_count; i++) {
			if (time_period == time_periods[i])
				break;
		}
		if (i == time_period_count) {
			if (time_period_count == max_count) {
				max_count *= 2;
				time_periods = NclRealloc(time_periods,max_count * sizeof(int));
			}
			time_periods[i] = time_period;
			if (g2rlist->rec_inq->forecast_time == 0) {
				if (g2rlist->rec_inq->time_offset == 0 && time_periods[i] == 0) {
					zero_offset_index = i;
				}
			}
			if (time_periods[i] > max_period) {
				max_period = time_periods[i];
				g2rlist_max_period = g2rlist;
			}
			time_period_count++;
		}
		g2rlist = g2rlist->next;
	}
	newplist = NULL;
	if (g2rlist_max_period) {
		g2plist->time_period = g2rlist_max_period->rec_inq->time_period;
		g2plist->forecast_time_units = g2rlist_max_period->rec_inq->forecast_time_units;
		g2plist->time_period_units = g2rlist_max_period->rec_inq->time_period_units;
	}
	g2plist->variable_time_unit = 0;

        i = 0;
	while (i < time_period_count && time_periods[i] != -999) {
		/* keep the max period elements as the remaining elements for the original variable */
		if (time_periods[i] == max_period) {
			i++;
			continue;
		}
		g2rlist = g2plist->thelist;
		tplist = NULL;
		prevrlist = NULL;
		while (g2rlist != NULL) {
			int time_period = _g2GetConvertedTime(common_time_perod_unit,
							      g2rlist->rec_inq->time_period_units,
							      g2rlist->rec_inq->time_period);

			if (time_period != time_periods[i]) {
				prevrlist = g2rlist;
				g2rlist = g2rlist->next;
			}
			/* zero offset elements (initialization records) get attached to the max period variable */
			else if (i == zero_offset_index && g2rlist->rec_inq->time_offset == 0) {
				prevrlist = g2rlist;
				g2rlist = g2rlist->next;
			}
			else {
				if (! tplist) {
					tplist = _g2NewListNode(g2rlist->rec_inq);
					g2frec->n_vars++;
					memcpy(&tplist->var_info,&g2plist->var_info,sizeof(NclGrib2FVarRec));
					tplist->forecast_time_iszero = 0;
				}
				else {
					_g2AddRecordToNode(tplist,g2rlist->rec_inq);
				}
				if (! prevrlist) {
					g2plist->thelist = g2plist->thelist->next;
					NclFree(g2rlist);
					g2rlist = g2plist->thelist;
				}
				else {
					prevrlist->next = g2rlist->next;
					NclFree(g2rlist);
					g2rlist = prevrlist->next;
				}
				g2plist->n_entries--;
			}
		}
		if (! newplist) {
			newplist = tplist;
		}
		else {
			for (ttplist = newplist; ttplist->next != NULL; ttplist = ttplist->next) {
				;
			}
			ttplist->next = tplist;
		}
		i++;
	}
	NclFree(time_periods);
	/* readjust the time offset based on the units of the separated variables */
	ttplist = g2plist;
	while (ttplist != NULL) {
		g2rlist = ttplist->thelist;
		while (g2rlist != NULL) {
			if (! ttplist->variable_time_unit) {
				g2rlist->rec_inq->time_offset = g2rlist->rec_inq->forecast_time;
			}
			else {
				g2rlist->rec_inq->time_offset = _g2GetConvertedTime(
					ttplist->forecast_time_units,
					g2rlist->rec_inq->forecast_time_units,
					g2rlist->rec_inq->forecast_time);
			}
			_g2AdjustTimeOffset(ttplist,g2rlist->rec_inq);
			ttplist->time_period_units = g2rlist->rec_inq->time_period_units;
			g2rlist = g2rlist->next;
		}
		/* The original plist (variable record) is separated from the newly created 
		 * plists at the moment; thus the following code:
		 */
		if (ttplist == g2plist) 
			ttplist = newplist;
		else
			ttplist = ttplist->next;
	}
	
	return newplist;
}

typedef struct _tigge_info {

  int flag;  /* parameter type descripter */
  int disc;  /* Section 0 Discipline */
  int pcat;  /* Section 4 table 4.0 Parameter category */
  int pnum;  /* Section 4 table 4.0 Parameter number */
  int tstat;  /* Section 4 type of statistical processing */
  int toffs;  /* Type of first fixed surface */
  int sfffs;  /* Scale factor of first fixed surface */
  int svffs;  /* scale value of first fixed surface */
  int tosfs;  /* type of second fixed surface */
  int sfsfs;  /* scale factor of second fixed surface */
  int svsfs;  /* scale value of second fixed surface */
   
  const char *name;
  const char *desc;
  const char *unit;
} TiggeInfo;

static TiggeInfo tigge_info[] = {
  /* TIGGE table entries 
     by Doug Schuster */

  {2, 0, 0, 0,    2, 103, 255,   255,   255, 255, 255, "mx2t6", "surface air maximum temperature", "K" },
  {2, 0, 0, 0,    3, 103, 255,   255,   255, 255, 255, "mn2t6", "surface air minimum temperature", "K" },
  {2, 0, 1, 53,   1,   1, 255,   255,   255, 255, 255, "sf", "snow fall water equivalent", "kg/m^2" },
  {2, 0, 6, 24,   1,   1, 255,   255,   255, 255, 255, "sund", "sunshine duration", "s" },
  {2, 0, 0, 10,   1,   1, 255,   255,   255, 255, 255, "slhf", "time integrated surface latent heat flux", "W/m^2 s" },
  {2, 0, 4, 9,    1,   1, 255,   255,   255, 255, 255, "ssr", "time integrated surface net solar radiation", "W/m^2 s" },
  {2, 0, 0, 11,   1,   1, 255,   255,   255, 255, 255, "sshf", "time integrated surface sensible heat flux", "W/m^2 s" },
  {2, 0, 1, 52,   1,   1, 255,   255,   255, 255, 255, "tp", "total precipitation", "kg/m^2" },
  {2, 0, 5, 5,    1,   8, 255,   255,   255, 255, 255, "ttr", "time integrated outgoing long wave radiation", "W/m^2 s" },
  {2, 0, 5, 5,    1,   1, 255,   255,   255, 255, 255, "str", "time integrated surface net thermal radiation", "W/m^2 s" },
  {3, 2, 0, 22, 255, 106,   0,     0,   106,   1,   2, "sm", "soil moisture", "kg/m^3" },
  {3, 2, 0, 2,  255, 106,   0,     0,   106,   1,   2, "st", "soil temperature", "K"}, 
  {3, 2, 3, 7,  255, 106,   0,     0,   106,   1,   2, "cap", "field_capacity", "proportion" },
  {3, 2, 0, 17, 255, 106,   0,     0,   106,   1,   2, "wilt", "wilting point", "proportion" },
  {4, 0, 6, 1,  255,   1, 255,   255,     8, 255, 255, "tcc", "total cloud cover", "%" },
  {4, 0, 1, 51, 255,   1, 255,   255,     8, 255, 255, "tcw", "total column water", "kg/m^2" },
  {4, 0, 7, 6,  255,   1, 255,   255,     8, 255, 255, "cape", "Convective available potential energy", "J/kg" },
  {4, 0, 7, 7,  255,   1, 255,   255,     8, 255, 255, "ci", "convective inhibition", "J/kg" },
  {5, 0, 0, 2,  255, 109,   6,     2,   255, 255, 255, "pt", "potential temperature", "K" },
  {5, 0, 2, 14, 255, 107,   0,   320,   255, 255, 255, "pv", "potential vorticity", "km^2/kg/s" },
  {5, 0, 2, 2,  255, 103,   0,    10,   255, 255, 255, "10u", "10 meter u velocity", "m/s" },
  {5, 0, 2, 3,  255, 103,   0,    10,   255, 255, 255, "10v", "10 meter v velocity", "m/s" }, 
  {6, 0, 2, 2,  255, 255, 255,   255,   255, 255, 255, "u", "u velocity", "m/s" },
  {6, 0, 2, 3,  255, 255, 255,   255,   255, 255, 255, "v", "v velocity", "m/s" },
  {1, 0, 0, 17, 255,   1, 255,   255,   255, 255, 255, "skt", "skin temperature", "K" },
  {1, 0, 1, 60, 255,   1, 255,   255,   255, 255, 255, "sd", "snow depth water equivalent", "kg/m^2" },
  {1, 0, 0, 0,  255, 100, 255,   255,   255, 255, 255, "t", "temperature", "K" },
  {1, 0, 0, 6,  255, 103, 255,   255,   255, 255, 255, "2d", "surface air dew point temperature", "K" },
  {1, 0, 0, 0,  255, 103, 255,   255,   255, 255, 255, "2t", "surface air temperature", "K" },
  {1, 0, 1, 0,  255, 100, 255,   255,   255, 255, 255, "q", "specific humidity", "kg/kg" },
  {1, 0, 3, 0,  255,   1, 255,   255,   255, 255, 255, "sp", "surface pressure", "Pa" },
  {1, 0, 3, 0,  255, 101, 255,   255,   255, 255, 255, "msl", "mean sea level pressure", "Pa" },
  {1, 0, 3, 5,  255, 100, 255,   255,   255, 255, 255, "gh", "geopotential height", "gpm" },
  {1, 2, 0, 0,  255,   1, 255,   255,   255, 255, 255, "lsm", "land sea mask", "fraction" },
  {1, 0, 3, 5,  255,   1, 255,   255,   255, 255, 255, "orog", "orography", "gpm" },
  /* END MARKER */
  { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, NULL, NULL, NULL }
};



static void _g2SetVarInfo
#if    NhlNeedProto
(
    Grib2FileRecord *g2frec,
    Grib2ParamList  *g2plist
)
#else
(g2frec,g2plist)
Grib2FileRecord *g2frec;
Grib2ParamList  *g2plist;
#endif /* NhlNeedProto */
{
	Grib2RecordInqRec   *g2inqrec = NULL;
	Grib2RecordInqRecList *g2rlist;
	int i;
	Grib2VarTraits *trp = &g2plist->traits;
	g2codeTable *ct;
	char buf[512];
	NhlErrorTypes cterr;

	g2rlist = g2plist->thelist;
	while (g2rlist != NULL) {
            if (g2rlist->rec_inq != NULL) {
                g2inqrec = g2rlist->rec_inq;
                break;
            }
	    g2rlist = g2rlist->next;
        }
	if (!g2inqrec) {
		NhlPError(NhlFATAL, NhlEUNKNOWN,
			  "NclGRIB2.c: Variable contains no records.");
		return;
	}
	g2plist->var_info.var_name_quark = NrmNULLQUARK;
	if (g2inqrec->table_source && ! strcmp(g2inqrec->table_source,"tigge")) {
		i = 0;
		while (tigge_info[i].flag != -1) {
			TiggeInfo *tip = &tigge_info[i];
			i++;
			if (tip->disc != trp->discipline)
				continue;
			if (tip->pcat != trp->param_cat)
				continue;
			if (tip->pnum != trp->param_number)
				continue;
			if (tip->flag == 2 && tip->tstat != trp->stat_proc_type)
				continue;
			if (tip->flag != 6 && tip->toffs != trp->first_level_type)
				continue;
			if ((tip->flag == 3 || tip->flag == 4) && tip->tosfs != trp->second_level_type)
				continue;
			sprintf(buf,"%s_P%d_L%d",tip->name,trp->pds_template,trp->first_level_type);
			g2plist->var_info.var_name_quark = NrmStringToQuark(buf);
			g2plist->var_info.long_name_q = NrmStringToQuark(tip->desc);
			g2plist->var_info.units_q = NrmStringToQuark(tip->unit);
			g2plist->var_info.param_q = NrmStringToQuark(tip->name);
			break;
		}
		if (g2plist->prob_type > -1) {
			sprintf(buf,"%s",NrmQuarkToString(g2plist->var_info.long_name_q));
			if (! strstr(buf,"probability")) {
				sprintf(&buf[strlen(buf)]," probability");
			}
			g2plist->var_info.long_name_q = NrmStringToQuark(buf);
		}
		if (g2plist->var_info.var_name_quark > NrmNULLQUARK)
			return;
	}
	ct = (g2codeTable *) NclMalloc(1 * sizeof(g2codeTable));
	if (ct == NULL) {
		NhlPError(NhlFATAL,ENOMEM,NULL);
		return;
	}
	memset(ct,0,sizeof(g2codeTable));
	sprintf(buf, "4.2.%d.%d.table", trp->discipline,trp->param_cat);
	cterr = Grib2ReadCodeTable(g2inqrec->table_source, 4, buf,trp->param_number,-1,ct);
	if (cterr < NhlWARNING) {
		return;
	}
	if (ct->oct != -1) {
                /* found parameter in table */
		g2plist->var_info.long_name_q = NrmStringToQuark(ct->descrip);

		if (ct->shname != NULL) {
			g2plist->var_info.param_q = NrmStringToQuark(ct->shname);
			if (trp->first_level_type == 255) {
				/* no valid level */
				sprintf(buf,"%s_P%d",ct->shname,trp->pds_template);
			}
			else if (trp->second_level_type == 255) {
				/* only one valid level */
				sprintf(buf,"%s_P%d_L%d",ct->shname,trp->pds_template,trp->first_level_type);
			}
			else if (trp->second_level_type == trp->first_level_type) {
				/* 2 levels of the same type */
				sprintf(buf,"%s_P%d_2L%d",ct->shname,trp->pds_template,trp->first_level_type);
			}
			else {
				/* 2 levels of different types */
				sprintf(buf,"%s_P%d_2L%d_%d",
					ct->shname,trp->pds_template,trp->first_level_type,trp->second_level_type);
			}
		} else {
			sprintf(buf, "VAR_%d_%d_%d_P%d",
				trp->discipline,trp->param_cat,trp->param_number,
				trp->pds_template);
			g2plist->var_info.param_q = NrmStringToQuark(buf);
			if (trp->first_level_type == 255) {
				/* no valid level */
				sprintf(buf, "VAR_%d_%d_%d_P%d",
					trp->discipline,trp->param_cat,trp->param_number,
					trp->pds_template);
			}
			else if (trp->second_level_type == 255) {
				/* only one valid level */
				sprintf(buf, "VAR_%d_%d_%d_P%d_L%d",
					trp->discipline,trp->param_cat,trp->param_number,
					trp->pds_template,trp->first_level_type);
			}
			else if (trp->second_level_type == trp->first_level_type) {
				/* 2 levels of the same type */
				sprintf(buf, "VAR_%d_%d_%d_P%d_2L%d",
					trp->discipline,trp->param_cat,trp->param_number,
					trp->pds_template,trp->first_level_type);
			}
			else {
				/* 2 levels of different types */
				sprintf(buf, "VAR_%d_%d_%d_P%d_2L%d_%d",
					trp->discipline,trp->param_cat,trp->param_number,
					trp->pds_template,trp->first_level_type,trp->second_level_type);
			}
		}
		g2plist->var_info.var_name_quark = NrmStringToQuark(buf);

		if (ct->units != NULL) {
			g2plist->var_info.units_q = NrmStringToQuark(ct->units);

		} else {
			g2plist->var_info.units_q = NrmStringToQuark("unknown");
		}
	} else {
                /* parameter not found */
		g2plist->var_info.long_name_q = NrmStringToQuark("unknown variable name");
		g2plist->var_info.units_q = NrmStringToQuark("unknown");
		sprintf(buf, "VAR_%d_%d_%d_P%d",
			trp->discipline,trp->param_cat,trp->param_number,trp->pds_template);
		g2plist->var_info.param_q = NrmStringToQuark(buf);
		if (trp->first_level_type == 255) {
			/* no valid level */
			sprintf(buf, "VAR_%d_%d_%d_P%d",
				trp->discipline,trp->param_cat,trp->param_number,
				trp->pds_template);
		}
		else if (trp->second_level_type == 255) {
			/* only one valid level */
			sprintf(buf, "VAR_%d_%d_%d_P%d_L%d",
				trp->discipline,trp->param_cat,trp->param_number,
				trp->pds_template,trp->first_level_type);
		}
		else if (trp->second_level_type == trp->first_level_type) {
			/* 2 levels of the same type */
			sprintf(buf, "VAR_%d_%d_%d_P%d_2L%d",
				trp->discipline,trp->param_cat,trp->param_number,
				trp->pds_template,trp->first_level_type);
		}
		else {
			/* 2 levels of different types */
			sprintf(buf, "VAR_%d_%d_%d_P%d_2L%d_%d",
				trp->discipline,trp->param_cat,trp->param_number,
				trp->pds_template,trp->first_level_type,trp->second_level_type);
		}
		g2plist->var_info.var_name_quark = NrmStringToQuark(buf);
	}
	if (g2plist->prob_type > -1) {
		sprintf(buf,"%s",NrmQuarkToString(g2plist->var_info.long_name_q));
		if (! strstr(buf,"probability")) {
			sprintf(&buf[strlen(buf)]," probability");
		}
		g2plist->var_info.long_name_q = NrmStringToQuark(buf);
	}
	Grib2FreeCodeTableRec(ct);
	return;
}
	
		
void _g2_seekgb(FILE *lugb,size_t iseek,size_t mseek,size_t *lskip,g2int *lgrib)

/* taken from the NCEP g2clib and modified by dib at NCAR to support >2GB files */

{
	g2int  ret;
	g2int k,k4,nread,lim,start,vers,lengrib;
	size_t ipos;
	int    end;
	unsigned char *cbuf;
	g2int  max_bytes;

	max_bytes = *lgrib;
	*lgrib=0;
	cbuf=(unsigned char *)malloc(mseek);
	nread=mseek;
	ipos=iseek;


	while (*lgrib==0 && nread==mseek) {

		ret=fseek(lugb,ipos,SEEK_SET);
		nread=fread(cbuf,sizeof(unsigned char),mseek,lugb);
		lim=nread-8;


		for (k=0;k<lim;k++) {
			gbit(cbuf,&start,(k+0)*8,4*8);
			gbit(cbuf,&vers,(k+7)*8,1*8);
			if (start==1196575042 && (vers==1 || vers==2)) {

				if (vers == 1) gbit(cbuf,&lengrib,(k+4)*8,3*8);
				if (vers == 2) gbit(cbuf,&lengrib,(k+12)*8,4*8);
				ret=fseek(lugb,ipos+k+lengrib-4,SEEK_SET);

				k4=fread(&end,4,1,lugb);
				if (k4 == 1 && end == 926365495) { 
					*lskip=ipos+k;
					*lgrib=lengrib;
					break;
				}
			}
		}
		ipos=ipos+lim;
		if (ipos > max_bytes)
			return;
	}

	free(cbuf);
	return;
}


static void *Grib2OpenFile
# if    NhlNeedProto
(void *rec, NclQuark path, int wr_status)
# else
(rec, path, wr_status)
    void    *rec;
    NclQuark path;
    int wr_status;
# endif /* NhlNeedProto */
{
    FILE    *fd;
    int err,
        i,
        j,
        k;

    /* g2clib API variables */
    unsigned char   *g2buf;
    gribfield   *g2fld;
    /* 
     * Note that g2int is bizarrely defined as long for 32 bit systems but as int for 64 bit systems
     * Therefore is is really always just a 32 bit integer.
     */

    g2int   sec0[3],
            sec1[13];

    g2int   nfields,
            nlocal;
    g2int   expand = 0;

    int unpack = 0;
    g2int   lgrib;
    size_t  lskip,
            seek = 0;

    ng_size_t  lengrib;
    int nrecs = 0,
        t_nrecs = 0;

    /* GRIB2 records */
    G2Rec   **g2rec = NULL;

    /* codetable variables */
    g2codeTable *ct = NULL;
    NhlErrorTypes cterr = 0;

    int ctflen = 0;
    char    *center = NULL,              /* center name */
            *center_name = NULL,
            *subcenter_name = NULL;      /* subcenter name */
            
    int center_len = 0;
    int centerID = -1,
        subcenterID = -1;                /* center, subcenter IDs */

    int secid = 0;

    char    tmp_dataDate[16],
            tmp_dataTime[16];


    /* NCL GRIB2 records */
    Grib2FileRecord *g2frec;
    Grib2RecordInqRec   *g2inqrec = NULL;
    Grib2RecordInqRecList   *g2inqrec_list = NULL,
                            **g2sort = NULL;

    Grib2ParamList  *g2plist = NULL,
                    *g2plist_n = NULL,
                    *g2plist_tmp = NULL;
    static int first = True;
    unsigned long long total_offset;

    if (first) {
	    _DateInit();
	    first = False;
    }

    /* read-only access at this time */
    if (wr_status <= 0) {
        NhlPError(NhlWARNING, NhlEUNKNOWN,
            "GRIB v2 files are read only; opening file read only.");
	}

    /* GRIB v2 records */
    g2rec = (G2Rec **) NclMalloc((nrecs + 1) * sizeof(G2Rec *));
    if (g2rec == NULL) {
        NhlPError(NhlFATAL, NhlEUNKNOWN,
            "Could not allocate memory for GRIB v2 data.");
            Grib2FreeGrib2Rec(g2rec);
            return NULL;
    }
    g2rec[nrecs] = NclMalloc(sizeof(G2Rec));
    if (g2rec[nrecs] == NULL) {
        NhlPError(NhlFATAL, NhlEUNKNOWN,
            "Could not allocate memory for GRIB v2 data entries.");
            NhlFree(g2rec[nrecs]);
            return NULL;
    }

    /*
     * Code table directory
     */

    grib2_codetable_dir = getenv("NIO_GRIB2_CODETABLES");
    if (grib2_codetable_dir == NULL) {
	    grib2_codetable_dir = _NGGetNCARGEnv("grib2_codetables");
    }
    if (grib2_codetable_dir == NULL) {
        NhlPError(NhlFATAL, NhlEUNKNOWN,
            " Unable to locate GRIB v2 code tables, cannot continue.");
        NhlFree(g2rec[nrecs]);
        return NULL;
    }

    g2_codetable_dirname_len = strlen(grib2_codetable_dir);
    ct = (g2codeTable *) NclMalloc(1 * sizeof(g2codeTable));
    if (ct == NULL) {
        NhlPError(NhlFATAL, NhlEUNKNOWN,
            " Unable to allocate code table data, cannot continue.");
        NhlFree(g2rec[nrecs]);
        return NULL;
    }
    memset(ct,0,sizeof(g2codeTable));
    fd = fopen(NrmQuarkToString(path), "r");
    vbuf = (void *) NclMalloc(4 * getpagesize());
    setvbuf(fd, vbuf, _IOFBF, 4 * getpagesize());

    (void) fseek(fd, 0L, SEEK_SET);

    /*
     * Loop until end of data and/or EOF
     */
    t_nrecs = nrecs;
    for (;;) {
	/* lgrib is now in and out; setting it to 0 causes _g2_seekgb to
	   look all the way through a file to find GRIB records. Setting it
           to a positive value makes _g2_seekgb to return failure if more
	   than that many bytes are read without finding a valid GRIB record. */

	lgrib = 0;
	_g2_seekgb(fd, seek, (size_t)32 * GBUFSZ_T, &lskip, &lgrib);
        /* EOF or other problem? */
        if (lgrib == 0)
            break;
	total_offset = lskip + lgrib * 2;
/*
        DIB: Removed this restriction: 2014-01-06
	if (total_offset > INT_MAX) {
                NhlPError(NhlWARNING, NhlEUNKNOWN,
			  "Grib2OpenFile: GRIB2 files larger than 2GB not supported: records whose end of record position is greater than 2GB will not be read");
		break;
	}
*/

        g2buf = (unsigned char *) NclMalloc((ng_size_t) lgrib);
        if (g2buf == NULL) {
            NhlPError(NhlFATAL, NhlEUNKNOWN,
                "Could not allocate memory for GRIB v2 raw data.");
                NhlFree(g2rec);
                return NULL;
        }
		
        err = fseek(fd, lskip, SEEK_SET);
        lengrib = fread(g2buf, sizeof(unsigned char), lgrib, fd);
        seek = lskip + lgrib;

        err = g2_info(g2buf, sec0, sec1, &nfields, &nlocal);
        
        if (nrecs > t_nrecs) {
            ++t_nrecs;
            g2rec = NclRealloc(g2rec, (nrecs + 1) * sizeof(G2Rec *));
            if (g2rec == NULL) {
                NhlPError(NhlFATAL, NhlEUNKNOWN,
                "Could not extend memory for GRIB v2 data.");
                NhlFree(g2rec);
                return NULL;
            }
            g2rec[nrecs] = NclMalloc(sizeof(G2Rec));
            if (g2rec[nrecs] == NULL) {
                NhlPError(NhlFATAL, NhlEUNKNOWN,
                    "Could not allocate temporary memory for GRIB v2 data.");
                    NhlFree(g2rec);
                    return NULL;
            }

        }

        g2rec[nrecs]->offset = lskip;
        g2rec[nrecs]->rec_size = lgrib;

        /* GRIB v2 Section 0 */
        secid = 0;
        (void) strcpy(g2rec[nrecs]->sec0.gid, "GRIB");
        g2rec[nrecs]->sec0.secid = 0;
        g2rec[nrecs]->sec0.discipline = sec0[0];
        g2rec[nrecs]->sec0.edition = sec0[1];
        g2rec[nrecs]->sec0.msglen = sec0[2];

        /* GRIB v2 Section 1 */
        secid = 1;
        g2rec[nrecs]->sec1.secid = 1;
        g2rec[nrecs]->sec1.centerID = sec1[0];
        g2rec[nrecs]->sec1.subcenterID = sec1[1];

        /*
         * Originating center
         *
         * If center (subcenter) are the same as previous record(s), no need
         * to reopen/search the centers table.
         */
        if (centerID == sec1[0]) {
            g2rec[nrecs]->sec1.center_name = NclMalloc(strlen(center_name) + 1);
            (void) strcpy(g2rec[nrecs]->sec1.center_name, center_name);

            if (subcenter_name != NULL) {
                g2rec[nrecs]->sec1.subcenter_name = NclMalloc(strlen(subcenter_name) + 1);
                (void) strcpy(g2rec[nrecs]->sec1.subcenter_name, subcenter_name);
            } else {
                g2rec[nrecs]->sec1.subcenter_name = NULL;
            }
        } else {
		cterr = Grib2ReadCodeTable("", -1, "centers.table", g2rec[nrecs]->sec1.centerID,-1,ct);
            if (cterr < NhlWARNING) {
                NhlFree(g2rec);
                return NULL;
            }
		    

	    if (! ct->descrip) {
		    g2rec[nrecs]->sec1.center_name = NclMalloc(strlen("Unknown Center ( )") + 5);
		    sprintf( g2rec[nrecs]->sec1.center_name,"Unknown Center (%d)",(int)sec1[0]);
	    }
	    else {
		    g2rec[nrecs]->sec1.center_name = NclMalloc(strlen(ct->descrip) + 1);
		    (void) strcpy(g2rec[nrecs]->sec1.center_name, ct->descrip);
	    }

            center_name = NclMalloc(strlen(g2rec[nrecs]->sec1.center_name) + 1);
            (void) strcpy(center_name, g2rec[nrecs]->sec1.center_name);

            /* if center has a "short name" */
            if (ct->shname != NULL) {
                g2rec[nrecs]->sec1.subcenter_name = NclMalloc(strlen(ct->shname) + 1);
                if (g2rec[nrecs]->sec1.subcenter_name == NULL) {
                    NhlPError(NhlFATAL, NhlEUNKNOWN,
                        "Could not allocate memory for GRIB v2 entry data.");
                        NhlFree(g2rec);
                        return NULL;
                }
        
                (void) strcpy(g2rec[nrecs]->sec1.subcenter_name, ct->shname);

                subcenter_name = NclMalloc(strlen(ct->shname) + 1);
                (void) strcpy(subcenter_name, ct->shname);
            } else {
                g2rec[nrecs]->sec1.subcenter_name = NULL;
                subcenter_name = NULL;
            }

            /* 
             * Set originating center info for codetable searches.
             * NOTE: not all known centers are represented, only the most commonly
             * used as per GRIB v1 usage in NCL.  Add as necessary.
             * DB: Now default is to try to use NCEP tables if center is unrecognized.
             */
            if (sec1[11] == 4 || sec1[11] == 5) {
                /*tigge data -- use tigge tables regardless of the actual center */
                center = "tigge";
                center_len = strlen(center);
                centerID = sec1[0];
                subcenterID = sec1[1];
            }
            else if (! center) {
		    /* for now just use ECMWF or NCEP tables if not TIGGE */
		    /* default to NCEP -- maybe it should be ECMWF */
                switch (sec1[0]) {
                    case 7:
                    case 8:
                    case 9:
                        /* US Navy FNMOC */
                    case 58:
                        /* NOAA FSL */
                    case 59:
                        /* NCAR */
                    case 60:
    	            default:
                        /* NCEP */
                        center = "ncep";
                        center_len = strlen(center);
                        centerID = sec1[0];
                        subcenterID = sec1[1];
                        break;


                    case 74:
                        /* UK Met. Office */
                        /* fallthrough */

                    case 78:
                        /* DWD Offenbach */
                        /* fallthrough */
                    case 34:
                        /* JMA -- use ECMWF for now */
                        /* FALLTHROUGH */

		    case 254:
			    /* EUMETSAT - fallthrough */
                    case 98:
                        /* ECMWF */
                        center = "ecmwf";
                        center_len = strlen(center);
                        centerID = sec1[0];
                        subcenterID = sec1[1];
                        break;
                }

                /* codetable filename base length: length of center name + 1 (section ID) */
	    }
	    ctflen = center_len + 1;
        }
        g2rec[nrecs]->table_source_name = NclMalloc(ctflen);
        strcpy(g2rec[nrecs]->table_source_name,center);
        g2rec[nrecs]->sec1.master_table_ver = sec1[2];
        g2rec[nrecs]->sec1.local_table_ver = sec1[3];

        /* table 1.2: Significance of Reference Time */
        g2rec[nrecs]->sec1.ref_time = sec1[4];
        g2rec[nrecs]->sec1.sig_ref_time = NULL;
#if 0
        table = "1.2.table";
        cterr = Grib2ReadCodeTable(center, secid, table, g2rec[nrecs]->sec1.ref_time,-1,ct);
        if (cterr < NhlWARNING) {
            NhlFree(g2rec);
            return NULL;
        }

        g2rec[nrecs]->sec1.sig_ref_time = NclMalloc(strlen(ct->descrip) + 1);
        if (g2rec[nrecs]->sec1.sig_ref_time == NULL) {
            NhlPError(NhlFATAL, NhlEUNKNOWN,
                "Could not allocate memory for GRIB v2 data entry.");
                NhlFree(g2rec);
                return NULL;
        }

        (void) strcpy(g2rec[nrecs]->sec1.sig_ref_time, ct->descrip);
#endif

        g2rec[nrecs]->sec1.date_time.year = sec1[5];
        g2rec[nrecs]->sec1.date_time.mon = sec1[6];
        g2rec[nrecs]->sec1.date_time.day = sec1[7];
        g2rec[nrecs]->sec1.date_time.hour = sec1[8];
        g2rec[nrecs]->sec1.date_time.min = sec1[9];
        g2rec[nrecs]->sec1.date_time.sec = sec1[10];
        (void) sprintf(tmp_dataDate, "%d%02d%02d", (int)sec1[5], (int)sec1[6], (int)sec1[7]);
        g2rec[nrecs]->sec1.date_time.dataDate = strtol(tmp_dataDate, NULL, 10);
        (void) sprintf(tmp_dataTime, "%02d%02d", (int)sec1[8], (int)sec1[9]);
        g2rec[nrecs]->sec1.date_time.dataTime = (int) strtol(tmp_dataTime, NULL, 10);

        /* table 1.3: Production Status of Data */
        g2rec[nrecs]->sec1.prod_status = sec1[11];
        g2rec[nrecs]->sec1.proc_prod_status = NULL;
#if 0
        table = "1.3.table";
        cterr = Grib2ReadCodeTable(center, secid, table, g2rec[nrecs]->sec1.prod_status, -1,ct);
        if (cterr < NhlWARNING) {
            NhlFree(g2rec);
            return NULL;
        }

        g2rec[nrecs]->sec1.proc_prod_status = NclMalloc(strlen(ct->descrip) + 1);
        (void) strcpy(g2rec[nrecs]->sec1.proc_prod_status, ct->descrip);
#endif

        /* table 1.4: Type of Data */
        g2rec[nrecs]->sec1.data_type = sec1[12];
        g2rec[nrecs]->sec1.proc_data_type = NULL;
#if 0
        table = "1.4.table";
        cterr = Grib2ReadCodeTable(center,secid,table, g2rec[nrecs]->sec1.data_type,-1,ct);
        if (cterr < NhlWARNING) {
            NhlFree(g2rec);
            return NULL;
        }

        if (ct->descrip) {
            g2rec[nrecs]->sec1.proc_data_type = NclMalloc(strlen(ct->descrip) + 1);
            (void) strcpy(g2rec[nrecs]->sec1.proc_data_type, ct->descrip);
        } else {
            g2rec[nrecs]->sec1.proc_data_type = NULL;
        }
#endif

        /*
         * Get GRIB v2 sections 2 thru 7.  These sections may be repeated
         * for each record.
         *
         * Variable 'nfields' is number of times sections are repeated.
         * Allocate space for sections 2 thru 7 now.
         */
        g2rec[nrecs]->sec2 = NclMalloc(nfields * sizeof(G2Sec2 *));
        if (g2rec[nrecs]->sec2 == NULL) {
            NhlPError(NhlFATAL, NhlEUNKNOWN,
            "Could not extend memory for GRIB v2 data.");
            NhlFree(g2rec);
            return NULL;
        }

        g2rec[nrecs]->sec3 = NclMalloc(nfields * sizeof(G2Sec3 *));
        if (g2rec[nrecs]->sec3 == NULL) {
            NhlPError(NhlFATAL, NhlEUNKNOWN,
            "Could not extend memory for GRIB v2 data.");
            NhlFree(g2rec);
            return NULL;
        }

        g2rec[nrecs]->sec4 = NclMalloc(nfields * sizeof(G2Sec4 *));
        if (g2rec[nrecs]->sec4 == NULL) {
            NhlPError(NhlFATAL, NhlEUNKNOWN,
            "Could not extend memory for GRIB v2 data.");
            NhlFree(g2rec);
            return NULL;
        }

        g2rec[nrecs]->sec5 = NclMalloc(nfields * sizeof(G2Sec5 *));
        if (g2rec[nrecs]->sec5 == NULL) {
            NhlPError(NhlFATAL, NhlEUNKNOWN,
            "Could not extend memory for GRIB v2 data.");
            NhlFree(g2rec);
            return NULL;
        }

        g2rec[nrecs]->sec6 = NclMalloc(nfields * sizeof(G2Sec6 *));
        if (g2rec[nrecs]->sec6 == NULL) {
            NhlPError(NhlFATAL, NhlEUNKNOWN,
            "Could not extend memory for GRIB v2 data.");
            NhlFree(g2rec);
            return NULL;
        }

        g2rec[nrecs]->sec7 = NclMalloc(nfields * sizeof(G2Sec7 *));
        if (g2rec[nrecs]->sec7 == NULL) {
            NhlPError(NhlFATAL, NhlEUNKNOWN,
            "Could not extend memory for GRIB v2 data.");
            NhlFree(g2rec);
            return NULL;
        }

        for (i = 0; i < nfields; i++) {
            g2rec[nrecs]->sec2[i] = NclMalloc(sizeof(G2Sec2));
            memset(g2rec[nrecs]->sec2[i], 0, sizeof(G2Sec2));
            g2rec[nrecs]->sec3[i] = NclMalloc(sizeof(G2Sec3));
            memset(g2rec[nrecs]->sec3[i], 0, sizeof(G2Sec3));
            g2rec[nrecs]->sec4[i] = NclMalloc(sizeof(G2Sec4));
            memset(g2rec[nrecs]->sec4[i], 0, sizeof(G2Sec4));
            g2rec[nrecs]->sec5[i] = NclMalloc(sizeof(G2Sec5));
            memset(g2rec[nrecs]->sec5[i], 0, sizeof(G2Sec5));
            g2rec[nrecs]->sec6[i] = NclMalloc(sizeof(G2Sec6));
            memset(g2rec[nrecs]->sec6[i], 0, sizeof(G2Sec6));
            g2rec[nrecs]->sec7[i] = NclMalloc(sizeof(G2Sec7));
            memset(g2rec[nrecs]->sec7[i], 0, sizeof(G2Sec7));
        }
        g2rec[nrecs]->num_rptd = nfields;

        /* loop over repeated sections, creating new records */
        for (i = 0; i < nfields; i++) {
            int valid_end_time_set = 0;
            err = g2_getfld(g2buf, i + 1, unpack, expand, &g2fld);
            if (err != 0) {
                /*
                 * g2clib could not correctly unpack the GRIB2 record
                 * error codes listed in g2clib source file g2_getfld.c
                 * g2clib will issue its own error message
                 */
                switch (err) {
                    case 1:
                        NhlPError(NhlWARNING, NhlEUNKNOWN,
                            "Begin record characters \"GRIB\" not found.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 2:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Not a GRIB2 record.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 3:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Invalid data field request.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 4:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "End record characters at invalid location.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 6:
                        NhlPError(NhlWARNING, NhlEUNKNOWN,
                            "GRIB record does not contain requested data fields.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 7:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "End record characters not found.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 8:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Unrecognized section.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 9:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Data Representation Template not yet implemented.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 10:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Error unpacking GRIB record section 3.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 11:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Error unpacking GRIB record section 4.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 12:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Error unpacking GRIB record section 5.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 13:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Error unpacking GRIB record section 6.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 14:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Error unpacking GRIB record section 7.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 15:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Error unpacking GRIB record section 1.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 16:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Error unpacking GRIB record section 2.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    case 17:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Specified bitmap does not exist.");
                            NhlFree(g2rec);
                            return NULL;
                            break;

                    default:
                        NhlPError(NhlWARNING, NhlEUNKNOWN, "Could not decode GRIB record.");
                            NhlFree(g2rec);
                            return NULL;
                        break;
                }
            }

            g2rec[nrecs]->version = g2fld->version;

            /* GRIB2 section 2 */
            secid = 2;
            g2rec[nrecs]->sec2[i]->secid = 2;
            g2rec[nrecs]->sec2[i]->locallen = g2fld->locallen;
            if (g2rec[nrecs]->sec2[i]->locallen > 0) {
                g2rec[nrecs]->sec2[i]->local = NclMalloc(g2fld->locallen + 1);
                memcpy(g2rec[nrecs]->sec2[i]->local, g2fld->local, g2fld->locallen);
            } else{
                g2rec[nrecs]->sec2[i]->local = NULL;
            }

            /* GRIB2 section 3   GDS */
            secid = 3;
            g2rec[nrecs]->sec3[i]->secid = 3;

#if 0
	    /* not much reason to read this table since we just using the octet number anyway
	       to figure out what to do next */
            /* table 3.0: Source of Grid Defn */
            table = "3.0.table";
            cterr = Grib2ReadCodeTable(center, secid, table, g2rec[nrecs]->sec3[i]->grid_def_src,-1,ct);
            if (cterr < NhlWARNING) {
                NhlFree(g2rec);
                return;
            }
#endif

            g2rec[nrecs]->sec3[i]->grid_def_src = g2fld->griddef;
            g2rec[nrecs]->sec3[i]->grid_def_name = NULL;
            g2rec[nrecs]->sec3[i]->grid_num = g2fld->igdtnum;
#if 0
            switch ( g2fld->griddef) {
                case 0:
                    /* table 3.1: Grid Defn Template Num */
                    table = "3.1.table";
                    cterr = Grib2ReadCodeTable(center, secid, table, g2fld->igdtnum,-1,ct);
                    if (cterr < NhlWARNING) {
                        NhlFree(g2rec);
                        return NULL;
                    }

                    g2rec[nrecs]->sec3[i]->grid_def_name = NclMalloc(strlen(ct->descrip) + 1);
                    (void) strcpy(g2rec[nrecs]->sec3[i]->grid_def_name, ct->descrip);
                    break;

                case 1:
                    /* grid def det'd by originating center */
                    break;

                case 255:
                    /* grid defn doesn't apply */
                    g2rec[nrecs]->sec3[i]->grid_num = ct->oct;  /* 255 */
                    g2rec[nrecs]->sec3[i]->grid_def_name
                            = NclMalloc(strlen("A grid definition doesn't apply.") + 1);
                    (void) strcpy(g2rec[nrecs]->sec3[i]->grid_def_name,
                            "A grid definition doesn't apply.");

                    /* FALLTHROUGH */
                default:
                    /* grid def doesn't apply */
                    break;
            }
#endif

            g2rec[nrecs]->sec3[i]->num_grid_data_pts = g2fld->ngrdpts;
            g2rec[nrecs]->sec3[i]->num_oct_opt = g2fld->numoct_opt;

#if 0
            /* table 3.11: Interpretation of List of Numbers Defining Number of Pts */
            g2rec[nrecs]->sec3[i]->interp_opt_num_pts = g2fld->interp_opt;
            table = "3.11.table";
            cterr = Grib2ReadCodeTable(center, secid, table,
				       g2rec[nrecs]->sec3[i]->interp_opt_num_pts,-1,ct);
            if (cterr < NhlWARNING) {
                NhlFree(g2rec);
                return NULL;
            }

            g2rec[nrecs]->sec3[i]->interp_opt_name = NclMalloc(strlen(ct->descrip) + 1);
            (void) strcpy(g2rec[nrecs]->sec3[i]->interp_opt_name, ct->descrip);
#endif

            g2rec[nrecs]->sec3[i]->grid_def_templ_num = g2fld->igdtnum;

            if (g2fld->numoct_opt != 0) {
                /* non-regular grid */
                g2rec[nrecs]->sec3[i]->grid_list_num_oct_num = g2fld->num_opt;
                g2rec[nrecs]->sec3[i]->grid_list_num_oct_opt
                        = NclMalloc(g2fld->num_opt * sizeof(int));
                memcpy(g2rec[nrecs]->sec3[i]->grid_list_num_oct_opt, g2fld->list_opt,
                        g2fld->num_opt * sizeof(int));
            } else {
                g2rec[nrecs]->sec3[i]->grid_list_num_oct_num = g2fld->num_opt;
                g2rec[nrecs]->sec3[i]->grid_list_num_oct_opt = NULL;
            }
            g2rec[nrecs]->sec3[i]->len_grid_template = g2fld->igdtlen;
            /* 
             * rather than malloc a new template data area, just copy the pointer
             * and then NULL it out so it wont get freed prematurely. 
             * consider whether we need to use g2int (it seems like it's always 
             * 32 bits ; so I'm not sure why)
             */
            g2rec[nrecs]->sec3[i]->grid_template = (int *)g2fld->igdtmpl; 
            g2fld->igdtmpl = NULL;
            g2rec[nrecs]->sec3[i]->is_thinned_grid = 0; /* this is determined later */
            g2rec[nrecs]->sec3[i]->scan_mode_offset = -1; /* this is determined later */
	    

#if 0
            /* table 3.2: Shape of Earth */
            g2rec[nrecs]->sec3[i]->shape_of_earth = NclMalloc(sizeof(G2shapeOfEarth));
            g2rec[nrecs]->sec3[i]->res_comp = NclMalloc(sizeof(G2resComponentFlags));
            g2rec[nrecs]->sec3[i]->scan_mode = NclMalloc(sizeof(G2scanModeFlags));

            g2rec[nrecs]->sec3[i]->shape_of_earth->shapeOfEarth = g2fld->igdtmpl[0];

/* this info not used/necessary */
#if 0
            table = "3.2.table";
            cterr = Grib2ReadCodeTable(center, secid, table,
				       g2rec[nrecs]->sec3[i]->shape_of_earth->shapeOfEarth,-1,ct);
            if (cterr < NhlWARNING) {
                NhlFree(g2rec);
                return NULL;
            }

            g2rec[nrecs]->sec3[i]->shape_of_earth->earthShape = NclMalloc(strlen(ct->descrip) + 1);
            (void) strcpy(g2rec[nrecs]->sec3[i]->shape_of_earth->earthShape, ct->descrip);
#endif
            g2rec[nrecs]->sec3[i]->shape_of_earth->scale_factor_rad_sph_earth
                    = (int) g2fld->igdtmpl[1];
            g2rec[nrecs]->sec3[i]->shape_of_earth->scaled_val_rad_sph_earth
                    = (int) g2fld->igdtmpl[2];
            g2rec[nrecs]->sec3[i]->shape_of_earth->scale_factor_maj_axis_obl_sph_earth
                    = (int) g2fld->igdtmpl[3];
            g2rec[nrecs]->sec3[i]->shape_of_earth->scaled_val_maj_axis_obl_sph_earth
                    = (int) g2fld->igdtmpl[4];
            g2rec[nrecs]->sec3[i]->shape_of_earth->scale_factor_min_axis_obl_sph_earth
                    = (int) g2fld->igdtmpl[5];
            g2rec[nrecs]->sec3[i]->shape_of_earth->scaled_val_min_axis_obl_sph_earth
                    = (int) g2fld->igdtmpl[6];
            g2rec[nrecs]->sec3[i]->shape_of_earth->npts_along_parallel = (int) g2fld->igdtmpl[7];
            g2rec[nrecs]->sec3[i]->shape_of_earth->npts_along_meridian = (int) g2fld->igdtmpl[8];
            g2rec[nrecs]->sec3[i]->shape_of_earth->angl_init_prod_domain = (int) g2fld->igdtmpl[9];
            g2rec[nrecs]->sec3[i]->shape_of_earth->subdiv_basic_angle = (int) g2fld->igdtmpl[10];

            g2rec[nrecs]->sec3[i]->shape_of_earth->lat_first_gridpt = g2fld->igdtmpl[11];
            g2rec[nrecs]->sec3[i]->shape_of_earth->lon_first_gridpt = g2fld->igdtmpl[12];

            g2rec[nrecs]->sec3[i]->res_comp->idir_given = g2getbits(g2fld->igdtmpl[13], 4, 1);
            g2rec[nrecs]->sec3[i]->res_comp->jdir_given = g2getbits(g2fld->igdtmpl[13], 5, 1);
            g2rec[nrecs]->sec3[i]->res_comp->uv_vectors
                    = (short) g2getbits(g2fld->igdtmpl[13], 6, 1);

            g2rec[nrecs]->sec3[i]->shape_of_earth->lat_last_gridpt = g2fld->igdtmpl[14];
            g2rec[nrecs]->sec3[i]->shape_of_earth->lon_last_gridpt = g2fld->igdtmpl[15];

            if (g2rec[nrecs]->sec3[i]->shape_of_earth->subdiv_basic_angle != 0 &&
                    g2rec[nrecs]->sec3[i]->shape_of_earth->angl_init_prod_domain != 0) {
                scale_factor = g2rec[nrecs]->sec3[i]->shape_of_earth->angl_init_prod_domain /
                (double) g2rec[nrecs]->sec3[i]->shape_of_earth->subdiv_basic_angle;
            }
            else {
                scale_factor = 1.0 / (double) G2_SCALE_FACTOR;
            }

            g2rec[nrecs]->sec3[i]->lat_first_gridpt = g2fld->igdtmpl[11] * scale_factor;
            g2rec[nrecs]->sec3[i]->lon_first_gridpt = g2fld->igdtmpl[12] * scale_factor;
            g2rec[nrecs]->sec3[i]->lat_last_gridpt = g2fld->igdtmpl[14]  * scale_factor;
            g2rec[nrecs]->sec3[i]->lon_last_gridpt = g2fld->igdtmpl[15]  * scale_factor;



            g2rec[nrecs]->sec3[i]->shape_of_earth->idir_incr = (int) g2fld->igdtmpl[16];
            g2rec[nrecs]->sec3[i]->shape_of_earth->jdir_incr = (int) g2fld->igdtmpl[17];
            g2rec[nrecs]->sec3[i]->shape_of_earth->idir_incr_scaled
                    = (float) g2fld->igdtmpl[16] * scale_factor;
            g2rec[nrecs]->sec3[i]->shape_of_earth->jdir_incr_scaled
                    = (float) g2fld->igdtmpl[17] * scale_factor;

            g2rec[nrecs]->sec3[i]->scan_mode->idir
                    = g2getbits(g2fld->igdtmpl[18], 7, 1);
            g2rec[nrecs]->sec3[i]->scan_mode->jdir
                    = g2getbits(g2fld->igdtmpl[18], 6, 1);

            /*
             * Set idir/jdir directional values.  Need these later for
             * computing non-regular ("quasi") grid lat/lon increments.
             */
            if (g2rec[nrecs]->sec3[i]->scan_mode->idir == 0)
                g2rec[nrecs]->sec3[i]->scan_mode->idir = 1;
            else
                g2rec[nrecs]->sec3[i]->scan_mode->idir = -1;

            if (g2rec[nrecs]->sec3[i]->scan_mode->jdir == 0)
                g2rec[nrecs]->sec3[i]->scan_mode->jdir = -1;
            else
                g2rec[nrecs]->sec3[i]->scan_mode->jdir = 1;

            g2rec[nrecs]->sec3[i]->scan_mode->adj_ijdir_consec
                    = g2getbits(g2fld->igdtmpl[18], 5, 1);
            g2rec[nrecs]->sec3[i]->scan_mode->scan_dir = g2getbits(g2fld->igdtmpl[18], 4, 1);
#endif

            /* GRIB2 section 4   PDS */
            secid = 4;
            g2rec[nrecs]->sec4[i]->secid = 4;
            /* table 4.0: Product Defn Template Number */
            g2rec[nrecs]->sec4[i]->pds_num = g2fld->ipdtnum;
            g2rec[nrecs]->sec4[i]->prod_def_name = NULL;
#if 0
            table = "4.0.table";
            cterr = Grib2ReadCodeTable(center, secid, table, g2rec[nrecs]->sec4[i]->pds_num,-1, ct);
            if (cterr < NhlWARNING) {
                NhlFree(g2rec);
                return NULL;
            }

            g2rec[nrecs]->sec4[i]->prod_def_name = NclMalloc(strlen(ct->descrip) + 1);
            (void) strcpy(g2rec[nrecs]->sec4[i]->prod_def_name, ct->descrip);
#endif

            /* table 4.1: Parameter Category by Product Discipline */
            g2rec[nrecs]->sec4[i]->prod_params = NclMalloc(sizeof(G2prodParams));
            memset(g2rec[nrecs]->sec4[i]->prod_params,0,sizeof(G2prodParams));
            if (g2fld->ipdtmpl != NULL)
                g2rec[nrecs]->sec4[i]->prod_params->param_cat = g2fld->ipdtmpl[0];
            else {
                NhlPError(NhlFATAL, NhlEUNKNOWN,
                    "NclGRIB2: Invalid Product Definition Template.");
                    NhlFree(g2rec);
                    return NULL;
            }
            g2rec[nrecs]->sec4[i]->prod_params->param_cat_name = NULL;
#if 0
            table = "4.1.table";
            cterr = Grib2ReadCodeTable(center, secid, table,
				       g2rec[nrecs]->sec4[i]->prod_params->param_cat,-1, ct);
            if (cterr < NhlWARNING) {
                NhlFree(g2rec);
                return NULL;
            }

            g2rec[nrecs]->sec4[i]->prod_params->param_cat_name
                    = NclMalloc(strlen(ct->descrip) + 1);
            (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->param_cat_name, ct->descrip);
#endif

            /*
             * table 4.2.x.y: Product Discipline
             * use sec0.discipline to form table name
             */
            if (g2fld->ipdtmpl != NULL)
                g2rec[nrecs]->sec4[i]->prod_params->param_num = g2fld->ipdtmpl[1];
            else {
                NhlPError(NhlFATAL, NhlEUNKNOWN,
                    "NclGRIB2: Invalid Product Definition Template.");
                    NhlFree(g2rec);
                    return NULL;
            }
#if 0
            memset(fnam, '\0', 256);
            (void) sprintf(fnam, "4.2.%d.%d.table", g2rec[nrecs]->sec0.discipline,
                    g2rec[nrecs]->sec4[i]->prod_params->param_cat);
            cterr = Grib2ReadCodeTable(center, secid, fnam,
				       g2rec[nrecs]->sec4[i]->prod_params->param_num,-1, ct);
            if (cterr < NhlWARNING) {
                NhlFree(g2rec);
                return NULL;
            }

            if (ct->oct != -1) {
                /* found parameter in table */
		    g2rec[nrecs]->sec4[i]->prod_params->param_name
			    = NclMalloc(strlen(ct->descrip) + 1);
		    (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->param_name, ct->descrip);

		    if (ct->shname != NULL) {
			    g2rec[nrecs]->sec4[i]->prod_params->short_name = NclMalloc(
				    strlen(ct->shname) + 1);
			    (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->short_name, ct->shname);
		    } else {
			    g2rec[nrecs]->sec4[i]->prod_params->short_name = NclMalloc(18 * sizeof(char));
			    (void) sprintf(g2rec[nrecs]->sec4[i]->prod_params->short_name, "VAR_%d_%d_%d",
					   g2rec[nrecs]->sec0.discipline,
					   g2rec[nrecs]->sec4[i]->prod_params->param_cat,
					   g2rec[nrecs]->sec4[i]->prod_params->param_num);
				   
		    }
		    if (ct->units != NULL) {
			    g2rec[nrecs]->sec4[i]->prod_params->units = NclMalloc(strlen(ct->units) + 1);
			    (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->units, ct->units);
		    } else {
			    g2rec[nrecs]->sec4[i]->prod_params->units = NclMalloc(strlen("unknown") + 1);
			    (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->units, "unknown");
		    }
            } else {
                /* parameter not found */
                g2rec[nrecs]->sec4[i]->prod_params->param_name
                        = NclMalloc(strlen("Unknown Variable Name") + 1);
                (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->param_name,
                        "Unknown Variable Name");

		g2rec[nrecs]->sec4[i]->prod_params->short_name = NclMalloc(18 * sizeof(char));
		(void) sprintf(g2rec[nrecs]->sec4[i]->prod_params->short_name, "VAR_%d_%d_%d",
			       g2rec[nrecs]->sec0.discipline,
			       g2rec[nrecs]->sec4[i]->prod_params->param_cat,
			       g2rec[nrecs]->sec4[i]->prod_params->param_num);
                g2rec[nrecs]->sec4[i]->prod_params->units = NclMalloc(strlen("unknown") + 1);
                (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->units, "unknown");
            }
#endif

            /* table 4.3: Type of Generating Process */
            if (g2fld->ipdtmpl != NULL)
                g2rec[nrecs]->sec4[i]->prod_params->gen_process = g2fld->ipdtmpl[2];
            else {
                NhlPError(NhlFATAL, NhlEUNKNOWN,
                    "NclGRIB2: Invalid Product Definition Template.");
                    NhlFree(g2rec);
                    return NULL;
            }
            g2rec[nrecs]->sec4[i]->prod_params->gen_proc_name = NULL;
#if 0
	    table = "4.3.table";
            cterr = Grib2ReadCodeTable(center, secid, table,
				       g2rec[nrecs]->sec4[i]->prod_params->gen_process,-1,ct);
            if (cterr < NhlWARNING) {
                NhlFree(g2rec);
                return NULL;
            }

            g2rec[nrecs]->sec4[i]->prod_params->gen_proc_name = NclMalloc(strlen(ct->descrip) + 1);
            (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->gen_proc_name, ct->descrip);
#endif
            if (g2fld->ipdtmpl != NULL) {
                g2rec[nrecs]->sec4[i]->prod_params->bkgd_gen_process = g2fld->ipdtmpl[3];
                g2rec[nrecs]->sec4[i]->prod_params->gen_processID = g2fld->ipdtmpl[4];
            } else {
                NhlPError(NhlFATAL, NhlEUNKNOWN,
                    "NclGRIB2: Invalid Product Definition Template.");
                    NhlFree(g2rec);
                    return NULL;
            }
	    g2rec[nrecs]->sec4[i]->prod_params->aerosol_type = 65535;
	    g2rec[nrecs]->sec4[i]->prod_params->size_interval_type = 255;
	    g2rec[nrecs]->sec4[i]->prod_params->wavelength_interval_type = 255;

            switch (g2rec[nrecs]->sec4[i]->pds_num) {
		    int offset;
	    case 20:
	    case 30:
	    case 31:
	    case 254:
		    g2rec[nrecs]->sec4[i]->prod_params->time_range_unit_id = 1;
		    g2rec[nrecs]->sec4[i]->prod_params->forecast_time = 0;
		    break;
	    case 53:
	    case 54:
		    g2rec[nrecs]->sec4[i]->prod_params->time_range_unit_id = 1;
		    g2rec[nrecs]->sec4[i]->prod_params->forecast_time = 0;
		    NhlPError(NhlWARNING, NhlEUNKNOWN, "NclGRIB2: Unsupported Product Definition Template.");
		    break;
	    default:
		    switch (g2rec[nrecs]->sec4[i]->pds_num) {
		    case 40:
		    case 41:
		    case 42:
		    case 43:
			    offset = 6;
			    break;
		    case 44:
		    case 45:
		    case 46:
		    case 47:
			    offset = 11;
			    break;
		    case 48:
			    offset = 16;
			    break;
		    default:
			    offset = 5;
		    }
			    
		    if (g2fld->ipdtmpl != NULL) {
			    g2rec[nrecs]->sec4[i]->prod_params->hrs_after_reftime_cutoff = g2fld->ipdtmpl[offset];
			    g2rec[nrecs]->sec4[i]->prod_params->min_after_reftime_cutoff = g2fld->ipdtmpl[offset+1];
			    /* table 4.4: Indicator of Unit of Time Range */
			    g2rec[nrecs]->sec4[i]->prod_params->time_range_unit_id = g2fld->ipdtmpl[offset+2];
			    g2rec[nrecs]->sec4[i]->prod_params->forecast_time = g2fld->ipdtmpl[offset+3];
		    } else {
			    NhlPError(NhlFATAL, NhlEUNKNOWN,
				      "NclGRIB2: Invalid Product Definition Template.");
			    NhlFree(g2rec);
			    return NULL;
		    }
		    g2rec[nrecs]->sec4[i]->prod_params->time_range_unit = NULL;

	    }
	    
            switch (g2rec[nrecs]->sec4[i]->pds_num) {
		    int level_off;
	    case 20:
	    case 30:
	    case 31:
	    case 254:
	    case 32:
	    case 33:
	    case 34:
	    case 1000:
	    case 1001:
	    case 1002:
		    g2rec[nrecs]->sec4[i]->prod_params->typeof_first_fixed_sfc = 255;
		    g2rec[nrecs]->sec4[i]->prod_params->typeof_second_fixed_sfc = 255;
		    break;
	    default:
		    switch (g2rec[nrecs]->sec4[i]->pds_num) {
		    case 48:
			    level_off = 20;
			    break;
		    case 47:
		    case 46:
		    case 45:
		    case 44:
			    level_off = 15;
			    break;
		    case 43:
		    case 42:
		    case 41:
		    case 40:
			    level_off = 10;
			    break;
		    default: 
			    level_off = 9;
			    break;
		    }

		    /* table 4.5: Fixed Surface Types and Units */
		    if (g2fld->ipdtmpl != NULL)
			    g2rec[nrecs]->sec4[i]->prod_params->typeof_first_fixed_sfc = g2fld->ipdtmpl[level_off];
		    else {
			    NhlPError(NhlFATAL, NhlEUNKNOWN,
				      "NclGRIB2: Invalid Product Definition Template.");
			    NhlFree(g2rec);
			    return NULL;
		    }
		    g2rec[nrecs]->sec4[i]->prod_params->first_fixed_sfc = NULL;
		    g2rec[nrecs]->sec4[i]->prod_params->units_first_fixed_sfc = NULL;

                    if (g2fld->ipdtmpl != NULL) {
                            g2rec[nrecs]->sec4[i]->prod_params->scale_factor_first_fixed_sfc = g2fld->ipdtmpl[level_off + 1];
	    	    	if (g2fld->ipdtmpl[level_off + 1] == -127)
		    		    g2rec[nrecs]->sec4[i]->prod_params->scaled_val_first_fixed_sfc = 0;
		        	else
			        	g2rec[nrecs]->sec4[i]->prod_params->scaled_val_first_fixed_sfc
				        	= g2fld->ipdtmpl[level_off + 2];
            	    } else {
                	    NhlPError(NhlFATAL, NhlEUNKNOWN,
                    		"NclGRIB2: Invalid Product Definition Template.");
                    	    NhlFree(g2rec);
                    	    return NULL;
            	    }

		    if (g2fld->ipdtmpl != NULL)
			    g2rec[nrecs]->sec4[i]->prod_params->typeof_second_fixed_sfc = g2fld->ipdtmpl[level_off +3];
		    else {
			    NhlPError(NhlFATAL, NhlEUNKNOWN,
				      "NclGRIB2: Invalid Product Definition Template.");
			    NhlFree(g2rec);
			    return NULL;
		    }
		    g2rec[nrecs]->sec4[i]->prod_params->second_fixed_sfc = NULL;
		    g2rec[nrecs]->sec4[i]->prod_params->units_second_fixed_sfc = NULL;
		    if (g2fld->ipdtmpl != NULL) {
			    g2rec[nrecs]->sec4[i]->prod_params->scale_factor_second_fixed_sfc = g2fld->ipdtmpl[level_off + 4];
			    if (g2fld->ipdtmpl[level_off +4] == -127)
				    g2rec[nrecs]->sec4[i]->prod_params->scaled_val_second_fixed_sfc = 0;
			    else
				    g2rec[nrecs]->sec4[i]->prod_params->scaled_val_second_fixed_sfc
					    = g2fld->ipdtmpl[level_off + 5];
		    } else {
			    NhlPError(NhlFATAL, NhlEUNKNOWN,
				      "NclGRIB2: Invalid Product Definition Template.");
			    NhlFree(g2rec);
			    return NULL;
		    }
#if 0
		    switch (g2rec[nrecs]->sec4[i]->prod_params->typeof_first_fixed_sfc) {
		    case 1: /* ground or water surface */
		    case 9: /* sea bottom */
			    g2rec[nrecs]->sec4[i]->prod_params->level = -1;
			    break;

		    default:
			    g2rec[nrecs]->sec4[i]->prod_params->level = -1;
			    if (g2fld->ipdtmpl != NULL) {
				    if (g2rec[nrecs]->sec4[i]->prod_params->scale_factor_first_fixed_sfc < 100)
					    g2rec[nrecs]->sec4[i]->prod_params->level = g2fld->ipdtmpl[1];
				    else
					    g2rec[nrecs]->sec4[i]->prod_params->level = g2fld->ipdtmpl[1] / 100;
			    } else {
				    NhlPError(NhlFATAL, NhlEUNKNOWN,
					      "NclGRIB2: Invalid Product Definition Template.");
				    NhlFree(g2rec);
				    return NULL;
			    }

			    break;
		    }
#endif
	    }
            /*
             * Depending on type of product, there may or may not be more info
             * available that what's been extracted to this point.  This is determined
             * by the Product Definition Templates (PDTs).
             */
            g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc = -1;
	    g2rec[nrecs]->sec4[i]->prod_params->spatial_proc = -1;
            g2rec[nrecs]->sec4[i]->prod_params->ind_time_range_unit_stat_proc_done = 0;
            switch (g2rec[nrecs]->sec4[i]->pds_num) {
                case 0:
                    /*
                     * Analysis or forecast at a horizontal level or in a
                     * horizontal layer at a point in time.
                     */
                    break;

                case 2:
                    /*
                     * Derived forecasts based on all ensemble members at a
                     * horizontal level or in a horizontal layer at a point
                     * in time.
                     */
                    break;

                case 3:
                    /*
                     * Derived forecasts based on a cluster of ensemble members
                     * over a rectangular area at a horizontal level or in a
                     * horizontal layer at a point in time.
                     */
                    break;

                case 4:
                    /*
                     * Derived forecasts based on a cluster of ensemble members
                     * over a circular area at a horizontal level or i
                     *  a horizontal layer at a point in time.
                     */
                    break;


                case 6:
                    /*
                     * Percentile forecasts at a horizontal level or in a
                     * horizontal layer at a point in time.
                     */
                    break;
                    
                case 5:
                case 9:
                    /*
                     * Probability forecasts at a horizontal level or in a
                     * horizontal layer at a point in time.
                     */

                    if (g2fld->ipdtmpl != NULL) {
                        g2rec[nrecs]->sec4[i]->prod_params->forecast_probability_number = g2fld->ipdtmpl[15];
                        g2rec[nrecs]->sec4[i]->prod_params->total_forecast_probabilities = g2fld->ipdtmpl[16];
                        g2rec[nrecs]->sec4[i]->prod_params->probability_type = g2fld->ipdtmpl[17];
                        g2rec[nrecs]->sec4[i]->prod_params->scale_factor_lower_limit = g2fld->ipdtmpl[18];
                        g2rec[nrecs]->sec4[i]->prod_params->scaled_value_lower_limit = g2fld->ipdtmpl[19];
                        g2rec[nrecs]->sec4[i]->prod_params->scale_factor_upper_limit = g2fld->ipdtmpl[20];
                        g2rec[nrecs]->sec4[i]->prod_params->scaled_value_upper_limit = g2fld->ipdtmpl[21];
                    } else {
                        NhlPError(NhlFATAL, NhlEUNKNOWN,
                            "NclGRIB2: Invalid Product Definition Template.");
                            NhlFree(g2rec);
                            return NULL;
                    }
			
                    if (g2rec[nrecs]->sec4[i]->pds_num == 5) 
                        break;

                    /* case 9 only */
		    valid_end_time_set = 1;
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.year
                            = g2fld->ipdtmpl[22];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.mon
                            = g2fld->ipdtmpl[23];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.day
                            = g2fld->ipdtmpl[24];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.hour
                            = g2fld->ipdtmpl[25];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.min
                            = g2fld->ipdtmpl[26];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.sec
                            = g2fld->ipdtmpl[27];
                    g2rec[nrecs]->sec4[i]->prod_params->num_timerange_spec_time_interval_calc
                            = g2fld->ipdtmpl[28];
                    g2rec[nrecs]->sec4[i]->prod_params->total_num_missing_data_vals
                            = g2fld->ipdtmpl[29];
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc = g2fld->ipdtmpl[30];
                    g2rec[nrecs]->sec4[i]->prod_params->stat_proc = NULL;
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_incr_betw_fields
                            = g2fld->ipdtmpl[31];
                    g2rec[nrecs]->sec4[i]->prod_params->incr_betw_fields = NULL;
                    g2rec[nrecs]->sec4[i]->prod_params->ind_time_range_unit_stat_proc_done
                            = g2fld->ipdtmpl[32];
                    g2rec[nrecs]->sec4[i]->prod_params->itr_unit = NULL;
                    g2rec[nrecs]->sec4[i]->prod_params->len_time_range_unit_stat_proc_done
                            = g2fld->ipdtmpl[33];
                    g2rec[nrecs]->sec4[i]->prod_params->ind_time_unit_incr_succ_fields
                            = g2fld->ipdtmpl[34];
                    g2rec[nrecs]->sec4[i]->prod_params->itr_succ_unit = NULL;
                    g2rec[nrecs]->sec4[i]->prod_params->time_incr_betw_fields
                            = g2fld->ipdtmpl[35];

                    break;

                case 7:
                    /*
                     * Analysis or forecast error at a horizontal level or
                     * in a horizontal layer at a point in time.
                     */
                    break;

                case 8:
                    /*
                     * Average, accumulation, extreme values or other
                     * statistically processed values at a horizontal level
                     * or in a horizontal layer in a continuous or
                     * non-continuous time interval.
                     */
		    valid_end_time_set = 1;

                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.year
                            = g2fld->ipdtmpl[15];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.mon
                            = g2fld->ipdtmpl[16];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.day
                            = g2fld->ipdtmpl[17];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.hour
                            = g2fld->ipdtmpl[18];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.min
                            = g2fld->ipdtmpl[19];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.sec
                            = g2fld->ipdtmpl[20];
                    g2rec[nrecs]->sec4[i]->prod_params->num_timerange_spec_time_interval_calc
                            = g2fld->ipdtmpl[21];

                    g2rec[nrecs]->sec4[i]->prod_params->total_num_missing_data_vals
                            = g2fld->ipdtmpl[22];

                    /* table 4.10: Type of Statistical Processing */
		    if (g2rec[nrecs]->sec4[i]->prod_params->num_timerange_spec_time_interval_calc == 2 && 
			    g2fld->ipdtmpl[23] > 191) {
			    g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc = 
				    g2fld->ipdtmpl[23] != 255 ? g2fld->ipdtmpl[23] : g2fld->ipdtmpl[29];
		    }
		    else {
			    g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc = g2fld->ipdtmpl[23];
		    }
                    g2rec[nrecs]->sec4[i]->prod_params->stat_proc = NULL;
#if 0
                    table = "4.10.table";
                    cterr = Grib2ReadCodeTable(center, secid, table,
					       g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc,-1,ct);
                    if (cterr < NhlWARNING) {
                        NhlFree(g2rec);
                        return NULL;
                    }

                    g2rec[nrecs]->sec4[i]->prod_params->stat_proc
                            = NclMalloc(strlen(ct->descrip) + 1);
                    (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->stat_proc,
                            ct->descrip);
#endif
		    if (g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc > 191 && g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc != 255) {
			    /* local NCEP definition */
			    g2rec[nrecs]->sec4[i]->prod_params->n_grids = g2fld->ipdtmpl[26];
			    g2rec[nrecs]->sec4[i]->prod_params->p2 = g2fld->ipdtmpl[28];
			    g2rec[nrecs]->sec4[i]->prod_params->p1 = g2fld->ipdtmpl[28] - g2fld->ipdtmpl[32];
		    }
		    else {

			    /* table 4.11: Type of Time Intervals */
			    g2rec[nrecs]->sec4[i]->prod_params->typeof_incr_betw_fields
				    = g2fld->ipdtmpl[24];
			    g2rec[nrecs]->sec4[i]->prod_params->incr_betw_fields = NULL;
#if 0		    
			    table = "4.11.table";
			    cterr = Grib2ReadCodeTable(center, secid, table,
						       g2rec[nrecs]->sec4[i]->prod_params->typeof_incr_betw_fields,-1,ct);
			    if (cterr < NhlWARNING) {
				    NhlFree(g2rec);
				    return NULL;
			    }

			    g2rec[nrecs]->sec4[i]->prod_params->incr_betw_fields
				    = NclMalloc(strlen(ct->descrip) + 1);
			    (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->incr_betw_fields,
					  ct->descrip);
#endif
    
			    /* table 4.4: Indicator of Unit of Time Range */
			    g2rec[nrecs]->sec4[i]->prod_params->ind_time_range_unit_stat_proc_done
				    = g2fld->ipdtmpl[25];
			    g2rec[nrecs]->sec4[i]->prod_params->itr_unit = NULL;
#if 0
			    table = "4.4.table";
			    cterr = Grib2ReadCodeTable(center, secid, table,
						       g2rec[nrecs]->sec4[i]->prod_params->ind_time_range_unit_stat_proc_done,-1,ct);
			    if (cterr < NhlWARNING) {
				    NhlFree(g2rec);
				    return NULL;
			    }

			    g2rec[nrecs]->sec4[i]->prod_params->itr_unit
				    = NclMalloc(strlen(ct->descrip) + 1);
			    (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->itr_unit,
					  ct->descrip);
#endif
			    g2rec[nrecs]->sec4[i]->prod_params->len_time_range_unit_stat_proc_done
				    = g2fld->ipdtmpl[26];

			    g2rec[nrecs]->sec4[i]->prod_params->ind_time_unit_incr_succ_fields
				    = g2fld->ipdtmpl[27];
			    g2rec[nrecs]->sec4[i]->prod_params->itr_succ_unit = NULL;
#if 0
			    table = "4.4.table";
			    cterr = Grib2ReadCodeTable(center, secid, table,
						       g2rec[nrecs]->sec4[i]->prod_params->ind_time_unit_incr_succ_fields,-1,ct);
			    if (cterr < NhlWARNING) {
				    NhlFree(g2rec);
				    return NULL;
			    }

			    g2rec[nrecs]->sec4[i]->prod_params->itr_succ_unit
				    = NclMalloc(strlen(ct->descrip) + 1);
			    (void) strcpy(g2rec[nrecs]->sec4[i]->prod_params->itr_succ_unit,
					  ct->descrip);
#endif
			    g2rec[nrecs]->sec4[i]->prod_params->time_incr_betw_fields
				    = g2fld->ipdtmpl[28];
		    }

                    break;


                case 10:
                    /*
                     * Percentile forecasts at a horizontal level or in a
                     * horizontal layer in a continuous or non-continuous time 
                     * interval.
                     */
                    break;

                case 1:
                    /*
                     * Individual ensemble forecast, control and perturbed, at a
                     * horizontal level or in a horizontal layer at a point in time.
                     * FALLTHROUGH
                     */

                case 11:
                    /*
                     * Individual ensemble forecast, control and perturbed, at a
                     * horizontal level or in a horizontal layer, in a continuous
                     * or non-continuous time interval.
                     */

                    /* table 4.6: Type of Ensemble Forecast */
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_ensemble_fx = g2fld->ipdtmpl[15];
                    g2rec[nrecs]->sec4[i]->prod_params->ensemble_fx_type = NULL;
                    
                    g2rec[nrecs]->sec4[i]->prod_params->perturb_num = g2fld->ipdtmpl[16];
                    g2rec[nrecs]->sec4[i]->prod_params->num_fx_ensemble = g2fld->ipdtmpl[17];

                    if (g2rec[nrecs]->sec4[i]->pds_num == 1) 
                        break;

                    /* statistical processing */
		    valid_end_time_set = 1;
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.year
                            = g2fld->ipdtmpl[18];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.mon
                            = g2fld->ipdtmpl[19];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.day
                            = g2fld->ipdtmpl[20];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.hour
                            = g2fld->ipdtmpl[21];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.min
                            = g2fld->ipdtmpl[22];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.sec
                            = g2fld->ipdtmpl[23];
                    g2rec[nrecs]->sec4[i]->prod_params->num_timerange_spec_time_interval_calc
                            = g2fld->ipdtmpl[24];

                    g2rec[nrecs]->sec4[i]->prod_params->total_num_missing_data_vals
                            = g2fld->ipdtmpl[25];

                    /* table 4.10: Type of Statistical Processing */
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc
                            = g2fld->ipdtmpl[26];
                    g2rec[nrecs]->sec4[i]->prod_params->stat_proc = NULL;
    
                    /* table 4.11: Type of Time Intervals */
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_incr_betw_fields
                            = g2fld->ipdtmpl[27];
                    g2rec[nrecs]->sec4[i]->prod_params->incr_betw_fields = NULL;
    
                    /* table 4.4: Indicator of Unit of Time Range */
                    g2rec[nrecs]->sec4[i]->prod_params->ind_time_range_unit_stat_proc_done
                            = g2fld->ipdtmpl[28];
                    g2rec[nrecs]->sec4[i]->prod_params->itr_unit = NULL;
                    g2rec[nrecs]->sec4[i]->prod_params->len_time_range_unit_stat_proc_done
                            = g2fld->ipdtmpl[29];

                    g2rec[nrecs]->sec4[i]->prod_params->ind_time_unit_incr_succ_fields
                            = g2fld->ipdtmpl[30];
                    g2rec[nrecs]->sec4[i]->prod_params->itr_succ_unit = NULL;
                    g2rec[nrecs]->sec4[i]->prod_params->time_incr_betw_fields
                            = g2fld->ipdtmpl[31];
                    break;

                case 12:
                    /*
                     * Derived forecasts based on all ensemble members at a
                     * horizontal level or in a horizontal layer, in a continuous
                     * or non-continuous time interval.
                     */
		    valid_end_time_set = 1;
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.year
                            = g2fld->ipdtmpl[17];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.mon
                            = g2fld->ipdtmpl[18];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.day
                            = g2fld->ipdtmpl[19];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.hour
                            = g2fld->ipdtmpl[20];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.min
                            = g2fld->ipdtmpl[21];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.sec
                            = g2fld->ipdtmpl[22];
                    g2rec[nrecs]->sec4[i]->prod_params->num_timerange_spec_time_interval_calc
                            = g2fld->ipdtmpl[23];

                    g2rec[nrecs]->sec4[i]->prod_params->total_num_missing_data_vals
                            = g2fld->ipdtmpl[24];

                    /* table 4.10: Type of Statistical Processing */
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc
                            = g2fld->ipdtmpl[25];
                    g2rec[nrecs]->sec4[i]->prod_params->stat_proc = NULL;
    
                    /* table 4.11: Type of Time Intervals */
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_incr_betw_fields
                            = g2fld->ipdtmpl[26];
                    g2rec[nrecs]->sec4[i]->prod_params->incr_betw_fields = NULL;
    
                    /* table 4.4: Indicator of Unit of Time Range */
                    g2rec[nrecs]->sec4[i]->prod_params->ind_time_range_unit_stat_proc_done
                            = g2fld->ipdtmpl[27];
                    g2rec[nrecs]->sec4[i]->prod_params->itr_unit = NULL;
                    g2rec[nrecs]->sec4[i]->prod_params->len_time_range_unit_stat_proc_done
                            = g2fld->ipdtmpl[28];

                    g2rec[nrecs]->sec4[i]->prod_params->ind_time_unit_incr_succ_fields
                            = g2fld->ipdtmpl[29];
                    g2rec[nrecs]->sec4[i]->prod_params->itr_succ_unit = NULL;
                    g2rec[nrecs]->sec4[i]->prod_params->time_incr_betw_fields
                            = g2fld->ipdtmpl[30];
                    break;

                case 13:
                    /*
                     * Derived forecasts based on a cluster of ensemble members
                     * over a rectangular area at a horizontal level or in a
                     * horizontal layer, in a continuous or non-continuous time interval.
                     */
                    break;

                case 14:
                    /*
                     * Derived forecasts based on a cluster of ensemble members over
                     * a circular area at a horizontal level or in a horizontal
                     * layer, in a continuous or non-continuous time interval.
                     */
                    break;
		    
                case 15:
                    /*
                     * Average, accumulation, extreme values or other statistically-processed 
		     * values over a spatial area at a horizontal level 
		     * or in a horizontal layer at a point in time
                     */
                    /* table 4.10: Type of Statistical Processing */
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc = g2fld->ipdtmpl[15];
                    g2rec[nrecs]->sec4[i]->prod_params->stat_proc = NULL;
		    g2rec[nrecs]->sec4[i]->prod_params->spatial_proc = g2fld->ipdtmpl[16];
		    /* todo: provide info on type of spatial processing */
		    break;
		    
                case 20:
                    /*
                     * Radar product.
                     */
                    /* fall through  - same deal as satellite obs */

                case 30:
                    /*
                     * Satellite product.
                     */
                    /* we don't have proper product template data structures yet,
                     * but for sure the level types are not applicable to satellite data,
                     * also forecast time is not relevant to observational data
                     */
                    break;

                case 40:
                    /*
                     * Analysis or forecast at a horizontal level or in a horizontal
                     * layer at a point in time for atmospheric chemical or physical
                     * constituents
                     */
                    break;

                case 41:
                    /* FALLTHROUGH */
                case 43:
                    /*
                     * Individual ensemble forecast, control and perturbed, at a horizontal
                     * level or in a horizontal layer at a point in time for atmospheric
                     * chemical or physical constituents
                     */
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_ensemble_fx = g2fld->ipdtmpl[15];
                    g2rec[nrecs]->sec4[i]->prod_params->ensemble_fx_type = NULL;
                    
                    g2rec[nrecs]->sec4[i]->prod_params->perturb_num = g2fld->ipdtmpl[16];
                    g2rec[nrecs]->sec4[i]->prod_params->num_fx_ensemble = g2fld->ipdtmpl[17];

                    if (g2rec[nrecs]->sec4[i]->pds_num == 41) 
                        break;

                    /* statistical processing */
		    valid_end_time_set = 1;
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.year
                            = g2fld->ipdtmpl[18];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.mon
                            = g2fld->ipdtmpl[19];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.day
                            = g2fld->ipdtmpl[20];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.hour
                            = g2fld->ipdtmpl[21];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.min
                            = g2fld->ipdtmpl[22];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.sec
                            = g2fld->ipdtmpl[23];
                    g2rec[nrecs]->sec4[i]->prod_params->num_timerange_spec_time_interval_calc
                            = g2fld->ipdtmpl[24];

                    g2rec[nrecs]->sec4[i]->prod_params->total_num_missing_data_vals
                            = g2fld->ipdtmpl[25];

                    /* table 4.10: Type of Statistical Processing */
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc
                            = g2fld->ipdtmpl[26];
                    g2rec[nrecs]->sec4[i]->prod_params->stat_proc = NULL;
    
                    /* table 4.11: Type of Time Intervals */
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_incr_betw_fields
                            = g2fld->ipdtmpl[27];
                    g2rec[nrecs]->sec4[i]->prod_params->incr_betw_fields = NULL;
    
                    /* table 4.4: Indicator of Unit of Time Range */
                    g2rec[nrecs]->sec4[i]->prod_params->ind_time_range_unit_stat_proc_done
                            = g2fld->ipdtmpl[28];
                    g2rec[nrecs]->sec4[i]->prod_params->itr_unit = NULL;
                    g2rec[nrecs]->sec4[i]->prod_params->len_time_range_unit_stat_proc_done
                            = g2fld->ipdtmpl[29];

                    g2rec[nrecs]->sec4[i]->prod_params->ind_time_unit_incr_succ_fields
                            = g2fld->ipdtmpl[30];
                    g2rec[nrecs]->sec4[i]->prod_params->itr_succ_unit = NULL;
                    g2rec[nrecs]->sec4[i]->prod_params->time_incr_betw_fields
                            = g2fld->ipdtmpl[31];
                    break;

                case 42:
                    /*
                     * Average, accumulation, extreme values or other statistically
                     * processed values at a horizontal level or in a horizontal layer
                     * in a continuous or non-continuous time interval for atmospheric
                     * chemical or physical constituents
                     */
		    valid_end_time_set = 1;
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.year
                            = g2fld->ipdtmpl[15];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.mon
                            = g2fld->ipdtmpl[16];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.day
                            = g2fld->ipdtmpl[17];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.hour
                            = g2fld->ipdtmpl[18];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.min
                            = g2fld->ipdtmpl[19];
                    g2rec[nrecs]->sec4[i]->prod_params->end_overall_time_interval.sec
                            = g2fld->ipdtmpl[20];
                    g2rec[nrecs]->sec4[i]->prod_params->num_timerange_spec_time_interval_calc
                            = g2fld->ipdtmpl[21];

                    g2rec[nrecs]->sec4[i]->prod_params->total_num_missing_data_vals
                            = g2fld->ipdtmpl[22];

                    /* table 4.10: Type of Statistical Processing */
                    g2rec[nrecs]->sec4[i]->prod_params->stat_proc = NULL;
                    g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc
                            = g2fld->ipdtmpl[23];
		    if (g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc > 191 && g2rec[nrecs]->sec4[i]->prod_params->typeof_stat_proc != 255) {
			    /* local NCEP definition */
			    g2rec[nrecs]->sec4[i]->prod_params->n_grids = g2fld->ipdtmpl[26];
			    g2rec[nrecs]->sec4[i]->prod_params->p2 = g2fld->ipdtmpl[28];
			    g2rec[nrecs]->sec4[i]->prod_params->p1 = g2fld->ipdtmpl[28] - g2fld->ipdtmpl[32];
		    }
		    else {
    			    /* table 4.11: Type of Time Intervals */
			    g2rec[nrecs]->sec4[i]->prod_params->typeof_incr_betw_fields
				    = g2fld->ipdtmpl[24];
			    g2rec[nrecs]->sec4[i]->prod_params->incr_betw_fields = NULL;
			    
			    /* table 4.4: Indicator of Unit of Time Range */
			    g2rec[nrecs]->sec4[i]->prod_params->ind_time_range_unit_stat_proc_done
				    = g2fld->ipdtmpl[25];
			    g2rec[nrecs]->sec4[i]->prod_params->itr_unit = NULL;
			    g2rec[nrecs]->sec4[i]->prod_params->len_time_range_unit_stat_proc_done
				    = g2fld->ipdtmpl[26];

			    g2rec[nrecs]->sec4[i]->prod_params->ind_time_unit_incr_succ_fields
				    = g2fld->ipdtmpl[27];
			    g2rec[nrecs]->sec4[i]->prod_params->itr_succ_unit = NULL;
			    g2rec[nrecs]->sec4[i]->prod_params->time_incr_betw_fields
				    = g2fld->ipdtmpl[28];
		    }
                    break;

                case 48:
			/* aerosols -- leave the interval_type params set to missing (255)
			   until we can implement size and wavelength dimensions */

			g2rec[nrecs]->sec4[i]->prod_params->aerosol_type = g2fld->ipdtmpl[2];
			/*g2rec[nrecs]->sec4[i]->prod_params->size_interval_type = g2fld->ipdtmpl[3];*/
			g2rec[nrecs]->sec4[i]->prod_params->size_first_scale_factor = g2fld->ipdtmpl[4];
			g2rec[nrecs]->sec4[i]->prod_params->size_first_scale_value = g2fld->ipdtmpl[5];
			g2rec[nrecs]->sec4[i]->prod_params->size_second_scale_factor = g2fld->ipdtmpl[6];
			g2rec[nrecs]->sec4[i]->prod_params->size_second_scale_value = g2fld->ipdtmpl[7];
			/*g2rec[nrecs]->sec4[i]->prod_params->wavelength_interval_type = g2fld->ipdtmpl[8];*/
			g2rec[nrecs]->sec4[i]->prod_params->wavelength_first_scale_factor = g2fld->ipdtmpl[9];
			g2rec[nrecs]->sec4[i]->prod_params->wavelength_first_scale_value = g2fld->ipdtmpl[10];
			g2rec[nrecs]->sec4[i]->prod_params->wavelength_second_scale_factor = g2fld->ipdtmpl[11];
			g2rec[nrecs]->sec4[i]->prod_params->wavelength_second_scale_value = g2fld->ipdtmpl[12];

		    break;
                case 254:
                    /*
                     * CCITT IA5 character string.
                     */
                    break;

                case 1000:
                    /*
                     * Cross-section of analysis and forecast at a point in time.
                     */
                    break;

                case 1001:
                    /*
                     * Cross-section of averaged or otherwise statistically
                     * processed analysis or forecast over a range of time.
                     */
                    break;

                case 1002:
                    /*
                     * Cross-section of analysis and forecast, averaged or
                     * otherwise statistically-processed over latitude or longitude.
                     */
                    break;

                case 1100:
                    /*
                     * Hovmoller-type grid with no averaging or other statistical
                     * processing.
                     */
                    break;

                case 1101:
                    /*
                     * Hovmoller-type grid with averaging or other statistical processing.
                     */
                    break;

                case 65535:
                    /* Missing */
                    break;

                default:
                    /* Reserved */
                    if (g2rec[nrecs]->sec4[i]->pds_num >= 1102
                            || g2rec[nrecs]->sec4[i]->pds_num <= 32767)
                        /* Reserved by WMO */
                        ;;

                        /* FALLTHROUGH */

                    if (g2rec[nrecs]->sec4[i]->pds_num >= 32768
                            || g2rec[nrecs]->sec4[i]->pds_num <= 65534)
                        /* Reserved for Local Use */
                        ;;
                        break;
            }


            g2rec[nrecs]->sec4[i]->num_coord = g2fld->num_coord;
            if (g2rec[nrecs]->sec4[i]->num_coord > 0) {
                g2rec[nrecs]->sec4[i]->coord_list = NclMalloc(g2fld->num_coord);
                memcpy(g2rec[nrecs]->sec4[i]->coord_list, g2fld->coord_list, g2fld->num_coord);
            }
        
            /* GRIB2 section 5   DRS */
            secid = 5;
            g2rec[nrecs]->sec5[i]->secid = 5;

            /* table 5.0: Data Representation Template Number */
            g2rec[nrecs]->sec5[i]->drt_templ_num = g2fld->idrtnum;
            g2rec[nrecs]->sec5[i]->drt_desc = NULL;
#if 0
            table = "5.0.table";
            cterr = Grib2ReadCodeTable(center, secid, table, g2rec[nrecs]->sec5[i]->drt_templ_num,-1,ct);
            if (cterr < NhlWARNING) {
                NhlFree(g2rec);
                return NULL;
            }

            g2rec[nrecs]->sec5[i]->drt_desc = NclMalloc(strlen(ct->descrip) + 1);
            (void) strcpy(g2rec[nrecs]->sec5[i]->drt_desc, ct->descrip);
#endif

            /*
             * the data repr applies only to data rep template 0-3 -- totally wrong for
             * spherical harmonic data
             */
            
            /*
             * the ref value must be converted to floating point using grib2lib 'rdieee'
             * function (or equivalent) -- but we don't need it because the library does
             * all the unpacking -- dib --
             */
            g2rec[nrecs]->sec5[i]->data_repr = NclMalloc(sizeof(G2dataRepr));

            if (g2fld->idrtmpl != NULL) {
                g2rec[nrecs]->sec5[i]->data_repr->refVal = (double) g2fld->idrtmpl[0];
                g2rec[nrecs]->sec5[i]->data_repr->bin_scale_factor = g2fld->idrtmpl[1];
                g2rec[nrecs]->sec5[i]->data_repr->dec_scale_factor = g2fld->idrtmpl[2];
                g2rec[nrecs]->sec5[i]->data_repr->nbits_packed_val = g2fld->idrtmpl[3];
                g2rec[nrecs]->sec5[i]->data_repr->own_missing = 0;
                g2rec[nrecs]->sec5[i]->data_repr->missing.doubleval = 0;
            } else {
                NhlPError(NhlFATAL, NhlEUNKNOWN,
                    "NclGRIB2: Invalid Data Representation Template.");
                    NhlFree(g2rec);
                    return NULL;
            }

            if (g2fld->idrtnum == 2 || g2fld->idrtnum == 3) {
                /* These data representation types may use their own missing value */
                if (g2fld->idrtmpl[6] > 0 && g2fld->idrtmpl[6] < 3) {
                    g2rec[nrecs]->sec5[i]->data_repr->own_missing = 1;
                    if (g2fld->idrtmpl[4] == 0) {
                        float tfloat;
                        rdieee(g2fld->idrtmpl + 7, &tfloat, 1);
                        g2rec[nrecs]->sec5[i]->data_repr->missing.floatval = tfloat;
                    }
                    else {
                        g2rec[nrecs]->sec5[i]->data_repr->missing.floatval = (float)g2fld->idrtmpl[7];
                    }
                }
            }

            /* table 5.1: Type of Original Field Values */
            g2rec[nrecs]->sec5[i]->data_repr->typeof_field_vals = g2fld->idrtmpl[4];
            g2rec[nrecs]->sec5[i]->data_repr->typeof_field_vals = 0; /* always float */
            g2rec[nrecs]->sec5[i]->data_repr->field_vals = NULL;
#if 0
            table = "5.1.table";
            cterr = Grib2ReadCodeTable(center, secid, table,
				       g2rec[nrecs]->sec5[i]->data_repr->typeof_field_vals,-1,ct);
            if (cterr < NhlWARNING) {
                NhlFree(g2rec);
                return NULL;
            }

            g2rec[nrecs]->sec5[i]->data_repr->field_vals = NclMalloc(strlen(ct->descrip) + 1);
            (void) strcpy(g2rec[nrecs]->sec5[i]->data_repr->field_vals, ct->descrip);
#endif

            /* type allocated is based on value read from table/template */
            g2rec[nrecs]->sec5[i]->ndpts = g2fld->ndpts;

            /* GRIB2 section 6  Bitmap section */
            secid = 6;
            g2rec[nrecs]->sec6[i]->secid = 6;
            g2rec[nrecs]->sec6[i]->unpacked = g2fld->unpacked;
            g2rec[nrecs]->sec6[i]->expanded = g2fld->expanded;

            /* table 6.0: Bitmap Indicator */
            g2rec[nrecs]->sec6[i]->bmap_ind = g2fld->ibmap;
            g2rec[nrecs]->sec6[i]->bmap_desc = NULL;
#if 0
            table = "6.0.table";
            cterr = Grib2ReadCodeTable(center, secid, table, g2rec[nrecs]->sec6[i]->bmap_ind,-1,ct);
            if (cterr < NhlWARNING) {
                NhlFree(g2rec);
                return NULL;
            }

            g2rec[nrecs]->sec6[i]->bmap_desc = NclMalloc(strlen(ct->descrip) + 1);
            (void) strcpy(g2rec[nrecs]->sec6[i]->bmap_desc, ct->descrip);
#endif

            if (g2fld->ibmap == 255) {
                /* no bitmap specified */
                g2rec[nrecs]->sec6[i]->bmap = NULL;
            } else {
                g2rec[nrecs]->sec6[i]->bmap = NULL;
/***		
                g2rec[nrecs]->sec6[i]->bmap = NclMalloc(g2fld->ngrdpts);
                memcpy(g2rec[nrecs]->sec6[i]->bmap, g2fld->bmap, g2fld->ngrdpts);
***/
            }

            /* GRIB2 section 7 */
            secid = 7;
            g2rec[nrecs]->sec7[i]->secid = 7;
/***
            g2rec[nrecs]->sec7[i]->data = (float *) NclMalloc(g2fld->ndpts);
            memcpy(g2rec[nrecs]->sec7[i]->data, g2fld->fld, g2fld->ndpts);
***/

            g2_free(g2fld);
#if 0
	    if (valid_end_time_set) {
		    int itime[6],etime[6], difftime[6], convert[6];
		    int mon[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
		    int ix;

		    convert[0] = 1;
		    convert[1] = 12;
		    convert[2] = 31;
		    convert[3] = 24;
		    convert[4] = 60;
		    convert[5] = 60;
		    itime[0] = g2rec[i]->sec1.date_time.year;
		    itime[1] = g2rec[i]->sec1.date_time.mon;
		    itime[2] = g2rec[i]->sec1.date_time.day;
		    itime[3] = g2rec[i]->sec1.date_time.hour;
		    itime[4] = g2rec[i]->sec1.date_time.min;
		    itime[5] = g2rec[i]->sec1.date_time.sec;

		    etime[0] = g2rec[i]->sec4[0]->prod_params->end_overall_time_interval.year;
		    etime[1] = g2rec[i]->sec4[0]->prod_params->end_overall_time_interval.mon;
		    etime[2] = g2rec[i]->sec4[0]->prod_params->end_overall_time_interval.day;
		    etime[3] = g2rec[i]->sec4[0]->prod_params->end_overall_time_interval.hour;
		    etime[4] = g2rec[i]->sec4[0]->prod_params->end_overall_time_interval.min;
		    etime[5] = g2rec[i]->sec4[0]->prod_params->end_overall_time_interval.sec;

		    if (memcmp(etime,itime,sizeof(itime)) <= 0) {
			    printf("end time less than or equal to initial time\n");
		    }
		    else {
			    for (ix = 5; ix >= 0; ix--) {
				    if (ix == 0) {
					    difftime[ix] = etime[ix] - itime[ix]; 
				    }
				    else if (ix == 2) { /* days */
					    while (etime[ix] < itime[ix]) {
						    etime[ix] += mon[etime[ix-1]];
						    if (HeisLeapYear(etime[0]) && etime[ix-1] == 1) {
							    etime[ix]++;
						    }
						    etime[ix -1]--;
					    }
					    difftime[ix] = etime[ix] - itime[ix]; 
				    }
				    else {
					    while (etime[ix] < itime[ix]) {
						    etime[ix] += convert[ix];
						    etime[ix -1]--;
					    }
					    difftime[ix] = etime[ix] - itime[ix]; 
				    }
			    }
			    printf("valid-time - initial-time: %dy %dmo %dd %dh %dmi %ds\n", difftime[0],difftime[1],difftime[2],difftime[3],difftime[4],difftime[5]);
			    
		    }
		    printf("forecast time: %d, duration %d\n",g2rec[i]->sec4[0]->prod_params->forecast_time,
			   g2rec[i]->sec4[0]->prod_params->len_time_range_unit_stat_proc_done);
	    }		    
#endif
        }
	++nrecs;
        NclFree(g2buf);
    }
    Grib2FreeCodeTableRec(ct);
	
    for (i = 0; i < nrecs; i++)
        g2rec[i]->numrecs = nrecs;

#if 0
    /* debug */
   Grib2PrintRecords(g2rec);
   /* debug */
#endif

    g2frec = (Grib2FileRecord *) rec;
    g2frec->n_vars = 0;
    g2frec->var_list = NULL;
    g2frec->wr_status = wr_status;
    g2frec->file_path_q = path;
    g2frec->internal_var_list = NULL;
    g2frec->n_internal_vars = 0;


    for (i = 0; i < nrecs; i++) {
        for (j = 0; j < g2rec[i]->num_rptd; j++) {
	    int comp_val;

            g2inqrec = NclMalloc(sizeof(Grib2RecordInqRec));
	    memset(g2inqrec, 0, sizeof(Grib2RecordInqRec));
            g2inqrec->rec_num = i + 1;
            g2inqrec->offset = g2rec[i]->offset;
	    g2inqrec->rec_size = g2rec[i]->rec_size;
	    if (g2rec[i]->num_rptd == 1) {
		    g2inqrec->field_num = 0; /* field_num == 0 indicates only 1 field in record */
	    } 
	    else {
		    g2inqrec->field_num = j + 1; /* counting from 1 */
	    }
            g2inqrec->the_dat = NULL;
            g2inqrec->version = g2rec[i]->version;
	    /* transfer the table source name to the g2inqrec */
	    g2inqrec->table_source = NclMalloc(strlen(g2rec[i]->table_source_name)+1);
	    strcpy(g2inqrec->table_source,g2rec[i]->table_source_name);

            /* PDS */

            /* GDS */
#if 0
            g2inqrec->gds = NclMalloc(sizeof(G2_GDS));
            memset(g2inqrec->gds,0,sizeof(G2_GDS));
            if (g2rec[i]->sec3[j]->grid_list_num_oct_num > 0) {
                g2inqrec->gds->grid_list_num_oct_num = g2rec[i]->sec3[j]->grid_list_num_oct_num;
                g2inqrec->gds->grid_list_num_oct_opt = NclMalloc(
                        g2rec[i]->sec3[j]->grid_list_num_oct_num * sizeof(int));
                memcpy(g2inqrec->gds->grid_list_num_oct_opt,
                        g2rec[i]->sec3[j]->grid_list_num_oct_opt,
                        g2rec[i]->sec3[j]->grid_list_num_oct_num * sizeof(int));
            } else {
                g2inqrec->gds->grid_list_num_oct_num = g2rec[i]->sec3[j]->grid_list_num_oct_num;
                g2inqrec->gds->grid_list_num_oct_opt = NULL;
            }

            g2inqrec->gds->shape_of_earth = NclMalloc(sizeof(G2shapeOfEarth));
            memset(g2inqrec->gds->shape_of_earth,0,sizeof(G2shapeOfEarth));
            g2inqrec->gds->res_comp = NclMalloc(sizeof(G2resComponentFlags));
            memset(g2inqrec->gds->res_comp,0,sizeof(G2resComponentFlags));
            g2inqrec->gds->scan_mode = NclMalloc(sizeof(G2scanModeFlags));
            memset(g2inqrec->gds->scan_mode,0,sizeof(G2scanModeFlags));
            memcpy(g2inqrec->gds, g2rec[i]->sec3[j], sizeof(G2_GDS));
#endif
            g2inqrec->gds = (G2_GDS *)g2rec[i]->sec3[j];

            /* Bitmap */

            /* Binary Data */

            /* Table 3.0: Source of Grid Defn */

            g2inqrec->grid_number = g2rec[i]->sec3[j]->grid_num;


/*
            if (g2rec[i]->sec1.subcenter_name != NULL) {
                g2inqrec->sub_center = NclMalloc(strlen(g2rec[i]->sec1.subcenter_name) + 1);
                (void) strcpy(g2inqrec->sub_center, g2rec[i]->sec1.subcenter_name);
            } else {
                g2inqrec->sub_center = NULL;
            }
*/
	    /* set up the Grib2VarTraits structure for comparing parameters */
	    g2inqrec->traits.center =  g2rec[i]->sec1.centerID;
	    g2inqrec->traits.subcenter =  g2rec[i]->sec1.subcenterID;
	    g2inqrec->traits.prod_status = g2rec[i]->sec1.prod_status;
	    g2inqrec->traits.gen_process_type = g2rec[i]->sec4[j]->prod_params->gen_process; /* no way to distinguish these in the var name now */
	    g2inqrec->traits.sig_ref_time = g2rec[i]->sec1.ref_time;
	    g2inqrec->traits.pds_template = g2rec[i]->sec4[j]->pds_num; 
	    g2inqrec->traits.discipline = g2rec[i]->sec0.discipline;
	    g2inqrec->traits.param_cat = g2rec[i]->sec4[j]->prod_params->param_cat;
	    g2inqrec->traits.param_number = g2rec[i]->sec4[j]->prod_params->param_num;
	    g2inqrec->traits.stat_proc_type = g2rec[i]->sec4[j]->prod_params->typeof_stat_proc;
	    g2inqrec->traits.first_level_type = g2rec[i]->sec4[j]->prod_params->typeof_first_fixed_sfc;
	    g2inqrec->traits.second_level_type =  g2rec[i]->sec4[j]->prod_params->typeof_second_fixed_sfc;
	    g2inqrec->traits.aerosol_type =  g2rec[i]->sec4[j]->prod_params->aerosol_type;
	    g2inqrec->traits.aerosol_size_interval_type = g2rec[i]->sec4[j]->prod_params->size_interval_type;
            g2inqrec->traits.aerosol_wavelength_interval_type = g2rec[i]->sec4[j]->prod_params->wavelength_interval_type;

	    g2inqrec->qcenter_name = NrmStringToQuark(g2rec[i]->sec1.center_name);

            if (((NrmQuark) g2frec->options[GRIB_THINNED_GRID_INTERPOLATION_OPT].values) ==
		NrmStringToQuark("cubic"))
                g2inqrec->interp_method = 1;
            else
                g2inqrec->interp_method =  0;

	    g2inqrec->has_bmap = g2rec[i]->sec6[j]->bmap_ind != 255 ? 1 : 0;
            g2inqrec->bds_flags = g2rec[i]->sec5[j]->drt_templ_num;
            g2inqrec->int_or_float = g2rec[i]->sec5[j]->data_repr->typeof_field_vals;
	    g2inqrec->has_own_missing = g2rec[i]->sec5[j]->data_repr->own_missing;
	    g2inqrec->missing = g2rec[i]->sec5[j]->data_repr->missing;

            /*
             * Variable info.  Fields will be populated per code above that reads
             * section 4 (PDS) and provides values read, or default values.
             */
#if 0
            g2name_rec = NclMalloc(sizeof(G2_TBLE2));
            g2name_rec->num = g2rec[i]->sec4[j]->prod_params->param_num;

            if (g2rec[i]->sec4[j]->prod_params->param_name != NULL) {
                g2name_rec->long_name
                    = NclMalloc(strlen(g2rec[i]->sec4[j]->prod_params->param_name) + 1);
                (void) strcpy(g2name_rec->long_name, g2rec[i]->sec4[j]->prod_params->param_name);
            } else {
                g2name_rec->long_name = NULL;
            }

            if (g2rec[i]->sec4[j]->prod_params->short_name != NULL) {
                g2name_rec->abrev
                    = NclMalloc(strlen(g2rec[i]->sec4[j]->prod_params->short_name) + 1);
                (void) strcpy(g2name_rec->abrev, g2rec[i]->sec4[j]->prod_params->short_name);
            } else {
                g2name_rec->abrev = NULL;
            }

            if (g2rec[i]->sec4[j]->prod_params->units != NULL) {
                g2name_rec->units = NclMalloc(strlen(g2rec[i]->sec4[j]->prod_params->units) + 1);
                (void) strcpy(g2name_rec->units, g2rec[i]->sec4[j]->prod_params->units);
            } else {
                g2name_rec->units = NULL;
            }

            g2inqrec->ptable_rec = g2name_rec;
#endif

            /* Time */
            g2inqrec->initial_time.year = g2rec[i]->sec1.date_time.year;
            g2inqrec->initial_time.days_from_jan1 = HeisDayDiff(1, 1,
                g2rec[i]->sec1.date_time.year, g2rec[i]->sec1.date_time.day,
                g2rec[i]->sec1.date_time.mon, g2rec[i]->sec1.date_time.year);
            g2inqrec->initial_time.minute_of_day =  g2rec[i]->sec1.date_time.hour * 60 + g2rec[i]->sec1.date_time.min;
            g2inqrec->initial_time.seconds =  g2rec[i]->sec1.date_time.sec;

	    g2inqrec->end_overall_interval.year = g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.year;
            g2inqrec->end_overall_interval.days_from_jan1 = 
		    HeisDayDiff(1, 1,
				g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.year, g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.day,
				g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.mon, g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.year);
            g2inqrec->end_overall_interval.minute_of_day =  
		    g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.hour * 60 + g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.min;
            g2inqrec->end_overall_interval.seconds =  g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.sec;
	    /* if end time is defined find the difference from the initial time (only if greater than initial time: it is initialized to 0)  */

	    g2inqrec->overall_interval_seconds = 0;
	    if (g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.year >= g2rec[i]->sec1.date_time.year) {
		    int day_diff = HeisDayDiff(g2rec[i]->sec1.date_time.day,g2rec[i]->sec1.date_time.mon,g2rec[i]->sec1.date_time.year,
					       g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.day,
					       g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.mon,
					       g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.year);
		    g2inqrec->overall_interval_seconds = 86400 * day_diff + 
			    3600 * (g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.hour - g2rec[i]->sec1.date_time.hour) +
			    60 * (g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.min - g2rec[i]->sec1.date_time.min) +
			    (g2rec[i]->sec4[j]->prod_params->end_overall_time_interval.sec - g2rec[i]->sec1.date_time.sec);
	    }

            g2inqrec->forecast_time = g2rec[i]->sec4[j]->prod_params->forecast_time;
	    g2inqrec->forecast_time_units = g2rec[i]->sec4[j]->prod_params->time_range_unit_id; 
	    if (g2rec[i]->sec4[j]->prod_params->typeof_stat_proc > 191 && g2rec[i]->sec4[j]->prod_params->typeof_stat_proc != 255) {
		    g2inqrec->p1 = g2rec[i]->sec4[j]->prod_params->p1;
		    g2inqrec->p2 = g2rec[i]->sec4[j]->prod_params->p2;
		    g2inqrec->n_grids = g2rec[i]->sec4[j]->prod_params->n_grids;
		    /* units of time period ??? -- for now use the forecast time units */
		    g2inqrec->time_period_units =  g2inqrec->forecast_time_units;
	    }
	    else {
		    g2inqrec->time_period = g2rec[i]->sec4[j]->prod_params->len_time_range_unit_stat_proc_done;
		    g2inqrec->time_period_units =  g2rec[i]->sec4[j]->prod_params->ind_time_range_unit_stat_proc_done;
	    }




            /* Levels */
            g2inqrec->level_indicator = g2rec[i]->sec4[j]->prod_params->typeof_first_fixed_sfc;
            _g2GetLevels(&g2inqrec->level0, &g2inqrec->level1,
			 (int) g2rec[i]->sec4[j]->prod_params->typeof_first_fixed_sfc,
			 (int) g2rec[i]->sec4[j]->prod_params->typeof_second_fixed_sfc,
			 (int) g2rec[i]->sec4[j]->prod_params->scaled_val_first_fixed_sfc,
			 (int) g2rec[i]->sec4[j]->prod_params->scale_factor_first_fixed_sfc,
			 (int) g2rec[i]->sec4[j]->prod_params->scaled_val_second_fixed_sfc,			 
			 (int) g2rec[i]->sec4[j]->prod_params->scale_factor_second_fixed_sfc
		    );

            g2inqrec->var_name_q = NrmNULLQUARK;
	    g2inqrec->spatial_proc = g2rec[i]->sec4[j]->prod_params->spatial_proc;

            /* Ensembles */
            g2inqrec->is_ensemble = 0;
            memset(&g2inqrec->ens, 0, sizeof(G2_ENS));
	    g2inqrec->ens.prob_type = -1;
            /* Ensemble or not?  Determined by PDS number */
	    
            switch (g2rec[i]->sec4[j]->pds_num) {
                case 0:
                    /*
                     * Analysis or forecast at a horizontal level or in a 
                     * horizontal layer at a point in time.
                     */
                    g2inqrec->is_ensemble = 0;
                    break;


                case 2:
                    /*
                     * Derived forecasts based on all ensemble members at a
                     * horizontal level or in a horizontal layer at a point
                     * in time.
                     */
			g2inqrec->is_ensemble = 0;
                    break;

                case 3:
                    /*
                     * Derived forecasts based on a cluster of ensemble members
                     * over a rectangular area at a horizontal level or in a
                     * horizontal layer at a point in time.
                     */
                    g2inqrec->is_ensemble = 0;
                    break;

                case 4:
                    /*
                     * Derived forecasts based on a cluster of ensemble members
                     * over a circular area at a horizontal level or i
                     *  a horizontal layer at a point in time.
                     */
                    g2inqrec->is_ensemble = 0;
                    break;
	        case 5:
	        case 9:
		    /* probability forecasts share the ensemble dimension -- so far there are no templates
		       with probabability and individual ensemble properties -- distinguish probability from
		       ensemble using prob_type > -1 */
                    g2inqrec->is_ensemble = 1;
		    g2inqrec->ens.prob_type = g2rec[i]->sec4[j]->prod_params->probability_type;
		    g2inqrec->ens.lower_limit_scale = g2rec[i]->sec4[j]->prod_params->scale_factor_lower_limit;
		    g2inqrec->ens.lower_limit_value = g2rec[i]->sec4[j]->prod_params->scaled_value_lower_limit;
		    g2inqrec->ens.upper_limit_scale = g2rec[i]->sec4[j]->prod_params->scale_factor_upper_limit;
		    g2inqrec->ens.upper_limit_value = g2rec[i]->sec4[j]->prod_params->scaled_value_upper_limit;
		    break;
				
                case 1:
                    /*
                     * Individual ensemble forecast, control and perturbed, at a
                     * horizontal level or in a horizontal layer at a point in time.
                     */
                case 11:
                    /*
                     * Individual ensemble forecast, control and perturbed, at a
                     * horizontal level or in a horizontal layer, in a continuous
                     * or non-continuous time interval.
                     */
	        case 41:
	        case 43:
	        case 45:
	        case 47:
                    g2inqrec->is_ensemble = 1;
                    g2inqrec->ens.type = (g2rec[i]->sec4[j]->prod_params->typeof_ensemble_fx >= 0 &&
			    g2rec[i]->sec4[j]->prod_params->typeof_ensemble_fx < 4) ?
			    g2rec[i]->sec4[j]->prod_params->typeof_ensemble_fx : 255;
		    g2inqrec->ens.id = g2rec[i]->sec4[j]->prod_params->perturb_num;
                    break;

                case 12:
                    /*
                     * Derived forecasts based on all ensemble members at a
                     * horizontal level or in a horizontal layer, in a continuous
                     * or non-continuous time interval.
                    g2inqrec->is_ensemble = 0;
                    break;
                     */

                case 13:
                    /*
                     * Derived forecasts based on a cluster of ensemble members
                     * over a rectangular area at a horizontal level or in a
                     * horizontal layer, in a continuous or non-continuous time interval.
                     */
                    g2inqrec->is_ensemble = 0;
                    break;

                case 14:
                    /*
                     * Derived forecasts based on a cluster of ensemble members over
                     * a circular area at a horizontal level or in a horizontal
                     * layer, in a continuous or non-continuous time interval.
                     */
                    g2inqrec->is_ensemble = 0;
                    break;

                default:
                    g2inqrec->is_ensemble = 0;
                    break;
            }

            if (g2frec->var_list == NULL) {
                g2frec->var_list = _g2NewListNode(g2inqrec);
                g2frec->n_vars = 1;
            } else if ((comp_val =_g2CompareRecord(g2frec->var_list,g2inqrec)) > 0) {
		    Grib2ParamList *vlist = g2frec->var_list;
		    g2frec->var_list = _g2NewListNode(g2inqrec);
		    g2frec->var_list->next = vlist;
		    g2frec->n_vars++;
	    }
	    else if (comp_val == 0) {
		    _g2AddRecordToNode(g2frec->var_list, g2inqrec);
	    }
	    else {
		    g2plist = g2frec->var_list;
		    while (g2plist->next && (comp_val = _g2CompareRecord(g2plist->next,g2inqrec)) < 0) {
			    g2plist = g2plist->next;
		    }
		    if (g2plist->next) {
			    if (comp_val > 0) { /* insert before g2plist->next */
				    g2plist_n = _g2NewListNode(g2inqrec);
				    _g2InsertNodeAfter(g2plist, g2plist_n);
				    g2frec->n_vars++;
			    }
			    else { /* must be 0 */
				    _g2AddRecordToNode(g2plist->next, g2inqrec);
			    }
		    }
		    else { 
			    g2plist_n = _g2NewListNode(g2inqrec);
			    _g2InsertNodeAfter(g2plist, g2plist_n);
			    g2frec->n_vars++;
		    }
	    }
	}
    }

    if (g2frec != NULL) {
        g2plist = g2frec->var_list;
        while (g2plist != NULL) {
		_g2SetVarInfo(g2frec,g2plist);
		g2plist_n = _g2AdjustTimeAndStatProcVars(g2frec,g2plist);
		if (! g2plist_n) {
			g2plist = g2plist->next;
			continue;
		}
		g2plist_tmp = g2plist->next;
		g2plist->next = g2plist_n;
		while (g2plist_n->next != NULL)
			g2plist_n = g2plist_n->next;
		g2plist_n->next = g2plist_tmp;
		g2plist = g2plist_tmp;
	}

        g2frec->grib_grid_cache = NULL;

        /* sort by time, then level, for each variable in the list */
        g2plist = g2frec->var_list;
        k = 0;

        while (g2plist != NULL) {
            g2inqrec_list = g2plist->thelist;
            g2sort = (Grib2RecordInqRecList **) NclMalloc(
                    (unsigned int) sizeof(Grib2RecordInqRecList *) * g2plist->n_entries);
            i = 0;

	    while (g2inqrec_list != NULL) {
		    g2sort[i] = g2inqrec_list;
		    g2inqrec_list->rec_inq->var_name_q = g2plist->var_info.var_name_quark;

		    /* if any records have a bitmap the variable is treated as having a bitmap */
		    if (g2inqrec_list->rec_inq->has_bmap) 
			    g2plist->has_bmap = 1;
		    g2inqrec_list = g2inqrec_list->next;
		    i++;
	    }

            qsort((void *) g2sort, i, sizeof(Grib2RecordInqRecList *), g2record_comp);

            g2plist->thelist = g2sort[0];
            for (i = 0; i < g2plist->n_entries - 1; i++) {
                g2sort[i]->next = g2sort[i + 1];
            }
            g2sort[g2plist->n_entries - 1]->next = NULL;

            /*
             * Determine dimensionality for each variable.  This is determined by:
             *     [yy:mm:dd:hh:mm] x [forecast offset] x [levels] x [grid x] x [grid y]
             * 
             * Below: k == variable number
             *        g2plist == Grib2 ParamList
             *        g2sort == all elements in order, connected.
             *
             * Missing entries inserted when it's determined that levels or forecast
             * times are missing.
             */

            /*
             * Determine grid/coord info as well as dimensionality for each record.
             * Fill in missing values.
             */
            if (g2plist->grid_number == 50) {
                /* grid num = 50 == spherical harmonic coefficients */
                g2plist->var_info.doff = 2;
                err = _g2DetermineDimensionAndGridInfo(g2frec, g2plist);
            } else {
                g2plist->var_info.doff = 1;
                err = _g2DetermineDimensionAndGridInfo(g2frec, g2plist);
            }

            if ((err < NhlNOERROR) && (g2plist_n == NULL)) {
                g2plist = g2plist->next;
                g2plist_n = g2frec->var_list;
                g2frec->var_list = g2plist;
                g2frec->n_vars--;
                _Grib2FreeParamRec(g2plist_n);
                g2plist_n = NULL;
            } else {
                if (err < NhlNOERROR) {
                    g2plist_tmp = g2plist;
                    g2plist_n->next = g2plist->next;
                    g2plist = g2plist->next;
                    g2frec->n_vars--;
                    _Grib2FreeParamRec(g2plist_tmp);
                } else {
                    g2plist_n = g2plist;
                    g2plist = g2plist->next;
                    ++k;
                }
            }

            NclFree(g2sort);
            g2sort = NULL;
        }

        /*
         * Scan variables and determine all dimensions; combine dimensions that are
         * equal.  The last two dimensions will always be the grid dimensions.
         *
         * Variables may contain between two and five dimensions (inclusive).  The
         * first three dimensions are:  [initial_time] x [forecast offset] x [levels]
         * Any (each) dimension could be == 1, in which case it's not a true dimension
         * but rather an attribute.
         */
        _g2SetFileDimsAndCoordVars(g2frec);
        _g2SetAttributeLists(g2frec);
        _g2MakeVarnamesUnique(g2frec); 
	if ((int)(g2frec->options[GRIB_PRINT_RECORD_INFO_OPT].values) != 0) {
		_g2PrintRecordInfo(g2frec);
	}

        fclose(fd);
        NclFree(vbuf);
	if (center_name != NULL)
		NclFree(center_name);
	if (subcenter_name != NULL)
		NclFree(subcenter_name);
        Grib2FreeGrib2Rec(g2rec);
        return g2frec;
    }
    return NULL;
}


static void Grib2FreeFileRec
# if    NhlNeedProto
(void* therec)
# else
(therec)
    void*   therec;
# endif
{
	Grib2FileRecord *thefile = (Grib2FileRecord *) therec;
	Grib2ParamList *vstep,*vstep1;
	Grib2DimInqRecList *dim,*dim1;
	Grib2InternalVarList *ivars,*itmp;
	Grib2AttInqRecList *theatts,*tmp;
	NclGrib2CacheList *thelist,*thelist0;
	NclGrib2CacheRec *ctmp,*ctmp0;

	vstep = thefile->var_list;
	while(vstep != NULL){
		vstep1 = vstep->next;
		_Grib2FreeParamRec(vstep);
		vstep  = vstep1;
	}
	thelist = thefile->grib_grid_cache;
        while(thelist != NULL) {
		if (thelist->int_missing_rec) {
			_NclDestroyObj((NclObj)thelist->int_missing_rec);
		}
		if (thelist->float_missing_rec) {
			_NclDestroyObj((NclObj)thelist->float_missing_rec);
		}
		ctmp = thelist->thelist;
		while(ctmp!=NULL) {
			ctmp0 = ctmp;
			ctmp = ctmp->next;
			NclFree(ctmp0);
		}
		thelist0 = thelist;
		thelist = thelist->next;
		NclFree(thelist0);
	}

	ivars = thefile->internal_var_list;
	while(ivars != NULL) {
		_NclDestroyObj((NclObj)ivars->int_var->value);
		theatts = ivars->int_var->theatts;
		while(theatts != NULL) {
			_NclDestroyObj((NclObj)theatts->att_inq->thevalue);
			NclFree(theatts->att_inq);
			tmp = theatts;
			theatts = theatts->next;
			NclFree(tmp);
		}	
		NclFree(ivars->int_var);
		itmp = ivars;	
		ivars = ivars->next;
		NclFree(itmp);
	}
	dim = thefile->probability_dims;
	if(dim != NULL) {
		while(dim != NULL) {
			dim1 = dim->next;
			if(dim->dim_inq != NULL) {
				NclFree(dim->dim_inq);
			}
			NclFree(dim);
			dim = dim1;
		}
	}
	dim = thefile->ensemble_dims;
	if(dim != NULL) {
		while(dim != NULL) {
			dim1 = dim->next;
			if(dim->dim_inq != NULL) {
				NclFree(dim->dim_inq);
			}
			NclFree(dim);
			dim = dim1;
		}
	}
	dim = thefile->it_dims;
	if(dim != NULL) {
		while(dim != NULL) {
			dim1 = dim->next;
			if(dim->dim_inq != NULL) {
				NclFree(dim->dim_inq);
			}
			NclFree(dim);
			dim = dim1;
		}
	}
	dim = thefile->ft_dims;
	if(dim != NULL) {
		while(dim != NULL) {
			dim1 = dim->next;
			if(dim->dim_inq != NULL) {
				NclFree(dim->dim_inq);
			}
			NclFree(dim);
			dim = dim1;
		}
	}
	dim = thefile->lv_dims;
	if(dim != NULL) {
		while(dim != NULL) {
			dim1 = dim->next;
			if(dim->dim_inq != NULL) {
				NclFree(dim->dim_inq);
			}
			NclFree(dim);
			dim = dim1;
		}
	}
	dim = thefile->grid_dims;
	if(dim != NULL) {
		while(dim != NULL) {
			dim1 = dim->next;
			if(dim->dim_inq != NULL) {
				if(dim->dim_inq->gds != NULL) {
					Grib2FreeGDS(dim->dim_inq->gds);
				}
				NclFree(dim->dim_inq);
			}
			NclFree(dim);
			dim = dim1;
		}
	}
	dim = thefile->scalar_dims;
	if(dim != NULL) {
		while(dim != NULL) {
			dim1 = dim->next;
			if(dim->dim_inq != NULL) {
				NclFree(dim->dim_inq);
			}
			NclFree(dim);
			dim = dim1;
		}
	}
	if (thefile->options) {
		NclFree(thefile->options);
	}
	NclFree(therec);
}


static NclQuark *Grib2GetVarNames
# if    NhlNeedProto
(void* therec, int *num_vars)
# else
(therec, num_vars)
void*   therec;
int *num_vars;
# endif
{
	Grib2FileRecord *thefile = (Grib2FileRecord*)therec;
	Grib2ParamList *step;
	Grib2InternalVarList *vstep;
	int i;
	NclQuark *arout;

	*num_vars = thefile->n_vars + thefile->n_internal_vars;
	arout = (NclQuark*)NclMalloc((unsigned)sizeof(NclQuark)* *num_vars);


	step = thefile->var_list;	
	for(i = 0; i < thefile->n_vars; i++) {
		arout[i] = step->var_info.var_name_quark;
		step = step->next;
	}

	vstep = thefile->internal_var_list;
	for(; i < thefile->n_vars + thefile->n_internal_vars; i++) {
		arout[i] = vstep->int_var->var_info.var_name_quark;
		vstep = vstep->next;
	}
	return arout;
}


static NclFVarRec *Grib2GetVarInfo
# if    NhlNeedProto
(void *therec, NclQuark var_name)
# else
(therec, var_name)
    void*   therec;
    NclQuark    var_name;
# endif
{
    Grib2FileRecord *thefile = (Grib2FileRecord *) therec;
    Grib2ParamList *step;
    NclFVarRec *tmp;
    Grib2InternalVarList *vstep;
    int i;

    vstep = thefile->internal_var_list;
    while(vstep != NULL) {
        if (vstep->int_var->var_info.var_name_quark == var_name) {
            tmp = (NclFVarRec*)NclMalloc(sizeof(NclFVarRec));
            tmp->var_name_quark  = vstep->int_var->var_info.var_name_quark;
            tmp->var_full_name_quark  = vstep->int_var->var_info.var_name_quark;
            tmp->var_real_name_quark  = vstep->int_var->var_info.var_name_quark;
            tmp->data_type  = vstep->int_var->var_info.data_type;
            tmp->num_dimensions  = vstep->int_var->var_info.num_dimensions;
            for (i = 0; i < tmp->num_dimensions; i++) {
                tmp->file_dim_num[i]  = vstep->int_var->var_info.file_dim_num[i];
            }
		
            return tmp;
        } else {
		    vstep = vstep->next;
        }
    }	

    step = thefile->var_list;	
    while(step != NULL) {
        if (step->var_info.var_name_quark == var_name) {
            tmp = (NclFVarRec*)NclMalloc(sizeof(NclFVarRec));
            tmp->var_name_quark  = step->var_info.var_name_quark;
            tmp->var_full_name_quark  = step->var_info.var_name_quark;
            tmp->var_real_name_quark  = step->var_info.var_name_quark;
            tmp->data_type  = step->var_info.data_type;
            tmp->num_dimensions  = step->var_info.num_dimensions;
            for (i = 0; i < tmp->num_dimensions; i++) {
                tmp->file_dim_num[i]  = step->var_info.file_dim_num[i];
            }

            return tmp;
        } else {
            step = step->next;
        }
    }

    return NULL;
}


static NclQuark *Grib2GetDimNames
# if    NhlNeedProto
(void* therec, int* num_dims)
# else
(therec, num_dims)
    void*   therec;
    int*    num_dims;
# endif
{
    Grib2FileRecord *thefile = (Grib2FileRecord *) therec;
    Grib2DimInqRecList *dstep;
    NclQuark *dims;
    int i,
        j;

    dims = (NclQuark *) NclMalloc((unsigned int) sizeof(NclQuark) * thefile->total_dims);
    i = 0;
    *num_dims = thefile->total_dims;
    dstep = thefile->scalar_dims;
    for(j=0; j < thefile->n_scalar_dims; j++) {
	    dims[dstep->dim_inq->dim_number] = dstep->dim_inq->dim_name;	
    	dstep = dstep->next;
    }

    dstep = thefile->probability_dims;
    for(j=0; j < thefile->n_probability_dims; j++) {
	    dims[dstep->dim_inq->dim_number] = dstep->dim_inq->dim_name;	
    	dstep = dstep->next;
    }

    dstep = thefile->ensemble_dims;
    for(j=0; j < thefile->n_ensemble_dims; j++) {
	    dims[dstep->dim_inq->dim_number] = dstep->dim_inq->dim_name;	
    	dstep = dstep->next;
    }

    dstep = thefile->it_dims;
    for(j=0; j < thefile->n_it_dims; j++) {
	    dims[dstep->dim_inq->dim_number] = dstep->dim_inq->dim_name;	
    	dstep = dstep->next;
    }

    dstep = thefile->ft_dims;
    for(j=0; j < thefile->n_ft_dims; j++) {
    	dims[dstep->dim_inq->dim_number] = dstep->dim_inq->dim_name;	
	    dstep = dstep->next;
    }

    dstep = thefile->lv_dims;
    for(j=0; j < thefile->n_lv_dims; j++) {
	    dims[dstep->dim_inq->dim_number] = dstep->dim_inq->dim_name;	
    	dstep = dstep->next;
    }

    dstep = thefile->grid_dims;
    for(j=0; j < thefile->n_grid_dims; j++) {
	    dims[dstep->dim_inq->dim_number] = dstep->dim_inq->dim_name;	
    	dstep = dstep->next;
    }

    return dims;
}


static NclFDimRec *Grib2GetDimInfo
#if	NhlNeedProto
(void* therec, NclQuark dim_name_q)
#else
(therec,dim_name_q)
void* therec;
NclQuark dim_name_q;
#endif
{
Grib2FileRecord *thefile = (Grib2FileRecord*)therec;
Grib2DimInqRecList *dstep;
NclFDimRec *tmpd = NULL;
char *tmp;

tmp = NrmQuarkToString(dim_name_q);
/*
* first character is either i,f, g or l
*/
	dstep = thefile->scalar_dims;
	while(dstep != NULL) {
		if(dstep->dim_inq->dim_name == dim_name_q) {
			tmpd = (NclFDimRec*)NclMalloc(sizeof(NclFDimRec));
			tmpd->dim_name_quark = dim_name_q;
			tmpd->dim_size = dstep->dim_inq->size;
			tmpd->is_unlimited = 0;
			return tmpd;
		}
		dstep = dstep->next;
	}		
	dstep = thefile->probability_dims;
	while(dstep != NULL) {
		if(dstep->dim_inq->dim_name == dim_name_q) {
			tmpd = (NclFDimRec*)NclMalloc(sizeof(NclFDimRec));
			tmpd->dim_name_quark = dim_name_q;
			tmpd->dim_size = dstep->dim_inq->size;
			tmpd->is_unlimited = 0;
			return tmpd;
		}
		dstep = dstep->next;
	}		
	dstep = thefile->ensemble_dims;
	while(dstep != NULL) {
		if(dstep->dim_inq->dim_name == dim_name_q) {
			tmpd = (NclFDimRec*)NclMalloc(sizeof(NclFDimRec));
			tmpd->dim_name_quark = dim_name_q;
			tmpd->dim_size = dstep->dim_inq->size;
			tmpd->is_unlimited = 0;
			return tmpd;
		}
		dstep = dstep->next;
	}		
	dstep = thefile->it_dims;
	while(dstep != NULL) {
		if(dstep->dim_inq->dim_name == dim_name_q) {
			tmpd = (NclFDimRec*)NclMalloc(sizeof(NclFDimRec));
			tmpd->dim_name_quark = dim_name_q;
			tmpd->dim_size = dstep->dim_inq->size;
			tmpd->is_unlimited = 0;
			return tmpd;
		}
		dstep = dstep->next;
	}		
	dstep = thefile->ft_dims;
	while(dstep != NULL) {
		if(dstep->dim_inq->dim_name == dim_name_q) {
			tmpd = (NclFDimRec*)NclMalloc(sizeof(NclFDimRec));
			tmpd->dim_name_quark = dim_name_q;
			tmpd->dim_size = dstep->dim_inq->size;
			tmpd->is_unlimited = 0;
			return tmpd;
		}
		dstep = dstep->next;
	}		
	dstep = thefile->grid_dims;
	while(dstep != NULL) {
		if(dstep->dim_inq->dim_name == dim_name_q) {
			tmpd = (NclFDimRec*)NclMalloc(sizeof(NclFDimRec));
			tmpd->dim_name_quark = dim_name_q;
			tmpd->dim_size = dstep->dim_inq->size;
			tmpd->is_unlimited = 0;
			return tmpd;
		}
		dstep = dstep->next;
	}		
	dstep = thefile->lv_dims;
	while(dstep != NULL) {
		if(dstep->dim_inq->dim_name == dim_name_q) {
			tmpd = (NclFDimRec*)NclMalloc(sizeof(NclFDimRec));
			tmpd->dim_name_quark = dim_name_q;
			tmpd->dim_size = dstep->dim_inq->size;
			tmpd->is_unlimited = 0;
			return tmpd;
		}
		dstep = dstep->next;
	}		
	return NULL;
}

static NclFAttRec *Grib2GetAttInfo
#if	NhlNeedProto
(void* therec, NclQuark att_name_q)
#else
(therec, att_name_q)
void* therec;
NclQuark att_name_q;
#endif
{
return NULL;
}

static NclQuark *Grib2GetAttNames
#if	NhlNeedProto
(void* therec,int *num_atts)
#else
(therec,num_atts)
void* therec;
int *num_atts;
#endif
{	
*num_atts = 0;
return NULL;
}

static NclQuark *Grib2GetVarAttNames
#if	NhlNeedProto
(void *therec , NclQuark thevar, int* num_atts)
#else
(therec , thevar, num_atts)
void *therec;
NclQuark thevar;
int* num_atts;
#endif
{
Grib2FileRecord *thefile = (Grib2FileRecord*)therec;
Grib2ParamList *step;
Grib2InternalVarList *vstep;
NclQuark *arout = NrmNULLQUARK;
Grib2AttInqRecList *theatts = NULL;
int i;


vstep = thefile->internal_var_list;
while(vstep != NULL) {
	if(vstep->int_var->var_info.var_name_quark == thevar) {
		*num_atts = vstep->int_var->n_atts;
		arout = (NclQuark*)NclMalloc(sizeof(NclQuark)*vstep->int_var->n_atts);
		theatts = vstep->int_var->theatts;
		break;
	} else {
		vstep = vstep->next;
	}
}	

if(vstep == NULL ) {
	step = thefile->var_list;	
	while(step != NULL) {
		if(step->var_info.var_name_quark == thevar) {
			*num_atts = step->n_atts;
			arout = (NclQuark*)NclMalloc(sizeof(NclQuark)*step->n_atts);
			theatts = step->theatts;
			break;
		} else {
			step = step->next;
		}
	}
}
if((arout != NrmNULLQUARK)&&(theatts!= NULL))  {
	for(i = 0; i < *num_atts; i++) {
		arout[i] = theatts->att_inq->name;
		theatts = theatts->next;
	}
	return arout;
} else {
	*num_atts = 0;	
	return NULL;
}
}


static NclFAttRec *Grib2GetVarAttInfo
#if	NhlNeedProto
(void *therec, NclQuark thevar, NclQuark theatt)
#else
(therec, thevar, theatt)
void *therec;
NclQuark thevar;
NclQuark theatt;
#endif
{
	Grib2FileRecord *thefile = (Grib2FileRecord *) therec;
	Grib2ParamList *step;
	Grib2InternalVarList *vstep;
	Grib2AttInqRecList *theatts = NULL;
	NclFAttRec *tmp;


	vstep = thefile->internal_var_list;
	while(vstep != NULL) {
		if(vstep->int_var->var_info.var_name_quark == thevar) {
			theatts = vstep->int_var->theatts;
			break;
		} else {
			vstep = vstep->next;
		}
	}	
	if(vstep == NULL ) {
		step = thefile->var_list;	
		while(step != NULL) {
			if(step->var_info.var_name_quark == thevar) {
				theatts = step->theatts;
				break;
			} else {
				step = step->next;
			}
		}
	}
	if(theatts!= NULL)  {
		while(theatts != NULL) {
			if(theatts->att_inq->name == theatt) {
				tmp = (NclFAttRec*)NclMalloc(sizeof(NclFAttRec));
				tmp->att_name_quark = theatt;
				tmp->data_type = theatts->att_inq->thevalue->multidval.data_type;
				tmp->num_elements = theatts->att_inq->thevalue->multidval.totalelements;
				return tmp;
			}
			theatts = theatts->next;
		}
	} 
	return NULL;
}

static void _g2AdjustCacheTypeAndMissing
#if NhlNeedProto
(int int_or_float,NclMultiDValData the_dat,NclScalar *missingv)
#else
(int_or_float,the_dat,missingv)
(int int_or_float,NclMultiDValData the_dat,NclScalar *missingv)
#endif
{
	if(int_or_float) {
		the_dat->multidval.hlu_type_rep[0] = ((NclTypeintClass)nclTypeintClass)->type_class.hlu_type_rep[0];
		the_dat->multidval.hlu_type_rep[1] = ((NclTypeintClass)nclTypeintClass)->type_class.hlu_type_rep[1];
		the_dat->multidval.data_type = ((NclTypeintClass)nclTypeintClass)->type_class.data_type;
		the_dat->multidval.type = (NclTypeClass)nclTypeintClass;
		if(missingv != NULL) {
			the_dat->multidval.missing_value.has_missing = 1;
			the_dat->multidval.missing_value.value = *missingv;
		} else {
			the_dat->multidval.missing_value.has_missing = 0;
		}
	} else {
        /* Type is float by default */
		if(missingv != NULL) {
			the_dat->multidval.missing_value.has_missing = 1;
			the_dat->multidval.missing_value.value = *missingv;
		} else {
			the_dat->multidval.missing_value.has_missing = 0;
		}
	}
}

static NclMultiDValData  _g2GetCacheVal
#if	NhlNeedProto
(Grib2FileRecord *therec, Grib2ParamList *step, Grib2RecordInqRec *current_rec)
#else
(therec,step,current_rec)
Grib2FileRecord *therec;
Grib2ParamList *step;
Grib2RecordInqRec *current_rec;
#endif
{
	NclGrib2CacheList *thelist;
	NclGrib2CacheRec *tmp;
	int i;
	int tg;
	void *val;
	int cache_size = MAX(1,(int)(therec->options[GRIB_CACHE_SIZE_OPT].values));

	thelist = therec->grib_grid_cache;
	while(thelist != NULL) {
		if (thelist->grid_index == step->grid_index) {
			while (thelist->n_entries > cache_size) {
				tmp = thelist->tail;
				thelist->tail = tmp->prev;
				if (thelist->tail)
					thelist->tail->next = NULL;
				NclFree(tmp);
				thelist->n_entries--;
			}
			if(thelist->n_entries == cache_size) {
				tmp = thelist->tail;
				tmp->rec->the_dat = NULL;
				tmp->rec = current_rec;
				if (tmp->prev) {
					tmp->prev->next = NULL;
					thelist->tail = tmp->prev;
					tmp->prev = NULL;
					tmp->next = thelist->thelist;
					tmp->next->prev = tmp;
					thelist->thelist = tmp;
				}
				return(tmp->thevalue);
			} 
			if(thelist->n_entries == 0) {
				thelist->thelist = NclMalloc(sizeof(NclGrib2CacheRec));
				thelist->thelist->prev = NULL;
				thelist->thelist->next = NULL;
				thelist->thelist->rec = current_rec;
				thelist->tail = thelist->thelist;
				tg = 1;
				for(i = 0; i< thelist->n_dims; i++) {
					tg*=thelist->dimsizes[i];
				}
				val = NclMalloc(sizeof(float)*tg);
				thelist->thelist->thevalue = _NclCreateVal(NULL,
								NULL,
								Ncl_MultiDValData,
								0,
								val,
								NULL,
								thelist->n_dims,
								thelist->dimsizes,
								PERMANENT,
								NULL,
								nclTypefloatClass);
				thelist->n_entries = 1;
				return(thelist->thelist->thevalue);
			} else {
				tmp = NclMalloc(sizeof(NclGrib2CacheRec));
				tmp->prev = NULL;
				tmp->next = thelist->thelist;
				tmp->next->prev = tmp;
				tmp->rec = current_rec;
				tg = 1;
				for(i = 0; i< thelist->n_dims; i++) {
					tg*=thelist->dimsizes[i];
				}
				val = NclMalloc(sizeof(float)*tg);
                                tmp->thevalue = _NclCreateVal(NULL,
                                                                NULL,
                                                                Ncl_MultiDValData,
                                                                0,
                                                                val,
                                                                NULL,
                                                                thelist->n_dims,
                                                                thelist->dimsizes,
                                                                PERMANENT,
                                                                NULL,
                                                                nclTypefloatClass);
				++thelist->n_entries;
				
				thelist->thelist = tmp;
				return(tmp->thevalue);
			}
		} else {
			thelist = thelist->next;
		}
	}
	return(NULL);
}

static NclMultiDValData  _g2GetCacheMissingVal
#if	NhlNeedProto
(Grib2FileRecord *therec, Grib2ParamList *step, Grib2RecordInqRec *current_rec)
#else
(therec,step,current_rec)
Grib2FileRecord *therec;
Grib2ParamList *step;
Grib2RecordInqRec *current_rec;
#endif
{
	NclGrib2CacheList *thelist;
	int i;
	int tg;
	void *tmp;
	NclScalar missingv;

	thelist = therec->grib_grid_cache;
	while(thelist != NULL) {
		if (thelist->grid_index != step->grid_index) {
			thelist = thelist->next;
			continue;
		}
		if(step->var_info.data_type == NCL_int) {
			if (thelist->int_missing_rec != NULL) {
				return thelist->int_missing_rec;
			}
			tg = 1;
			for(i = 0; i <  thelist->n_dims; i++) {
				tg *= thelist->dimsizes[i];	
			}
			tmp = NclMalloc(sizeof(int) * tg);
			for( i = 0; i < tg; i++){
				((int*)tmp)[i] = G2_DEFAULT_MISSING_INT;
			}
			missingv.intval = G2_DEFAULT_MISSING_INT;
			
			thelist->int_missing_rec  = _NclCreateVal(
				NULL,
				NULL,
				Ncl_MultiDValData,
				0,
				tmp,
				&missingv,
				thelist->n_dims,
				thelist->dimsizes,
				PERMANENT,
				NULL,
				nclTypeintClass);
			return thelist->int_missing_rec;
		} else {
			if (thelist->float_missing_rec != NULL) {
				return thelist->float_missing_rec;
			}
			tg = 1;
			for(i = 0; i <  thelist->n_dims; i++) {
				tg *= thelist->dimsizes[i];	
			}
			tmp = NclMalloc(sizeof(float) * tg);
			for( i = 0; i < tg; i++){
				((float*)tmp)[i] = G2_DEFAULT_MISSING_FLOAT;
			}
			missingv.floatval = G2_DEFAULT_MISSING_FLOAT;
			
			thelist->float_missing_rec  = _NclCreateVal(
				NULL,
				NULL,
				Ncl_MultiDValData,
				0,
				tmp,
				&missingv,
				thelist->n_dims,
				thelist->dimsizes,
				PERMANENT,
				NULL,
				nclTypefloatClass
				);
			return thelist->float_missing_rec;
		}
	}
	return(NULL);
}


static void GetSphericalHarmonicData
#if	NhlNeedProto
(
	gribfield *gfld, 
	float *real, 
	float *imag
)
#else
(gfld,real,imag)
	gribfield *gfld;
	float *real;
	float *imag;
#endif
{
	g2SHTemplate *sh;
	float *fld;
	int JJ,KK,MM; /* full dataset */
	int Ts,Js,Ks,Ms; /* unpacked subset */
	int m,n,Ns,Nm;
	int fldix;
	float norm_factor;

	sh = (g2SHTemplate *) gfld->igdtmpl;
	JJ = sh->j_pent_res;
	KK = sh->k_pent_res;
	MM = sh->m_pent_res;

	Js = gfld->idrtmpl[5];
	Ks = gfld->idrtmpl[6];
	Ms = gfld->idrtmpl[7];
	Ts = gfld->idrtmpl[8];
	fld = gfld->fld;
	norm_factor = sqrt(2.0) * 2.0;

	fldix = 0;
	for (m=0; m<=MM; m++) {
		Nm=JJ;      /* triangular or trapezoidal */
		if ( KK == JJ+MM ) Nm=JJ+m;          /* rhombodial */
		Ns=Js;      /* triangular or trapezoidal */
		if ( Ks == Js+Ms ) Ns=Js+m;          /* rhombodial */
		for (n=m; n<=Nm; n++) {
			real[n * (KK+1) + m] = norm_factor * fld[fldix++];
			imag[n * (KK+1) + m] = norm_factor * fld[fldix++];
		}
	}
	return;
}
	

static void *GetData
#if	NhlNeedProto
(FILE *fp, Grib2RecordInqRec *rec,void **missing,int scan_mode_offset)
#else
(fp, rec,missing,scan_mode_offset)
FILE *fp;
Grib2RecordInqRec *rec;
void **missing;
int scan_mode_offset;
#endif
{
	int size;
	unsigned char *buf;
	gribfield *gfld;
	int err;
	float *ret_val;
	int i,j,k,n;
	int field_num;
	int force_linear = 0;
	int has_missing = 0;
	float missing2;
	int j_consecutive = 0;
	int alternating_direction = 0;
	int scan_mode = 0;

	buf = NclMalloc(rec->rec_size);
	fseek(fp,rec->offset,SEEK_SET);
	size = fread(buf,1,rec->rec_size,fp);
	if (size < rec->rec_size) {
		NclFree(buf);
		NhlPError(NhlFATAL,NhlEUNKNOWN,"Error reading GRIB file");
		return NULL;
	}
	field_num = MAX(1,rec->field_num);
	err = g2_getfld(buf,field_num,1,1,&gfld);
	if (err || ! gfld->unpacked || gfld->ndpts == 0) {
		NclFree(buf);
		NhlPError(NhlFATAL,NhlEUNKNOWN,"Error reading GRIB file");
		return NULL;
	}
	if (scan_mode_offset > -1) {
		scan_mode = gfld->igdtmpl[scan_mode_offset];
	}
	*missing = (void*)NclMalloc((unsigned)sizeof(float));
	*(float*)(*missing) = G2_DEFAULT_MISSING_FLOAT;
	if (gfld->idrtnum == 2 || gfld->idrtnum == 3) {
		if (gfld->idrtmpl[6] == 1) {
			if (gfld->idrtmpl[4] == 0)
				rdieee(gfld->idrtmpl + 7,(float *)(*missing),1);
			else
				*(float *)(*missing) = (float) gfld->idrtmpl[7];
		}
		else if (gfld->idrtmpl[6] == 2) {
			if (gfld->idrtmpl[4] == 0) {
				rdieee(gfld->idrtmpl + 7,(float *)(*missing),1);
				rdieee(gfld->idrtmpl + 8,&missing2,1);
			}
			else {
				*(float *)(*missing) = (float) gfld->idrtmpl[7];
				missing2 = (float) gfld->idrtmpl[8];
			}
			/* since we cannot handle 2 missing values we're going to 
			   set all values that equal the second missing value to
			   the first missing value */
			for (i = 0; i < gfld->ngrdpts; i++) {
				if (gfld->fld[i] == missing2) {
					gfld->fld[i] = *(float *)(*missing);
				}
			}
		}
	}
	NclFree(buf);

	if (gfld->igdtnum == 50) {
		float *real, *img;
		int n,ri,nx,ny;

		n = rec->the_dat->multidval.n_dims;
		ri = 2;
		nx = rec->the_dat->multidval.dim_sizes[n-1];
		ny = rec->the_dat->multidval.dim_sizes[n-2];  

		ret_val = NclMalloc(sizeof(float) * ri * nx * ny);
		memset(ret_val,0,sizeof(float) * ri * nx * ny);
		
		real = ret_val;
		img = real + nx * ny;
		
		if (gfld->idrtmpl[9] != 1 || gfld->idrtnum != 51) { 
			NhlPError(NhlWARNING,NhlEUNKNOWN,"Cannot decode spherical harmonic data that is not 32-bit float and stored using complex packing");
			g2_free(gfld);
			return ret_val;
		}
		GetSphericalHarmonicData(gfld,real,img);
	}
	else if (gfld->ibmap != 255 && gfld->bmap != NULL) {
		if (gfld->numoct_opt > 0) { /* thinned grid */
			n = rec->the_dat->multidval.n_dims;
			ret_val = NclMalloc(sizeof(float) * 
					    rec->the_dat->multidval.dim_sizes[n-2] *
					    rec->the_dat->multidval.dim_sizes[n-1]);
			force_linear = 1;
		}
		else {
			ret_val = NclMalloc(gfld->ngrdpts * sizeof(float));
		}

		for (i = 0; i < gfld->ngrdpts; i++) {
			if (gfld->bmap[i]) {
				ret_val[i]  =  gfld->fld[i];
			}
			else {
				ret_val[i]  = *(float*)(*missing);
				has_missing = 1;
			}
		}
	}
	else if (gfld->numoct_opt > 0) { /* thinned grid */
		n = rec->the_dat->multidval.n_dims;
		ret_val = NclMalloc(sizeof(float) * 
				    rec->the_dat->multidval.dim_sizes[n-2] *
				    rec->the_dat->multidval.dim_sizes[n-1]);
		memcpy(ret_val,(void*)gfld->fld,gfld->ngrdpts * sizeof(float));
	}
	else {
		ret_val = gfld->fld;
		gfld->fld = NULL;
	}
	if (gfld->numoct_opt > 0) { /* thinned grid */
		int jpmax;
		int kcode;
		int kret =1;
		float *ztemp, *zline, *zwork;
		float pmsval = DEFAULT_MISSING_FLOAT;
		int nlat,nlon;
		int operio = 0;
                int oveggy = 0;
		
		n = rec->the_dat->multidval.n_dims;
		nlat = rec->the_dat->multidval.dim_sizes[n-2];
		nlon = rec->the_dat->multidval.dim_sizes[n-1];
		
		if (gfld->interp_opt == 1) { /* thinned longitude */
			n = nlat;
			kcode = (rec->interp_method == 0 || force_linear) ? 1 : 3;
		}
		else {
			n = nlon;
			kcode = (rec->interp_method == 0 || force_linear) ? 11 : 13;
		}
		if (gfld->num_opt != n) {
			NhlPError(NhlFATAL,NhlEUNKNOWN,"Error reading GRIB file");
			return NULL;
		}
		jpmax = MAX(nlat,nlon/2 + 1);
		ztemp = NclMalloc(jpmax * jpmax * 2 * sizeof(float));
		zline = NclMalloc(jpmax * 2 * sizeof(float));
		zwork = NclMalloc((2 * jpmax + 3) * 3 * sizeof(float));
		if (! (ztemp && zline && zwork)) {
			NhlPError(NhlFATAL,ENOMEM,NULL);
			return NULL;
		}
		/*
		printf("%s -- has_missing: %d, force_linear: %d\n",
		       NrmQuarkToString(rec->var_name_q),has_missing,force_linear);
		*/
					  
		NGCALLF(qu2reg3,QU2REG3)(ret_val,gfld->list_opt,&nlat,&nlon,&kcode,&pmsval,&kret,
					 &has_missing,&operio,&oveggy,&jpmax,ztemp,zline,zwork);
		NclFree(ztemp);
		NclFree(zline);
		NclFree(zwork);
	}

	/* If the scan_mode indicates, then data may need to be reordered */

	if (scan_mode_offset > -1) {
		j_consecutive = g2getbits(scan_mode, 5, 1); /* not yet supported */
		alternating_direction = g2getbits(scan_mode, 4, 1);
	}

	if (alternating_direction) {
		int nx,ny;
		float *tmprow;

		n = rec->the_dat->multidval.n_dims;
		nx = rec->the_dat->multidval.dim_sizes[n-1];
		ny = rec->the_dat->multidval.dim_sizes[n-2];  
		tmprow = NclMalloc(sizeof(float) * nx);

		/* every other row is reversed */
		for (i = 1; i < ny; i+= 2) {
			memcpy(tmprow,ret_val + i * nx, nx * sizeof(float));
			for (j = 0, k = nx - 1; j < nx; j++, k--) {
				*(ret_val + i * nx + j) = tmprow[k];
			}
		}
		NclFree(tmprow);
	}
		
	g2_free(gfld);
	return ret_val;
}
	


static void *Grib2ReadVar
#if	NhlNeedProto
(void* therec, NclQuark thevar, long* start, long* finish,long* stride,void* storage)
#else
(therec, thevar, start, finish,stride,storage)
void* therec;
NclQuark thevar;
long* start;
long* finish;
long* stride;
void* storage;
#endif
{
	Grib2FileRecord *rec = (Grib2FileRecord*)therec;
	Grib2ParamList *step;
	Grib2RecordInqRec *current_rec;
	void *out_data;
	long *grid_start;
	long *grid_finish;
	long *grid_stride;
	int n_other_dims = 0;
	ng_size_t current_index[5] = {0,0,0,0,0};
	ng_size_t dim_offsets[5] = {-1,-1,-1,-1,-1};
	int i,j;
	ng_size_t offset;
	int done = 0,inc_done =0;
	ng_size_t data_offset = 0;
	void *tmp;
	void *missing;
	NclScalar missingv;
	FILE* fd;
	ng_size_t grid_dim_sizes[3];
	int n_grid_dims;
	NclMultiDValData tmp_md;
	NclSelectionRecord  sel_ptr;
	Grib2InternalVarList *vstep;
	int current_interp_method;

	vstep = rec->internal_var_list;
	while(vstep != NULL ) {
		if(vstep->int_var->var_info.var_name_quark == thevar) {
			sel_ptr.n_entries = vstep->int_var->var_info.num_dimensions;
			out_data = storage;
			for(i = 0; i < vstep->int_var->var_info.num_dimensions; i++ ) {
				sel_ptr.selection[i].sel_type = Ncl_SUBSCR;
				sel_ptr.selection[i].dim_num = i;
				sel_ptr.selection[i].u.sub.start = start[i];
				sel_ptr.selection[i].u.sub.finish = finish[i];
				sel_ptr.selection[i].u.sub.stride = stride[i];
				sel_ptr.selection[i].u.sub.is_single = 0;
			}
			tmp_md = (NclMultiDValData)_NclReadSubSection((NclData)vstep->int_var->value,&sel_ptr,NULL);
			memcpy((void*)&((char*)out_data)[data_offset],tmp_md->multidval.val,tmp_md->multidval.totalsize);
			if(tmp_md->obj.status != PERMANENT) {
				_NclDestroyObj((NclObj)tmp_md);
			}
			return(out_data);
		}
		vstep = vstep->next;

	}

	if ((NrmQuark)(rec->options[GRIB_THINNED_GRID_INTERPOLATION_OPT].values) == NrmStringToQuark("cubic")) {
		current_interp_method = 1;
	}
	else {
		current_interp_method = 0;
	}



	step = rec->var_list;
	while(step != NULL) {
		if(step->var_info.var_name_quark == thevar) {
			fd = fopen(NrmQuarkToString(rec->file_path_q),"r");
			vbuf = (void*)NclMalloc(4*getpagesize());
			setvbuf(fd,vbuf,_IOFBF,4*getpagesize());

			out_data = storage;

			if(step->var_info.doff == 1) {
				grid_start = &(start[(step->var_info.num_dimensions - 2) ]);
				grid_finish = &(finish[(step->var_info.num_dimensions - 2) ]);
				grid_stride = &(stride[(step->var_info.num_dimensions - 2) ]);
				n_other_dims = step->var_info.num_dimensions - 2;
				for(i = 0; i < n_other_dims; i++) {
					current_index[i] = start[i];
					dim_offsets[i] = step->var_info.dim_sizes[i];
					for (j = i + 1; j < n_other_dims; j++) {
						dim_offsets[i] *= step->var_info.dim_sizes[j];
					}
				}
				n_grid_dims = 2;
				grid_dim_sizes[0] = step->var_info.dim_sizes[step->var_info.num_dimensions - 2];
				grid_dim_sizes[1] = step->var_info.dim_sizes[step->var_info.num_dimensions - 1];
				sel_ptr.n_entries = 2;
				sel_ptr.selection[0].sel_type = Ncl_SUBSCR;
				sel_ptr.selection[0].dim_num = 0;
				sel_ptr.selection[0].u.sub.start = grid_start[0];
				sel_ptr.selection[0].u.sub.finish = grid_finish[0];
				sel_ptr.selection[0].u.sub.stride = grid_stride[0];
				sel_ptr.selection[0].u.sub.is_single = 0;
				sel_ptr.selection[1].sel_type = Ncl_SUBSCR;
				sel_ptr.selection[1].dim_num = 1;
				sel_ptr.selection[1].u.sub.start = grid_start[1];
				sel_ptr.selection[1].u.sub.finish = grid_finish[1];
				sel_ptr.selection[1].u.sub.stride = grid_stride[1];
				sel_ptr.selection[1].u.sub.is_single = 0;
			} else if(step->var_info.doff == 2) {
				grid_start = &(start[(step->var_info.num_dimensions - 3) ]);
				grid_finish = &(finish[(step->var_info.num_dimensions - 3) ]);
				grid_stride = &(stride[(step->var_info.num_dimensions - 3) ]);
				n_other_dims = step->var_info.num_dimensions - 3;
				
			
				for(i = 0; i < n_other_dims; i++) {
					current_index[i] = start[i];
					dim_offsets[i] = step->var_info.dim_sizes[i];
					for (j = i + 1; j < n_other_dims; j++) {
						dim_offsets[i] *= step->var_info.dim_sizes[j];
					}
				}
				n_grid_dims = 3;
				grid_dim_sizes[0] = step->var_info.dim_sizes[step->var_info.num_dimensions - 3];
				grid_dim_sizes[1] = step->var_info.dim_sizes[step->var_info.num_dimensions - 2];
				grid_dim_sizes[2] = step->var_info.dim_sizes[step->var_info.num_dimensions - 1];
				sel_ptr.n_entries = 3;
				sel_ptr.selection[0].sel_type = Ncl_SUBSCR;
				sel_ptr.selection[0].dim_num = 0;
				sel_ptr.selection[0].u.sub.start = grid_start[0];
				sel_ptr.selection[0].u.sub.finish = grid_finish[0];
				sel_ptr.selection[0].u.sub.stride = grid_stride[0];
				sel_ptr.selection[0].u.sub.is_single = 0;
				sel_ptr.selection[1].sel_type = Ncl_SUBSCR;
				sel_ptr.selection[1].dim_num = 1;
				sel_ptr.selection[1].u.sub.start = grid_start[1];
				sel_ptr.selection[1].u.sub.finish = grid_finish[1];
				sel_ptr.selection[1].u.sub.stride = grid_stride[1];
				sel_ptr.selection[1].u.sub.is_single = 0;
				sel_ptr.selection[2].sel_type = Ncl_SUBSCR;
				sel_ptr.selection[2].dim_num = 2;
				sel_ptr.selection[2].u.sub.start = grid_start[2];
				sel_ptr.selection[2].u.sub.finish = grid_finish[2];
				sel_ptr.selection[2].u.sub.stride = grid_stride[2];
				sel_ptr.selection[2].u.sub.is_single = 0;
			}
			

			offset = 0;
			while(!done) {
				offset = 0;
				if(n_other_dims > 0 ) {
					for(i = 0; i < n_other_dims - 1; i++) {
						offset += dim_offsets[i+1] * current_index[i];
					}
					offset += current_index[n_other_dims-1];
				}
				current_rec = step->thelist[offset].rec_inq;
	/*
	* For now(4/27/98) missing records persist, Eventually I'll implement one missing record per grid type for
	* general use. (8/13/07: now the data is shared although the records are still created -- dib)
	*/
				if(current_rec == NULL) {
					step->thelist[offset].rec_inq = _g2MakeMissingRec();
					current_rec = step->thelist[offset].rec_inq;
					current_rec->the_dat = _g2GetCacheMissingVal(therec,step,current_rec);
				}
				
				if((current_rec->the_dat == NULL) || 
				   (current_rec->interp_method != current_interp_method &&
				    current_rec->var_name_q > NrmNULLQUARK)) {
	/*
	* Retrieves LRU cache MultiDVal specific to this grid type
	*/
					if (current_rec->the_dat) {
						current_rec->interp_method = current_interp_method;
					}
					else {
						current_rec->the_dat = _g2GetCacheVal(therec,step,current_rec);
						if(current_rec->the_dat == NULL){
							NhlPError(NhlFATAL,NhlEUNKNOWN,
								  "NclGRIB2: Unrecoverable caching error reading variable %s; can't continue",
								  NrmQuarkToString(current_rec->var_name_q));
							fclose(fd);
							NclFree(vbuf);
							return(NULL);
						}
					}
					missing = NULL;
					tmp = NULL;
					tmp = GetData(fd,current_rec,&missing,step->gds->scan_mode_offset);
					if (tmp &&  current_rec->the_dat->multidval.val) {
						NclFree(current_rec->the_dat->multidval.val);
						current_rec->the_dat->multidval.val = tmp;
					}
					
					if(tmp != NULL) {
						if(step->var_info.data_type == NCL_int) {
							if(missing != NULL) {
								missingv.intval = *(int*)missing;
							} else {
								missingv.intval = G2_DEFAULT_MISSING_INT;
							}
						}
						else {
							if(missing != NULL) {
								missingv.floatval = *(float*)missing;
							} else {
								missingv.floatval = G2_DEFAULT_MISSING_FLOAT;
							}
						}
						/*
						 * Needed to fix chicken/egg problem with respect to type and missing values
						 */
						_g2AdjustCacheTypeAndMissing(0,current_rec->the_dat,(missing == NULL) ? NULL : &missingv);

						NclFree(missing);
					} else {
	/*
	* Need to figure out what to do here
	*/
					}
				} 
				if(current_rec->the_dat != NULL) {
					tmp_md = (NclMultiDValData)_NclReadSubSection((NclData)current_rec->the_dat,&sel_ptr,NULL);
					memcpy((void*)&((char*)out_data)[data_offset],tmp_md->multidval.val,tmp_md->multidval.totalsize);
					data_offset += tmp_md->multidval.totalsize;
					if(tmp_md->obj.status != PERMANENT) {
						_NclDestroyObj((NclObj)tmp_md);
					}
				} else {
					NhlPError(NhlFATAL,NhlEUNKNOWN,
						  "NclGRIB2: Unrecoverable error reading variable %s; can't continue",
						  NrmQuarkToString(current_rec->var_name_q));
					fclose(fd);
					NclFree(vbuf);
					return(NULL);
				}

				if(n_other_dims > 0 ) {	
					current_index[n_other_dims-1] += stride[n_other_dims-1];
					for(i = n_other_dims-1; i > 0 ; i--) {
						if(current_index[i] > finish[i]) {
							current_index[i] = start[i];
							current_index[i-1] += stride[i-1];
						} else {
							inc_done = 1;
						}
						if(inc_done) {
							inc_done = 0;
							break;
						}
					}
					if(current_index[0] > finish[0]) {
						done = 1;
					}
				} else {
					done = 1;
				}
			}
			fclose(fd);
			NclFree(vbuf);
			return(out_data);
		} 
		step = step->next;
	}
	NhlPError(NhlFATAL,NhlEUNKNOWN,"NclGRIB2: Variable (%s) is not an element of file (%s)",NrmQuarkToString(thevar),NrmQuarkToString(rec->file_path_q));

	return(NULL);
}



static NclFVarRec *Grib2GetCoordInfo
# if    NhlNeedProto
(void* therec, NclQuark thevar)
# else
(therec, thevar)
    void*   therec;
    NclQuark    thevar;
# endif
{
    return Grib2GetVarInfo(therec, thevar);
}


static void *Grib2ReadCoord
# if    NhlNeedProto
(void* therec, NclQuark thevar, long* start, long* finish, long* stride, void* storage)
# else
(therec, thevar, start, finish, stride, storage)
    void*   therec;
    NclQuark    thevar;
    long*   start;
    long*   finish;
    long*   stride;
    void*   storage;
# endif
{
    return Grib2ReadVar(therec, thevar, start, finish, stride, storage);
}

static void *Grib2ReadAtt
#if	NhlNeedProto
(void *therec,NclQuark theatt,void* storage)
#else
(therec,theatt,storage)
void * therec;
NclQuark theatt;
void* storage;
#endif
{
    return NULL;
}


static void *Grib2ReadVarAtt
#if	NhlNeedProto
(void * therec, NclQuark thevar, NclQuark theatt, void * storage)
#else
(therec, thevar, theatt, storage)
void * therec;
NclQuark thevar;
NclQuark theatt;
void* storage;
#endif
{
	Grib2FileRecord *thefile = (Grib2FileRecord*)therec;
	Grib2ParamList *step;
	Grib2InternalVarList *vstep;
	Grib2AttInqRecList *theatts = NULL;
	void *out_dat;

	vstep = thefile->internal_var_list;
	while(vstep != NULL) {
		if(vstep->int_var->var_info.var_name_quark == thevar) {
			theatts = vstep->int_var->theatts;
			break;
		} else {
			vstep = vstep->next;
		}
	}	

	if(vstep == NULL ) {
		step = thefile->var_list;	
		while(step != NULL) {
			if(step->var_info.var_name_quark == thevar) {
				theatts = step->theatts;	
				break;
			} else {
				step = step->next;
			}
		}
	}
	if(theatts!= NULL)  {
		while(theatts != NULL) {
			if(theatts->att_inq->name == theatt) {
				if(storage != NULL) {
					memcpy(storage,theatts->att_inq->thevalue->multidval.val,theatts->att_inq->thevalue->multidval.totalsize);
					return(storage);
				} else {
					out_dat = (void*)NclMalloc(theatts->att_inq->thevalue->multidval.totalsize);
					memcpy(out_dat,theatts->att_inq->thevalue->multidval.val,theatts->att_inq->thevalue->multidval.totalsize);
					return(out_dat);
				}
			}
			theatts = theatts->next;
		}
	}
	return(NULL);
}

static NclBasicDataTypes Grib2MapToNcl
# if    NhlNeedProto
(void* the_type)
# else
(the_type)
	void *the_type;
# endif
{
	int int_or_float = *(int *) the_type;

	if(int_or_float) {
		return(NCL_int);
	} else {
		return(NCL_float);
	}
}

static void *Grib2MapFromNcl
# if    NhlNeedProto
(NclBasicDataTypes the_type)
# else
(the_type)
	NclBasicDataTypes the_type;
# endif
{
    int *tmp ;

    tmp = (int *) NclMalloc((unsigned int) sizeof(int));
	
    switch (the_type) {
        case NCL_int:
            *tmp = 1;
            break;

        case NCL_float:
            *tmp = 0;
            break;

        default:
            *tmp = -1;
            break;
    }

    return((void *) tmp);
}

static void _g2UpdateGridTypeAttribute
(
	Grib2FileRecord *rec,
	NrmQuark interp_val
	)
{
	Grib2ParamList *step;
	NclQuark *tmp_string = NULL;
	Grib2AttInqRecList *step_att;
	NrmQuark grid_type_att_name;
	int method;
	g2codeTable *ct = NULL;
	char buf[512];

	step = rec->var_list;
	grid_type_att_name = NrmStringToQuark("grid_type");
	method = ((NrmQuark) rec->options[GRIB_THINNED_GRID_INTERPOLATION_OPT].values) ==
		NrmStringToQuark("cubic") ? 1 : 0;

	ct = (g2codeTable *) NclMalloc(1 * sizeof(g2codeTable));
	if (ct == NULL) {
		NhlPError(NhlFATAL, NhlEUNKNOWN,
			  " Unable to allocate code table data, cannot continue.");
		return;
	}
	memset(ct,0,sizeof(g2codeTable));

	while (step != NULL) {
		if (step->gds->is_thinned_grid) {
			step_att = step->theatts;
			while (step_att != NULL) {
				if (step_att->att_inq->name != grid_type_att_name) {
					step_att = step_att->next;
					continue;
				}
				tmp_string = (NrmQuark *) step_att->att_inq->thevalue->multidval.val;
				if (Grib2ReadCodeTable(step->ref_rec->table_source, 3, 
						       "3.1.table",step->grid_number,-1,ct) < NhlWARNING) {
					return;
				}
				if (ct->descrip) {
					sprintf(buf,"%s (quasi-regular grid expanded by %s interpolation)",
						ct->descrip, (method && ! step->has_bmap) ? "cubic" : "linear");
					*tmp_string = NrmStringToQuark(buf);
				}
				else {
					sprintf(buf,"%d (quasi-regular grid expanded by %s interpolation)",
						step->grid_number, (method && ! step->has_bmap) ? "cubic" : "linear");
					*tmp_string = NrmStringToQuark(buf);
				}
				break;
			}
		}
		step = step->next;
	}
	Grib2FreeCodeTableRec(ct);
	return;
}


static NhlErrorTypes Grib2SetOption
#if	NhlNeedProto
(void *therec,NclQuark option, NclBasicDataTypes data_type, int n_items, void * values)
#else
(therec,theatt,data_type,n_items,values)
	void *therec;
	NclQuark theatt;
	NclBasicDataTypes data_type;
	int n_items;
	void * values;
#endif
{
	Grib2FileRecord *rec = (Grib2FileRecord*)therec;
	int i;

	if (option ==  NrmStringToQuark("thinnedgridinterpolation")) {
		if (((NrmQuark) rec->options[GRIB_THINNED_GRID_INTERPOLATION_OPT].values) != *(NrmQuark *)values) {
			rec->options[GRIB_THINNED_GRID_INTERPOLATION_OPT].values = (void*) *(NrmQuark *)values;
			_g2UpdateGridTypeAttribute(rec,*(NrmQuark *)values);
		}
	}

	if (option ==  NrmStringToQuark("initialtimecoordinatetype")) {
		rec->options[GRIB_INITIAL_TIME_COORDINATE_TYPE_OPT].values = (void*) *(NrmQuark *)values;
		g2SetInitialTimeCoordinates(therec);
	}
	
	if (option ==  NrmStringToQuark("defaultncepptable")) {
		rec->options[GRIB_DEFAULT_NCEP_PTABLE_OPT].values = (void*) *(NrmQuark *)values;
	}
	if (option ==  NrmStringToQuark("printrecordinfo")) {
		rec->options[GRIB_PRINT_RECORD_INFO_OPT].values = (void *) *(int *)values;
	}
	if (option ==  NrmStringToQuark("singleelementdimensions")) {
		/* rec->options[GRIB_SINGLE_ELEMENT_DIMENSIONS_OPT].values = (void*) values; don't need to set this, it would need to be copied */
		rec->options[GRIB_SINGLE_ELEMENT_DIMENSIONS_OPT].n_values = n_items;
		rec->single_dims = GRIB_No_Dims;
		for (i = 0; i < n_items; i++) {
			if (((NrmQuark*)values)[i] == NrmStringToQuark("none")) {
				rec->single_dims = GRIB_No_Dims;
				break;
			}
			else if (((NrmQuark*)values)[i] == NrmStringToQuark("all")) {
				rec->single_dims = GRIB_All_Dims;
				break;
			}
			else if (((NrmQuark*)values)[i] == NrmStringToQuark("probability")) {
				rec->single_dims |= GRIB_Ensemble_Dims;
			}
			else if (((NrmQuark*)values)[i] == NrmStringToQuark("ensemble")) {
				rec->single_dims |= GRIB_Ensemble_Dims;
			}
			else if (((NrmQuark*)values)[i] == NrmStringToQuark("initial_time")) {
				rec->single_dims |= GRIB_Initial_Time_Dims;
			}
			else if (((NrmQuark*)values)[i] == NrmStringToQuark("forecast_time")) {
				rec->single_dims |= GRIB_Forecast_Time_Dims;
			}
			else if (((NrmQuark*)values)[i] == NrmStringToQuark("level")) {
				rec->single_dims |= GRIB_Level_Dims;
			}
		}
	}
	if (option ==  NrmStringToQuark("timeperiodsuffix")) {
		rec->options[GRIB_TIME_PERIOD_SUFFIX_OPT].values = (void*) *(int *)values;
	}
	if (option ==  NrmStringToQuark("cachesize")) {
		rec->options[GRIB_CACHE_SIZE_OPT].values = (void*) *(int *)values;
	}
	
	return NhlNOERROR;
}


NclFormatFunctionRec Grib2Rec = {
/* NclInitializeFileRecFunc initialize_file_rec */      Grib2InitializeFileRec,
/* NclCreateFileFunc	   create_file; */		Grib2CreateFile,
/* NclOpenFileFunc         open_file; */		Grib2OpenFile,
/* NclFreeFileRecFunc      free_file_rec; */		Grib2FreeFileRec,
/* NclGetVarNamesFunc      get_var_names; */		Grib2GetVarNames,
/* NclGetVarInfoFunc       get_var_info; */		Grib2GetVarInfo,
/* NclGetDimNamesFunc      get_dim_names; */		Grib2GetDimNames,
/* NclGetDimInfoFunc       get_dim_info; */		Grib2GetDimInfo,
/* NclGetAttNamesFunc      get_att_names; */		Grib2GetAttNames,
/* NclGetAttInfoFunc       get_att_info; */		Grib2GetAttInfo,
/* NclGetVarAttNamesFunc   get_var_att_names; */	Grib2GetVarAttNames,
/* NclGetVarAttInfoFunc    get_var_att_info; */		Grib2GetVarAttInfo,
/* NclGetCoordInfoFunc     get_coord_info; */		Grib2GetCoordInfo,
/* NclReadCoordFunc        read_coord; */		Grib2ReadCoord,
/* NclReadCoordFunc        read_coord; */		NULL,
/* NclReadVarFunc          read_var; */			Grib2ReadVar,
/* NclReadVarFunc          read_var; */			NULL,
/* NclReadAttFunc          read_att; */			Grib2ReadAtt,
/* NclReadVarAttFunc       read_var_att; */		Grib2ReadVarAtt,
/* NclWriteCoordFunc       write_coord; */		NULL,
/* NclWriteCoordFunc       write_coord; */		NULL,
/* NclWriteVarFunc         write_var; */		NULL,
/* NclWriteVarFunc         write_var; */		NULL,
/* NclWriteAttFunc         write_att; */		NULL,
/* NclWriteVarAttFunc      write_var_att; */		NULL,
/* NclAddDimFunc           add_dim; */			NULL,
/* NclAddChunkDimFunc      add_chunk_dim; */		NULL,
/* NclRenameDimFunc        rename_dim; */		NULL,
/* NclAddVarFunc           add_var; */			NULL,
/* NclAddVarChunkFunc      add_var_chunk; */		NULL,
/* NclAddVarChunkCacheFunc add_var_chunk_cache; */	NULL,
/* NclSetVarCompressLevelFunc set_var_compress_level; */ NULL,
/* NclAddVarFunc           add_coord_var; */		NULL,
/* NclAddAttFunc           add_att; */			NULL,
/* NclAddVarAttFunc        add_var_att; */		NULL,
/* NclMapFormatTypeToNcl   map_format_type_to_ncl; */	Grib2MapToNcl,
/* NclMapNclTypeToFormat   map_ncl_type_to_format; */	Grib2MapFromNcl,
/* NclDelAttFunc           del_att; */			NULL,
/* NclDelVarAttFunc        del_var_att; */		NULL,
#include "NclGrpFuncs.null"
/* NclSetOptionFunc        set_option;  */              Grib2SetOption
};


