[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[McIDAS #KUC-159541]: Memory errors with GINI Local Server in McIDAS-X



Hi Jess,

re:
> I can incorporate your mods, that is not a problems.

Very good.

re:
> I don't think we'd
> function very well if we couldn't since we have a lot of home grown
> McIDAS commands here.

:-)

re:
> Whenever you have a chance to send them, that'd
> be great.

I have attached the following files to this email:

giniutil.h
giniutil.c
giniadir.cp
giniaget.cp
servutil.c

Here is what I would do to incorporate the modified code:

<as 'mcidas'>
cd ~/bin
mkdir backup
mv giniadir giniaget backup
cd ~/mcidas<revision>/src
mkdir backup
mv giniutil.h giniutil.h giniadir.cp giniaget.cp servutil.c backup
-- copy the modified source files from this email to the ~/mcidas<revision>/src
   directory
make libsdi.a giniadir giniaget && ln giniadir giniaget ~/bin

re:
> Thanks and have a great weekend!

No worries and you too!

Cheers,

Tom
--
****************************************************************************
Unidata User Support                                    UCAR Unidata Program
(303) 497-8642                                                 P.O. Box 3000
address@hidden                                   Boulder, CO 80307
----------------------------------------------------------------------------
Unidata HomePage                       http://www.unidata.ucar.edu
****************************************************************************


Ticket Details
===================
Ticket ID: KUC-159541
Department: Support McIDAS
Priority: Normal
Status: Closed
/*
 * Copyright(c) 2004, Space Science and Engineering Center, UW-Madison
 * Refer to "McIDAS Software Acquisition and Distribution Policies"
 * in the file  mcidas/data/license.txt
 */

/**** $Id: giniutil.h,v 1.2 2004/05/17 22:12:04 beckys Rel $ ****/

/*  giniutil.h
**
**  This is the main include file for the NOAAPORT GINI ADDE servers
**
*/

/*
** Include files
*/

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include "m0arg.h"                /* McIDAS arg fetcher include file         */
#include "mcidas.h"               /* McIDAS include file                     */
#include "servutil.h"             /* Unidata ADDE server main include file   */

/*
** Build handling
**
** UNIDATA == 0 -> SSEC McIDAS build
** UNIDATA == 1 -> Unidata McIDAS build
*/

#ifndef UNIDATA
#define UNIDATA 0
#endif

/*
** Error handling
*/

#define ERR_STAT_MALLOC      -1   /* error mallocing memory                  */
#define ERR_STAT_DIR         -2   /* error reading directory                 */
#define ERR_STAT_NONAV      -31   /* error error initializing navigation     */
#define ERR_STAT_NOAUX      -39   /* error initializing aux block            */
#define ERR_STAT_NOCAL      -40   /* error initializing calibration          */
#define ERR_STAT_BADLINE    -41   /* error reading image line                */
#define ERR_STAT_BADZBLOK   -42   /* error reading Zlib compressed image blk */
#define ERR_STAT_BLANK_IMG  -47   /* image portion requested doesnt exist    */
#define ERR_STAT_SEEK       -49   /* error seeking to data portion of file   */
#define ERR_STAT_SELECT     -50   /* ??                                      */
#define ERR_STAT_NOIMG      -51   /* no images meet selection criteria       */

/*
** Default values
*/

#define DEF_NUM_ELEMS       640   /* default num of elements to send         */
#define DEF_NUM_LINES       480   /* default num of lines to send            */
#define READ_BUFFER_SIZE      1   /* # of image lines to buffer on read      */

/*
** Nav parameters
*/

#define EARTH_RAD_METERS    6371200     /* Spherical earth radius [m]        */
#define EARTH_ECCENTRICITY  0.0         /* earth eccentricity * 1e6          */
#define RAD_TO_DEG          57.295779   /* conversion from rads to degs      */
#define DEG_TO_RAD          0.017453292 /* conversion from degs to rads      */

/*
** GINI file values
*/

#define GINI_PIB_LEN        21        /* GINI Product Identification Block   */
#define GINI_PDB_LEN        512       /* GINI Product Description Block      */
#define GINI_HED_LEN        533       /* GINI Product Header                 */
#define GINI_ZBUF_LEN       5120      /* GINI Zlib read buffer               */

/*
** GINI server utility interface prototypes
*/

int CalibrateGiniImgData( int, int *, unsigned char *, int , char *, int );
int GetGiniDirs( FILELIST *, int *, int *, int *, int * );
int GetGiniLine( FILELIST *, READPARM *, int, unsigned char *, char * );
int GetGiniHeader( FILELIST *, unsigned char * );
int GetGiniTime( FILELIST *, int *, int *, int * );
int GetInt( unsigned char *, int );
int GiniToMcCal( unsigned char *, int * );
int GiniToMcDir( unsigned char *, int * );
int GiniToMcNav( unsigned char *, int * );
int IsZlibHed( unsigned char * );
int SelectGiniImages( CRITERIA *, FILELIST **, char * );
int TestGiniImages( FILELIST *, FILELIST **, CRITERIA * );
#if UNIDATA
Fint4 ispnghed_( unsigned char * );
#endif
/*
 * Copyright(c) 2004, Space Science and Engineering Center, UW-Madison
 * Refer to "McIDAS Software Acquisition and Distribution Policies"
 * in the file  mcidas/data/license.txt
 */

/**** $Id: giniutil.c,v 1.4 2009/11/18 23:06:02 tomy Tst $ ****/

#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#include <sys/stat.h>
#include <math.h>
#include "mcidas.h"
#include "giniutil.h"
#if UNIDATA
#include "png.h"
#endif
#include "zlib.h"
#include "zutil.h"

#define DEBUG_CALIB 0
#define DEBUG_DIRS  0
#define DEBUG_ENTRY 0
#define DEBUG_HEADR 0
#define DEBUG_IMAGE 0
#define DEBUG_LINE  0
#define DEBUG_MCCAL 0
#define DEBUG_MCDIR 0
#define DEBUG_MCNAV 0
#define DEBUG_TEST  0
#define DEBUG_TIME  0

static char dbg[MAX_ERR_LEN];    /* debug message */

/* <<<<< UPC add 20081204 - add needed function prototypes >>>>> */
Fint lit_( const char *, FsLen );


/*************************** CalibrateGiniImgData ****************************/

int
CalibrateGiniImgData( int band, int *calcod, unsigned char *data,
                      int nval, char *unit, int size )

/*
** Name:       CalibrateGiniImgData
**
** Purpose:    Convert GINI data to requested unit in place
**
** Parameters:
**             band   - band number
**             calcod - calibration codicil
**             data   - buffer of data to calibrate
**             nval   - number of bytes to convert
**             unit   - unit to convert to
**             size   - number of bytes per element to be returned
**
** Returns:
**             SUCCESS == 1
**             FAILURE == 0
**
*/

{

    unsigned short *s2;                     /* pointer to unsigned short     */
    unsigned short *cal2;                   /* pointer to calibration array  */

    int            *s4;                     /* pointer to unsigned short     */
    int            *cal4;                   /* pointer to calibration array  */
    int            *calblk;                 /* calibration segments for UNIT */

    int             i, j, k;                /* generic counters              */
    int             ival;                   /* brightness value as an int    */
    int             minB, maxB;             /* min,max calibration BRTI vals */
    int             ncals;                  /* # calibration segments        */

    int  dbz;                               /* echo strength [dbZ]           */
    int  dvip[]={ 0, 30, 40, 45, 50, 55 };  /* DVIP break points             */

    float          *a;                      /* val = (a * data + b)/s        */
    float          *b;                      /* val = (a * data + b)/s        */
    float          *s;                      /* scale factor                  */

    float           numer;                  /* numerator                     */
    float           denom;                  /* denominator                   */

    float           scale;                  /* scale factor from AUX block   */
    float           minval;                 /* min data val from AUX block   */
    float           maxval;                 /* max data val from AUX block   */

#if DEBUG_ENTRY
    M0sxtrce( "in CalibrateGiniImgData" );
#endif

#if DEBUG_CALIB
    (void) sprintf( dbg, "CalibrateGiniImgData:: unit: %s  band: %d  size: %d",
                    unit, band, size );
    M0sxtrce( dbg );
#endif

    /*
    ** If unit is RAW no calibration is necessary
    */

    if ( !strncmp( unit, "RAW", 3 ) ) {
#if DEBUG_CALIB
      M0sxtrce( "CalibrateGiniImgData:: RAW requires no calibration" );
#endif
      return SUCCESS;
    }

    /* <<<<< UPC mod 20110314 - scale TEMP by 10 to match servutil.c mod >>>>> 
*/
    scale = 1.0;
    if ( !strncmp( unit, "TEMP", 4 ) ) scale = 10.0;

    /*
    ** Count all calibration segments for UNIT in calcod
    */

    ncals = 0;
    for ( i = 0; i < calcod[0]; i++ )
      if ( !strncmp( unit, (char *) &calcod[1+(8*i)], strlen(unit) ) ) ncals++;

    if ( ncals == 0 ) {
      (void) sprintf( dbg, "CalibrateGiniImgData:: unit: %s  ncals: %d",
                      unit, ncals );
      M0sxtrce( dbg );
      return FAILURE;
    }

    /*
    ** allocate space for cal block for all UNIT cal segments
    */

    calblk = (int *) malloc( ncals * 8 * sizeof(int) + 1 );
    if ( calblk == (int *) NULL ) {
      M0sxtrce( "CalibrateGiniImgData:: calblk malloc error" );
      return ERR_STAT_MALLOC;
    }

    /*
    ** copy all UNIT cal segments from calcod to calblk
    */

    calblk[0] = ncals;
    k = 0;
    for ( i = 0; i < calcod[0]; i++ ) {
      if ( !strncmp( unit, (char *) &calcod[1+(8*i)], strlen(unit) ) ) {
        for ( j = 1; j < 9; j++ ) {
          calblk[j+(8*k)] = calcod[j+(8*i)];
        }
        k++;
      }
    }

    /*
    ** Create arrays of transform coefficients:  dval = a[i] * ival * b[i]
    */

    a = (float *) malloc( ncals * sizeof(float) );
    b = (float *) malloc( ncals * sizeof(float) );
    s = (float *) malloc( ncals * sizeof(float) );

    if ( a == (float *)NULL || b == (float *)NULL || s == (float *)NULL ) {
      M0sxtrce( "CalibrateGiniImgData:: transform coefficients malloc error" );
      return ERR_STAT_MALLOC;
    }

    for ( i = 0; i < ncals; i++ ) {

      numer = calblk[2+(i*8)] - calblk[3+(i*8)];
      denom = calblk[4+(i*8)] - calblk[5+(i*8)];
      a[i]  = numer / denom;
      b[i]  = calblk[2+(i*8)] - a[i] * calblk[4+(i*8)];
      s[i]  = calblk[7+(i*8)];
#if DEBUG_CALIB
      (void) sprintf( dbg, "CalibrateGiniImgData:: i: %d, a[i]: %f,  b[i]: %f, 
s[i]: %f",
                      i, a[i], b[i], s[i] );
      M0sxtrce( dbg );
#endif

    }

    /*
    ** Convert raw image data to calibrated unit
    */

    switch ( size ) {

      case 1:

        for ( i = 0; i < nval; i++ ) {
          ival = (int) data[i];
          k = -1;
          for ( j = 0; j < ncals; j++ ) {
            if ( calblk[4+(j*8)] <= ival && ival <= calblk[5+(j*8)] ) {
              k = j;
#if DEBUG_CALIB
              (void) sprintf( dbg, "CalibrateGiniImgData:: k: %d, minB: %d  
ival: %d  maxB: %d",
                              k, calblk[4+(j*8)], ival, calblk[5+(j*8)] );
              M0sxtrce( dbg );
#endif
            }
          }
          if ( k >= 0 )
            data[i] = scale * (a[k] * data[i] + b[k])/s[k];
          else
            data[i] = 0;
        }

        break;

    case 2:

      (void) sprintf( dbg, "CalibrateGiniImgData:: 2-byte calibration for %s",
                      unit );
      M0sxtrce( dbg );

      cal2 = (unsigned short *) malloc( nval * sizeof(short) );

      if ( cal2 == (unsigned short *) NULL ) {
        (void) memset( data, 0, nval );
        (void) strcat( dbg, "CalibrateGiniImgData:: 2-byte malloc error" );
        M0sxtrce( dbg );
        return ERR_STAT_MALLOC;
      }

      s2 = cal2;

      for ( i = 0; i < nval; i++ ) {
        ival = (int) data[i];
        k = -1;
        for ( j = 0; j < ncals; j++ ) {
          if ( calblk[4+(j*8)] <= ival && ival <= calblk[5+(j*8)] ) {
            k = j;
#if DEBUG_CALIB
            (void) sprintf( dbg, "CalibrateGiniImgData:: k: %d, minB: %d  ival: 
%d  maxB: %d",
                            k, calblk[4+(j*8)], ival, calblk[5+(j*8)] );
            M0sxtrce( dbg );
#endif
          }
        }

        if ( k >= 0 )
          *s2 = scale * (a[k] * ival + b[k])/s[k];
        else
          *s2 = 0;

        s2++;

      }

      (void) swbyt2_( cal2, &nval );
      (void) memcpy( data, cal2, nval * sizeof(short) );
      free( cal2 );

      break;

    case 4:

      (void) sprintf( dbg, "CalibrateGiniImgData:: 4-byte calibration for %s",
                      unit );
      M0sxtrce( dbg );

      cal4 = (int *) malloc( nval * sizeof(int) );

      if ( cal4 == (int *) NULL ) {
        (void) memset( data, 0, nval );
        (void) strcat( dbg, "CalibrateGiniImgData:: 4-byte malloc error" );
        M0sxtrce( dbg );
        return ERR_STAT_MALLOC;
      }

      s4 = cal4;

      for ( i = 0; i < nval; i++ ) {
        ival = (int) data[i];
        k = -1;
        for ( j = 0; j < ncals; j++ ) {
          if ( calblk[4+(j*8)] <= ival && ival <= calblk[5+(j*8)] ) {
            k = j;
#if DEBUG_CALIB
            (void) sprintf( dbg, "CalibrateGiniImgData:: k: %d, minB: %d  ival: 
%d  maxB: %d",
                            k, calblk[4+(j*8)], ival, calblk[5+(j*8)] );
            M0sxtrce( dbg );
#endif
          }
        }

        if ( k >= 0 )
          *s4 = scale * (a[k] * ival + b[k])/s[k];
        else
          *s4 = 0;

#if DEBUG_CALIB
        (void) sprintf( dbg, "CalibrateGiniImgData:: raw value: ival = %d, 
calibrated value: *s4 = %d", ival, *s4 );
        M0sxtrce( dbg );
#endif

        s4++;

      }

      (void) swbyt4_( cal4, &nval );
      (void) memcpy( data, cal4, nval * sizeof(int) );
      free( cal4 );

      break;

    default:

#if DEBUG_CALIB
      (void) sprintf( dbg, "CalibrateGiniImgData:: unsupported size type: %d",
                      size );
      M0sxtrce( dbg );
#endif

      return FAILURE;

    }

    return SUCCESS;

}

/******************************* GetGiniDirs *********************************/

int
GetGiniDirs( FILELIST *cur, int *aradir, int *navcod, int *calcod, int *auxblk )

/*
** Name:       GetGiniDirs
**
** Purpose:    Read a GINI file and create corresponding McIDAS area directory
**
** Parameters: 
**             cur     - FILELIST structure containing current image information
**                       name  - full qualified name of the file
**                       pos   - position to add to list
**                       hoff  - byte offset to beginning of image header
**                       doff  - byte offset to beginning of image data
**                       size  - number of lines in Zlib compressed block
**                       type  - file type
**                       time  - file data time [sec since 1970]
**             aradir  - McIDAS area directory for image
**             navcod  - McIDAS navigation codicil for image
**             calcod  - McIDAS calibration codicil for image
**             auxblk  - McIDAS supplementary block for image
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
** NOTES:
**             The indices for aradir in this function are zero-based.
**
*/

{

    unsigned char  header[GINI_PDB_LEN];    /* GINI header                   */

    char ctemp[GINI_PDB_LEN];

    char           cmemo[33];               /* memo field                    */
    char          *cmode[]={"Maintenance", "Clear Air", "Precip Mode"};
    char           ctilt[9];                /* radar tilt or layer           */
    char           ctitle[41];              /* AUX block memo field          */
    char           cunit[9];                /* product data unit             */

    int            auxsiz;                  /* AUX block size in words       */
    int            update_rate[]={10, 6, 5};/* clear air/precip/storm update */
    int            levels[256];

    int i,j;                                /* loop variables                */
    int rc;                                 /* function return status        */

    int creator;                            /* Originating center            */
    int band_id;                            /* GINI channel ID               */

    int ncalseg;                            /* number of calibration segments*/
    int nexrcode=0;                         /* code for equiv. NEXRAD prod.  */
    int res;                                /* product resolution [km]       */
    int min_val;                            /* product minimum value         */
    int max_val;                            /* product maximum value         */
    int scale;                              /* data scale factor             */
    int offset;                             /* data offset value             */
    int nlevels;                            /* number of data levels         */

    float delta;

    time_t datetime;                        /* holds time in seconds         */
    struct tm settime;                      /* time structure                */
    struct tm *crtime;                      /* time structure pointer        */

    int day;                                /* day   - DD  (e.g. 03)         */
    int imon;                               /* month - MMM (e.g. Jan)        */
    int year;                               /* year  - YY  (e.g. 98)         */

    struct stat file_stat;                  /* 'stat' information            */
    FILE *fd;                               /* file descriptor               */

#if DEBUG_ENTRY
    M0sxtrce( "in GetGiniDirs" );
#endif

    rc = GetGiniHeader( cur, header );

    if ( rc == FAILURE ) {
#if DEBUG_DIRS
      M0sxtrce( "GetGiniDirs: Error reading GINI image header" );
#endif
      return FAILURE;
    }

    /*************************************************************************/
    /*                          AREA directory                               */
    /*************************************************************************/

    /*
    ** Convert the GINI header information to McIDAS AREA file format
    */

    for ( i = 0; i < IMG_DIR_LEN; i++ ) {
      aradir[i] = 0;
    }

    rc = GiniToMcDir( header, aradir );

    /*
    ** Use the file creation time as the image creation time
    */

    (void) stat( cur->name, &file_stat );
    datetime = file_stat.st_ctime;

    crtime = gmtime( &datetime );

    aradir[16] =  1000*crtime->tm_year + crtime->tm_yday + 1;
    aradir[17] = 10000*crtime->tm_hour + 100*crtime->tm_min + crtime->tm_sec;

#if DEBUG_DIRS
    for ( i = 0; i < IMG_DIR_LEN; i++ ) {
      sprintf(ctemp, "GetGiniDirs:: aradir[%2d]: %d", i, aradir[i] );
      M0sxtrce( ctemp );
    }
#endif

    cur->size = file_stat.st_size-aradir[9]; /* size of file in bytes less
                                                GINI HDR and EOF records     */


    /*************************************************************************/
    /*                      Navigation Codicil                               */
    /*************************************************************************/

    /*
    ** Create a McIDAS navigation block from the GINI header information
    */

    for ( i = 0; i < NAV_COD_LEN; i++ ) {
      navcod[i] = 0;
    }

    rc = GiniToMcNav( header, navcod );


    /*************************************************************************/
    /*                      Calibration Codicil                              */
    /*************************************************************************/

    /*
    ** Convert the GINI header information to McIDAS calibration codicil or
    ** auxiliary block depending on band.
    */

    for ( i = 0; i < CAL_COD_LEN+1; i++ ) {
      calcod[i] = 0;
    }

    rc = GiniToMcCal( header, calcod );


    /*************************************************************************/
    /*                      Auxiliary block                                  */
    /*************************************************************************/

    for ( i = 0; i < AUX_BLK_LEN; i++ ) {
      auxblk[i] = 0;
    }

    creator = (int) *( header + 1 );       /* <<<<< 20051007 - UPC Add >>>>> */
    band_id = (int) *( header + 3 );

#if DEBUG_DIRS
    (void) sprintf( dbg, "GetGiniDirs:: create entity: %d, band id: %d",
                    creator, band_id );
    M0sxtrce( dbg );
#endif

    res = (int) *( header + 41 );                   /* image resolution [km] */

    if ( creator != 99 ) {                           /* NOAAPORT GINI images */

      switch ( band_id ) {             /* CAL block/AUX block depend on band */

        case 0:                                  /* No CAL/AUX block for VIS */
          break;

        case 2:                                    /* CAL block for IR bands */
        case 3:
        case 4:
        case 5:
        case 6:                            /* <<<<< 20051007 - UPC Add >>>>> */
        case 7:                            /* <<<<< 20051007 - UPC Add >>>>> */
  
          aradir[51] = lit_( "PRD ", 4 );          /* PRD calibration        */
          aradir[62] = aradir[33];                 /* start of cal block     */
          aradir[33] = aradir[62]+4*CAL_COD_LEN;   /* start of data block    */
  
          break;
  
      }

    } else {                      /* Unidata NEXRAD Level 3 composite images */

      switch ( band_id ) {             /* CAL block/AUX block depend on band */
  
        case 26:                              /* AUX block for NET composite */
  
          (void) sprintf( cmemo, "%d km CONUS NET Composite [K FT]", res );
          (void) strcpy( ctilt, "-99" );
          (void) strcpy( ctitle, "TOPS: Echo Tops" );
          (void) strcpy( cunit,  "K FT" );
  
          nexrcode   = 41;                    /* NEXRAD code for Echo Tops   */
  
          min_val    = calcod[2];
          max_val    = calcod[3];
          scale      = calcod[7];
          offset     = calcod[8];
          nlevels    = 16;
          delta      = (max_val - min_val) / (nlevels - 2);
          levels[0]  = -9997;
          levels[1]  = min_val;
          for ( i = 2; i < nlevels; i++ ) {
            levels[i] = levels[i-1] + delta;
          }
  
          break;
  
        case 27:                              /* AUX block for N0R composite */
        case 28:                              /* AUX block for NCR composite */
        case 32:                              /* AUX block for N0Q composite */

          if ( band_id == 27 ) {             /* NEXRAD Base Reflectivity N0R */
  
            (void) sprintf( cmemo, "%d km CONUS N0R Composite [dBZ]", res );
            (void) strncpy( ctilt, "Tilt 1", 6 );
            (void) strcpy( ctitle, "BREF: Base Reflectivity" );
            nexrcode   = 19;
            nlevels    = 22;
  
          } else if ( band_id == 32 ) {      /* NEXRAD Base Reflectivity N0Q */

            /* <<<<< UPC add 20101119 - for "high resolution" composites >>>>> 
*/
            (void) sprintf( cmemo, "%d km CONUS N0Q Composite [dBZ]", res );
            (void) strncpy( ctilt, "Tilt 1", 6 );
            (void) strcpy( ctitle, "BREF: Base Reflectivity" );
            nexrcode   = 94;
            nlevels    = 24;

          } else {                           /* NEXRAD Comp Reflectivity NCR */
  
            (void) sprintf( cmemo, "%d km CONUS NCR Composite [dBZ]", res );
            (void) strcpy( ctilt, "-99" );
            (void) strcpy( ctitle, "CREF: Composite Reflectivity" );
            nexrcode   = 37;
            nlevels    = 22;
  
          }
  
          (void) strcpy( cunit, "dBZ" );
  
          min_val    = calcod[2];
          max_val    = calcod[3];
          scale      = calcod[7];
          offset     = calcod[8];
          delta      = (float)(max_val - min_val) / (nlevels - 1);
          levels[0]  = min_val;
          for ( i = 1; i < nlevels; i++ ) {
            levels[i] = levels[i-1] + delta;
          }
  
          break;
  
        case 29:                              /* AUX block for NVL composite */
  
          (void) sprintf( cmemo, "%d km CONUS NVL Composite [mm]", res );
          (void) strcpy( ctilt, "-99" );
          (void) strcpy( ctitle, "VIL: Vertically-integrated Liquid Water" );
          (void) strcpy( cunit, "kg/m^2" );
  
          nexrcode   = 57;                    /* NEXRAD code for vert liq H2O*/
  
          min_val    = calcod[2];
          max_val    = calcod[3];
          scale      = calcod[7];
          offset     = calcod[8];
          nlevels    = 16;
          delta      = (max_val - min_val) / (nlevels - 1);
          levels[0]  = -9997;
          levels[1]  = 1;
          for ( i = 2; i < nlevels; i++ ) {
            levels[i] = delta * (i-1);
          }
  
          break;
  
        case 30:                              /* AUX block for N1P composite */
  
          (void) sprintf( cmemo, "%d km CONUS N1P Composite [IN]", res );
          (void) strcpy( ctilt, "-99" );
          (void) strcpy( ctitle, "PRE1: Surface 1-hour Rainfall Total" );
          (void) strcpy( cunit, "IN" );
  
          nexrcode   = 78;                    /* NEXRAD code for 1-hr Precip */
  
          min_val    = 0;
          max_val    = 240;
          scale      = 20;
          offset     = 0;
          nlevels    = 16;
          delta      = (max_val - min_val) / (nlevels - 1);
          for ( i = 0; i < 8; i++ ) {
            levels[i] = 5.7143*i;
          }
          for ( i = 8; i < 12 ; i++ ) {
            levels[i] = 40 + 20*(i-7);
          }
          for ( i = 12; i < nlevels ; i++ ) {
            levels[i] = 120 + 30*(i-11);
          }
  
          break;
  
        case 31:                              /* AUX block for NTP composite */
  
          (void) sprintf( cmemo, "%d km CONUS NTP Composite [IN]", res );
          (void) strcpy( ctilt, "-99" );
          (void) strcpy( ctitle, "PRET: Surface Storm Total Rainfall" );
          (void) strcpy( cunit, "IN" );
  
          nexrcode   = 80;                    /* NEXRAD code for Total Precip*/
  
          min_val    = 0;
          max_val    = 240;
          scale      = 10;
          offset     = 0;
          nlevels    = 16;
          delta      = (max_val - min_val) / (nlevels - 1);
          for ( i = 0; i < 8; i++ ) {
            levels[i] = 5.7143*i;
          }
          for ( i = 8; i < 12 ; i++ ) {
            levels[i] = 40 + 20*(i-7);
          }
          for ( i = 12; i < nlevels ; i++ ) {
            levels[i] = 120 + 30*(i-11);
          }
  
          break;
  
      }

    }

    if ( nexrcode ) {

      auxsiz     = 43 + nlevels;              /* AUX block length in words   */

      aradir[ 2] = 7;                         /* Radar                       */
      aradir[20] = lit_( "TWX ", 4 );         /* Closest NEXRAD to center    */
      aradir[21] = nexrcode;                  /* NEXRAD code for Echo Tops   */
      aradir[22] = 2;                         /* Composites every 5 minutes  */
      (void) strncpy( (char *) &aradir[24], cmemo, 32 );
      aradir[51] = lit_( "NEXR", 4 );         /* source type                 */
      aradir[52] = lit_( "RAW ", 4 );         /* RAW values stored in image  */

      aradir[56] = 0;
      aradir[59] = aradir[34]+4*NAV_COD_LEN;  /* byte offset to AUX block    */
      aradir[62] = 0;                         /* byte offset to cal block    */
      aradir[60] = 4 * auxsiz;                /* AUX block length in bytes   */
      aradir[33] = aradir[59] + aradir[60];   /* start of data block         */

      auxblk[ 0] = 0x04030201;                /* magic number                */
      auxblk[ 1] = 4 * auxsiz;                /* AUX block size in bytes     */
      auxblk[ 2] = 4;                         /* length of entry name in byte*/
      auxblk[ 3] = 4 * (auxsiz-5);            /* length of entry bytes       */
      auxblk[ 4] = lit_("INFO",4);            /* AUX block name is 'INFO'    */

      (void) memset( &auxblk[5], ' ', 40 );   /* memo field                  */
      (void) memcpy( &auxblk[5], ctitle, strlen(ctitle) );
      auxblk[15] = aradir[21];                /* NEXRAD product code         */
      auxblk[16] = 8;                         /* # bytes in radar textual ID */
      (void) memset( &auxblk[17], ' ', 8 );   /* radar ID                    */
      (void) memcpy( &auxblk[17], &aradir[20], 4 );
      auxblk[19] = navcod[3];                 /* radar latitude              */
      auxblk[20] = navcod[4];                 /* radar longitude             */
      auxblk[21] = 0;                         /* radar elevation             */
      auxblk[22] = aradir[3];                 /* composite DAY [YYYDDD]      */
      auxblk[23] = aradir[4];                 /* composite TIME [HHMMSS]     */
      auxblk[24] = 2;                         /* radar mode                  */
      auxblk[26] = res;                       /* resolution                  */
      auxblk[25] = 128;                       /* range                       */
      auxblk[27] = update_rate[aradir[22]];   /* update rate [10|6|5]        */
      (void) memset( &auxblk[28], ' ', 8 );   /* tilt angle or layer         */
      (void) memcpy( &auxblk[28], ctilt, strlen(ctilt) );
      auxblk[42] = scale;                     /* data scale                  */
      auxblk[30] = auxblk[42];                /* min/max scale factor        */
      auxblk[31] = min_val*auxblk[30];        /* min data value              */
      auxblk[32] = max_val*auxblk[30];        /* max data value              */
      auxblk[33] = auxblk[22];                /* start DAY [YYYDDD]          */
      auxblk[34] = auxblk[23];                /* start TIME [HHMMSS]         */
      auxblk[35] = auxblk[22];                /* end DAY [YYYDDD]            */
      auxblk[36] = auxblk[23];                /* end TIME [HHMMSS]           */
      auxblk[37] = 8;                         /* # bytes in unit             */
      (void) memset( &auxblk[38], ' ', 8 );   /* data unit                   */
      (void) memcpy( &auxblk[38], cunit, strlen(cunit) );
      auxblk[40] = nlevels;                   /* # data levels               */
      auxblk[41] = offset;                    /* data offset                 */
                                              /* auxblk[42] is scale above   */
      for ( i = 0; i < auxblk[40]; i++ ) {    /* scaled calibrated values    */
        auxblk[43+i] = levels[i];
      }

#if DEBUG_DIRS
    for ( i = 0; i < auxsiz; i++ ) {
      (void) sprintf( dbg, "GetGiniDirs:: auxblk[%3d] = 0x%08X %2d",
                      i, auxblk[i], auxblk[i] );
      M0sxtrce( dbg );
    }
#endif

    } else {

      auxblk[0] = -1;

    }

    /*
    ** Done
    */

    return SUCCESS;

}

/******************************* GetGiniLine *********************************/

int
GetGiniLine( FILELIST *cur, READPARM *read, int band, unsigned char *buf,
             char *err )

/*
** Name:       GetGiniLine
**
** Purpose:    Extract a line of data from a GINI image
**
** Parameters:
**             cur     - FILELIST structure containing current image information
**                       name  - full qualified name of the file
**                       pos   - position to add to list
**                       hoff  - byte offset to beginning of image header
**                       doff  - byte offset to beginning of image data
**                       size  - number of lines in Zlib compressed block
**                       type  - file type
**                       time  - file data time [sec since 1970]
**             read    - READPARM struct containing read specs
**             band    - band number of elements to read
**             buf     - buffer containing image data
**             err     - error string to return
**
** Returns:
**             SUCCESS == 1
**             FAILURE == 0
**
** History: 19980320 - Shamelessly adapted from a combination of SSEC example
**                     ADDE server code (MUG training set)
**          20030713 - Added support for Zlib compressed image read
**
*/

{

    static unsigned char *lbuf=NULL;          /* input line buffer           */
    static unsigned char *p;                  /* input line buffer pointer   */

    static int            eof=0;              /* end of data indicator       */
    static int            lastline=0;         /* last line of file read      */

    static FILE          *fd;                 /* file descriptor             */

    int                   nline=0;
    int                   rc;

    static size_t         lsize;              /* size of line input buffer   */
    static size_t         nmove=0;            /* # bytes to move in buffer   */
    static size_t         nread;              /* # bytes to read             */

#if UNIDATA
    static int            png_blin = -1;      /* PNG buffer beginning line   */
    static int            png_elin = -1;      /* PNG buffer ending line      */
    static png_structp    png_ptr;
#endif

    static unsigned char  zbuf[GINI_ZBUF_LEN];/* Zlib read buffer            */
    static int            z_blin = -1;        /* Zlib buffer beginning line  */
    static int            z_elin = -1;        /* Zlib buffer ending line     */
    static z_stream       d_stream;           /* Zlib decompression stream   */

#if DEBUG_LINE
    M0sxtrce( "in GetGiniLine" );
#endif

    /*
    ** If have not already done so, open the data file 'cur->name'
    */

    if ( lbuf == (unsigned char *) NULL ) {

      /*
      ** Allocate input buffer large enough to hold line of maximum length
      ** <<<<< UPC mod 20110307 - change size of lbuf to MAX_LINE_LEN >>>>>
      */

      if ( read->maxele > MAX_LINE_LEN ) {
        (void) sprintf( dbg, "GetGiniLine:: Unreasonable image line length %4d",
                        read->maxele );
        M0sxtrce( dbg );
        (void) strcpy( err, "Unreasonable image line length" );
        return ERR_STAT_BADLINE;
      }

      lsize = MAX_LINE_LEN;
      lbuf  = (unsigned char *) malloc( lsize );

      if ( lbuf == (unsigned char *) NULL ) {
        (void) sprintf( dbg, "GetGiniLine:: Unable to malloc %d bytes", lsize );
        M0sxtrce( dbg );
        (void) strcpy( err, "Unable to allocate memory" );
        return ERR_STAT_MALLOC;
      }

      /*
      ** Open image file and set location of first line in file
      */

      fd = fopen ( cur->name, "rb" );

      if ( fd == (FILE *) NULL ) {
        (void) sprintf( dbg, "GetGiniLine:: Unable to open input image" );
        M0sxtrce( dbg );
        (void) strcpy( err, "Unable to open input image" );
        return FAILURE;
      }

#if DEBUG_LINE
      (void) sprintf( dbg, "GetGiniLine:: cur->hoff = %4d, cur->doff = %4d in 
%s",
                      cur->hoff, cur->doff, cur->name );
      M0sxtrce( dbg );
#endif

      if ( fseek( fd, (long) cur->doff, SEEK_SET ) ) {
        (void) sprintf( dbg, "GetGiniLine:: Unable to seek to line" );
        M0sxtrce( dbg );
        (void) strcpy( err, "Unable to seek to data in input image" );
        (void) fclose( fd );
        return FAILURE;
      }

      if ( cur->type == 1 ) {

        (void) sprintf( dbg, "GetGiniLine:: read %4d Zlib compressed bytes",
                        GINI_ZBUF_LEN );
        M0sxtrce( dbg );

        nread = fread( zbuf, (size_t) 1, (size_t) GINI_ZBUF_LEN, fd );
        if ( nread != GINI_ZBUF_LEN ) {
          (void) sprintf( dbg, "GetGiniLine:: Unable to read Zlib buffer" );
          M0sxtrce( dbg );
          (void) strcpy( err, "Error reading input image" );
          (void) fclose( fd );
          return FAILURE;
        }

        d_stream.zalloc    = (alloc_func) 0;
        d_stream.zfree     = (free_func) 0;
        d_stream.opaque    = (voidpf) 0;
        d_stream.next_in   = (Bytef *) zbuf;
        d_stream.avail_in  = (uInt) nread;
        d_stream.next_out  = lbuf;
        d_stream.avail_out = (uInt) lsize;

        if ( inflateInit( &d_stream ) != Z_OK ) {
          M0sxtrce( "GetGiniLine:: Zlib inflateInit error" );
          return FAILURE;
        }

        rc = inflate( &d_stream, Z_NO_FLUSH );
        if ( rc != Z_OK && rc != Z_STREAM_END ) {
          M0sxtrce( "GetGiniLine:: Zlib inflate error" );
          return FAILURE;
        }

        cur->size = d_stream.total_out / read->maxele;
        z_blin    = 0;
        z_elin    = cur->size - 1;
        
        nmove = nread - d_stream.total_in;
        nread = d_stream.total_in;

#if UNIDATA
      } else if ( cur->type == 2 ) {

        int         bit_depth        = 8;
        int         color_type       = PNG_COLOR_TYPE_GRAY;
        int         interlace_type   = PNG_INTERLACE_NONE;
        int         compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
        int         filter_type      = PNG_FILTER_TYPE_DEFAULT;
        int         nbytes;

        png_uint_32 lines;
        png_uint_32 elems;

        png_infop   info_ptr;
        png_infop   end_info;

        (void) sprintf( dbg, "GetGiniLine:: process png compressed bytes" );
        M0sxtrce( dbg );

        /*
        ** Setup PNG pointers, etc.
        */
        png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
        if ( !png_ptr ) {
          (void) strcpy( err, "Error creating PNG read structure" );
          (void) fclose( fd );
          return FAILURE;
        }

        info_ptr = png_create_info_struct( png_ptr );
        end_info = png_create_info_struct( png_ptr );

        if ( !info_ptr ) {
          png_destroy_write_struct( &png_ptr, (png_infopp) NULL );
          (void) strcpy( err, "Error creating PNG info structure" );
          (void) fclose( fd );
          return FAILURE;
        }

        if ( setjmp(png_ptr->jmpbuf) ) {
          png_destroy_read_struct( &png_ptr, &info_ptr, &end_info );
          (void) strcpy( err, "Error setting jump in PNG image" );
          (void) fclose( fd );
          return FAILURE;
        }

        png_init_io( png_ptr, fd );

        /*
        ** Read the PNG informational header
        */
        png_read_info( png_ptr, info_ptr );
        png_get_IHDR( png_ptr, info_ptr, &elems, &lines, &bit_depth,
                      &color_type, &interlace_type, &compression_type,
                      &filter_type );

#if DEBUG_LINE
        (void) sprintf( dbg, "GetGiniLine:: lines,elems: %dx%d", lines, elems );
        M0sxtrce( dbg );
#endif

      }
#endif

    }

    /*
    ** Check to see if we have already gotten to the EOF or if the requested
    ** line is past the end of file
    */

    if ( eof ) {
      (void) sprintf( dbg, "GetGiniLine:: already at EOF" );
      M0sxtrce( dbg );
      (void) memset( buf, (int) 0, (size_t) read->maxele );
      (void) strcpy( err, "Already at EOF" );
      return ERR_STAT_BADLINE;
    }

    if ( read->beglin >= read->maxlin ) {
      (void) sprintf( dbg, "GetGiniLine:: read is past EOF" );
      M0sxtrce( dbg );
      eof = 1;
      (void) memset( buf, 0, (size_t) read->maxele );
      (void) strcpy( err, "Read is past EOF" );
      return ERR_STAT_BADLINE;
    }

    /*
    ** Read in the requested line
    */

#if DEBUG_LINE
    (void) sprintf( dbg, "GetGiniLine:: beglin: %d lline: %d mele: %d bele: %d",
                    read->beglin, lastline, read->maxele, read->begele );
    M0sxtrce( dbg );
#endif

    switch ( cur->type ) {

      case 0:                                         /* Uncompressed images */

        (void) sprintf( dbg, "GetGiniLine:: read uncompressed image line %d",
                        lastline );
        M0sxtrce( dbg );

        {
          long offset = (read->beglin - lastline) * read->maxele;

          if ( fseek( fd, offset, SEEK_CUR ) ) {
            (void) sprintf( dbg, "GetGiniLine:: error seeking to offset: %ld", 
offset );
            M0sxtrce( dbg );
            (void) memset( buf, 0, (size_t) read->maxele );
            (void) strcpy( err, "Unable to seek to data" );
            return ERR_STAT_SEEK;
          }

          if ( !fread( lbuf, (size_t) read->maxele, (size_t) 1, fd ) ) {
            (void) sprintf( dbg, "GetGiniLine:: error reading file: %d", 
read->maxele );
            M0sxtrce( dbg );
            return ERR_STAT_BADLINE;
          }
          p = lbuf;

          lastline = read->beglin + 1;
        }

        break;

      case 1:                                      /* Zlib-compressed images */

        (void) sprintf( dbg, "GetGiniLine:: read Zlib-compressed image line %d",
                        lastline );
        M0sxtrce( dbg );

#if DEBUG_LINE
        (void) sprintf( dbg, "GetGiniLine:: read->beglin = %4d, z_elin = %4d",
                        read->beglin, z_elin );
        M0sxtrce( dbg );
#endif

        while ( read->beglin > z_elin ) {

          (void) sprintf( dbg, "GetGiniLine:: nread = %4d, nmove = %4d",
                          nread, nmove );
          M0sxtrce( dbg );

          (void) memmove( zbuf, zbuf+nread, nmove );
          nread = fread( zbuf+nmove, (size_t)1, nread, fd );

          if ( ! nread ) {
            eof = 1;
            (void) memset( buf, 0, (size_t) read->maxele );
            (void) strcpy( err, "Already at EOF" );
            return ERR_STAT_BADLINE;
          }

          nmove = nmove + nread;

          d_stream.zalloc    = (alloc_func) 0;
          d_stream.zfree     = (free_func) 0;
          d_stream.opaque    = (voidpf) 0;
          d_stream.next_in   = (Bytef *) zbuf;
          d_stream.avail_in  = (uInt) nmove;
          d_stream.next_out  = lbuf;
          d_stream.avail_out = (uInt) lsize;

          if ( inflateInit( &d_stream ) != Z_OK ) {
            M0sxtrce( "GetGiniLine:: Zlib inflateInit error" );
            return FAILURE;
          }

          rc = inflate( &d_stream, Z_NO_FLUSH );
          if ( rc != Z_OK && rc != Z_STREAM_END ) {
            M0sxtrce( "GetGiniLine:: Zlib inflate error" );
            return FAILURE;
          }

          /*
          ** Sanity check.  Make sure number of lines in output buffer is 
consistent.
          */

          nline = d_stream.total_out / read->maxele;
          if ( nline != cur->size ) {
            eof = 1;
            (void) sprintf( dbg, "GetGiniLine:: line size mismatch wanted %2d 
got %2d",
                            cur->size, nline );
            M0sxtrce( dbg );
            (void) memset( buf, 0, (size_t) read->maxele );
            (void) strcpy( err, "Already at EOF" );
            return ERR_STAT_BADZBLOK;
          }

          nread  = d_stream.total_in;
          nmove -= d_stream.total_in;

          z_blin = z_elin + 1;
          z_elin = z_blin + cur->size - 1;

          (void) sprintf( dbg, "GetGiniLine:: z_blin = %4d, z_elin = %4d",
                          z_blin, z_elin );
          M0sxtrce( dbg );

        }

        p = (read->beglin - z_blin) * read->maxele + lbuf;

        break;

#if UNIDATA
      case 2:                               /* Unidata PNG-compressed images */

        (void) sprintf( dbg, "GetGiniLine:: read PNG-compressed image line %d",
                        lastline );
        M0sxtrce( dbg );

        while ( read->beglin > png_elin ) {
          png_read_row( png_ptr, (png_bytep) lbuf, NULL );
          png_elin++;
        }

        p = lbuf;

        break;
#endif

      default:

        (void) sprintf( dbg, "GetGiniLine:: unrecognized image type %d",
                        cur->type );
        M0sxtrce( dbg );

        return ERR_STAT_SELECT;

    }

    /*
    ** Copy the desired portion of the line to the output buffer and return
    */

#if DEBUG_LINE
    (void) sprintf( dbg, "GetGiniLine:: copied %d elements into line buffer",
                        read->maxele-read->begele );
    M0sxtrce( dbg );
#endif

    (void) memcpy( buf, p+read->begele, read->maxele-read->begele );

    return SUCCESS;

}

/******************************* GetGiniTime *********************************/

int
GetGiniTime( FILELIST *cur, int *date, int *time, int *ss )

/*
** Name:    GetGiniTime
**
** Purpose: Get the time of the current Gini image
**
** Note:    This routine reads image data from a copyrighted WSI NOWrad
**          formatted file.  This format is proprietary to WSI Corp. and
**          is not for redistribution.  No portion of this code may be
**          redistributed.
**
** Parameters:
**             cur     - FILELIST structure containing current image information
**                       name  - full qualified name of the file
**                       pos   - position to add to list
**                       hoff  - byte offset to beginning of image header
**                       doff  - byte offset to beginning of image data
**                       size  - number of lines in Zlib compressed block
**                       type  - file type
**                       time  - file data time [sec since 1970]
**             date    - image date [CCYYDDD]
**             time    - image time [HHMMSS]
**             ss      - image sensor source (=10)
**
** Returns:
**          SUCCESS == 1
**          FAILURE == 0
**
*/

{
    unsigned char  header[GINI_PDB_LEN];    /* GINI file header              */

    int   aradir[IMG_DIR_LEN];              /* AREA file header              */

    int   rc;                               /* function return status        */

    /* <<<<< UPC mod 20071025 - use tsec to get seconds since 19700101 >>>>> */
    int   tsec;                             /* seconds since Jan 1 1970      */

#if DEBUG_ENTRY
    M0sxtrce( "in GetGiniTime" );
#endif

    rc = GetGiniHeader( cur, header );

    if ( rc == FAILURE ) {
#if DEBUG_TIME
      M0sxtrce( "GetGiniTime: Error reading GINI image header" );
#endif
      return FAILURE;
    }

    /*
    ** Synthesize the AREA header
    */

    rc = GiniToMcDir( header, aradir );

    *ss   = aradir[2];
    *date = aradir[3];
    *time = aradir[4];
    
    /*
    ** Calculate image time
    */

    /* <<<<< UPC mod 20071025 - use tsec to get seconds since 19700101 >>>>> */
    rc = Mcdaytimetosec( *date, *time, &tsec );
    cur->time = (time_t) tsec;

    return SUCCESS;
    
}

/***************************** SelectGiniImages ******************************/

int
SelectGiniImages( CRITERIA *request, FILELIST **satisfy, char *err)

/*
** Name:       SelectGiniImages
**
** Purpose:    Return a list of GINI images
**
** Parameters: 
**             request - request criteria
**             satisfy - list of files that satisfy criteria
**             err     - textual error message
**
** Returns:
**             SUCCESS 1
**
*/

{

    int       ALL=lit_( "ALL ", 4 );        /* select all images in dir      */
    int       curpos=0;                     /* initial position number       */
    int       rc;                           /* function return status        */

    time_t    dtime=0;                      /* data time [sec since 1970]    */

    FILELIST  list={0};                     /* local FILELIST object         */

    FILELIST *head=NULL;                    /* list of files in directory    */
    FILELIST *cur=NULL;                     /* list of files in directory    */

    FILELIST *rawlist=NULL;                 /* list of files in directory    */

    FILELIST *test=NULL;                    /* list of files to test         */
    FILELIST *timlist=NULL;                 /* list of time ordered files    */

#if DEBUG_ENTRY
    M0sxtrce( "in SelectGiniImages" );
#endif

    /*
    ** The integer representation of 'ALL ' means get all the images in dir
    */

    M0swbyt4( &ALL, 1 );

    /*
    ** Generate a list of files whose names match 'request->filemask' in
    ** all directories under 'dir'.
    */

    rc = GetFileList( request, &rawlist );

    if ( rc == FAILURE ) {

      (void) sprintf( err, "error generating list of files" );
      return FAILURE;

    }

    /*
    ** Test images found to see if they match the patterns in 'request'.
    ** Add those that match to the renumbered list 'head'.
    */

    rc = TestGiniImages( rawlist, &head, request );

    if ( rc == FAILURE ) {
      (void) strcpy( err, "SelectGiniImages:: TestGiniImages memory allocation 
error" );
      return FAILURE;
    }

    /*
    ** Create the return list of files
    */

    if ( request->bpos == ALL ) {           /* ALL files requested           */

      M0sxtrce( "SelectGiniImages:: ALL files requested" );

      *satisfy = head;

    } else if ( request->bpos > 0 ) {       /* absolute positions requested  */

      M0sxtrce( "SelectGiniImages:: absolute file positions" );

      while ( head != NULL ) {

        /*
        ** Pop a file off of the stack and check position
        */

        rc = PopFile( &list, &head );

        if (list.pos < request->bpos || list.pos > request->epos) continue;

        /*
        ** Push file that passes test onto new 'test' stack
        */

        rc = PushFileByName( &list, &test );

        if ( rc == FAILURE ) {
          (void) strcpy( err, "Unable to allocate memory" );
          return FAILURE;
        }

      }

      *satisfy = test;

    } else if ( request->bpos <= 0 ) {      /* time relative file positions  */

      M0sxtrce( "SelectGiniImages:: relative file positions" );

      cur = head;
      while ( cur != NULL ) {               /* Reorder by time               */

        rc = PushFileByTime( cur, &test );
        cur = cur->next;

      }

      curpos = 1;

      while ( test != NULL ) {              /* pop file; check pos, push     */

        /*
        ** Decrement time dependent position
        */

        curpos--;

        /*
        ** Pop a file off of the stack and check position
        */

        rc = PopFile( &list, &test );
        if (curpos > request->bpos || curpos < request->epos) continue;

        /*
        ** Push file that passes test onto new 'timlist' stack
        */

        rc = PushFileByTime( &list, &timlist );

        if ( rc == FAILURE ) {
          (void) strcpy( err, "Unable to allocate memory" );
          return FAILURE;
        }

      }

      *satisfy = timlist;

    }

    return SUCCESS;

}

/****************************** TestGiniImages *******************************/

int
TestGiniImages( FILELIST *oldlist, FILELIST **newlist, CRITERIA *request )

/*
** Name:       TestGiniImage
**
** Purpose:    Test an image against the specification criteria detailed
**             in struct request
**
** Parameters: 
**             oldlist - FILELIST structure containing current image information
**                       name  - full qualified name of the file
**                       pos   - position to add to list
**                       hoff  - byte offset to beginning of image header
**                       doff  - byte offset to beginning of image data
**                       size  - number of lines in Zlib compressed block
**                       type  - file type
**                       time  - file data time [sec since 1970]
**             newlist - FILELIST list of images satisfing 'request' criteria
**                       name  - full qualified name of the file
**                       pos   - position to add to list
**                       hoff  - byte offset to beginning of image header
**                       doff  - byte offset to beginning of image data
**                       size  - number of lines in Zlib compressed block
**                       type  - file type
**                       time  - file data time [sec since 1970]
**             request - CRITERIA to test name
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
** NOTES:
**             No tests will be performed for CRITERIA values of -1
**
*/

{

    int         curpos=0;

    int         imgday;                     /* image day [CCYYDDD]           */
    int         imgss;                      /* satellite id for image        */
    int         imgtim;                     /* image time [HHMMSS]           */
    int         rc;                         /* function status               */

    FILELIST   *cur;                        /* current element of 'oldlist'  */
    FILELIST   *old;                        /* previous element of 'oldlist' */

    FILELIST   *last;                       /* previous element of 'newlist' */
    FILELIST   *next;                       /* next element of 'newlist'     */
    FILELIST   *this;                       /* current element of 'newlist'  */

#if DEBUG_ENTRY
    M0sxtrce( "in TestGiniImages" );
#endif

    /*
    ** Loop through input list of images and create an ouput list
    ** that has only ones that meet 'request' criteria
    */

    cur = oldlist;

    while ( cur != (FILELIST *) NULL ) {

      curpos++;

      /*
      ** Get a McIDAS date, time, and sensor source information for this image
      */

      rc = GetGiniTime( cur, &imgday, &imgtim, &imgss );

      if ( rc == FAILURE ) {
        old = cur;
        cur = cur->next;
        free( old );
        continue;
      }

      /*
      ** Test beginning and ending time
      */

      if ( request->begtim >= 0 ) {

        if (imgtim < request->begtim || imgtim > request->endtim) {
          M0sxtrce( "TestGiniImages:: fails begin/end time test" );
          old = cur;
          cur = cur->next;
          free( old );
          continue;
        }

      }

      /*
      ** Test beginning and ending day
      */

      if ( request->begday >= 0 ) {

        if (imgday < request->begday || imgday > request->endday) {
          M0sxtrce( "TestGiniImages:: fails begin/end day test" );
          old = cur;
          cur = cur->next;
          free( old );
          continue;
        }

      }

      /*
      ** Test beginning and ending McIDAS ss number
      */

      if ( request->begss >= 0 ) {

        if (imgss < request->begss || imgss > request->endss) {
          M0sxtrce( "TestGiniImages:: fails begin/end SS test" );
          old = cur;
          cur = cur->next;
          free( old );
          continue;
        }

      }

      /*
      ** At this point, we have an image that meets all 'request'
      ** criteria.
      */

      cur->pos = curpos;

      if ( *newlist == (FILELIST *) NULL ) {         /* output list is empty */

        *newlist   = cur;

      } else {

        last->next = cur;

      }

      last       = cur;
      cur        = cur->next;
      last->next = (FILELIST *) NULL;

    }

    /*
    ** Done
    */

    return SUCCESS;

}

/********************************** GetInt ***********************************/

int
GetInt( unsigned char *ptr, int num )

/*
** Name:       GetInt
**
** Purpose:    Convert GINI 2, 3, or 4-byte quantities to 4-byte int
**
** Parameters: 
**             ptr  - pointer to first header element of sequence
**             num  - number of bytes in quantity to convert
**
** Returns:
**             integer represented by byte sequence
**
** History:    ???????? - Create by SSEC for GINI to AREA file converter
**             19991022 - Adapted for GINI ADDE 'ADIR' and 'AGET' servers
**             20001011 - Changed way 'word' is calculated; incorrect
**                        values were being generated under SC5.0 x86 C
**
*/

{

    unsigned char  octet[4];  /* <<<<< UPC mod 20080830 - 3 -> 4 >>>>> */

    int            base=1;
    int            i;
    int            word=0;

#if DEBUG_TEST
    (void) sprintf( dbg, "In GetInt:: num = %d", num );
    M0sxtrce( dbg );
#endif

    /*
    ** Make a local copy of the byte sequence
    */

    (void) memcpy( octet, ptr, num );

    /*
    ** Check MSBit: if set, the number is negative
    */

    if ( *octet > 127 ) {
      *octet -= 128;
      base = -1;
    }

    /*
    ** Calculate the integer value of the byte sequence
    */

    for ( i = num-1; i >= 0; i-- ) {
      word += base * octet[i];
      base *= 256;
#if DEBUG_TEST
      (void) sprintf( dbg, "GetInt:: octet = %d, word = %d", octet[i], word );
      M0sxtrce( dbg );
#endif
    }

    /*
    ** Done
    */

    return word;

}

/******************************* GiniToMcNav *********************************/

int
GiniToMcNav( unsigned char *header, int *navcod )

/*
** Name:       GiniToMcNav
**
** Purpose:    Convert GINI navigation McIDAS format
**
** Parameters: 
**             header  - GINI format PDB header (521 bytes)
**             mcdir   - McIDAS navigation codicil
**
** Reference:
**                      Interface Control Document (ICD)
**                                  for
**              AWIPS-National Environmental Satellite, Data and
**                        Information Service (NESDIS)
**
**                               AA0130008 CH-2
**                               August 1, 1999
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
** History:    ???????? - Create by SSEC for GINI to AREA file converter
**             19990927 - Adapted for GINI ADDE 'ADIR' and 'AGET' servers
**
*/

{

    int        proj;                        /* projection type indicator     */
                                            /* 1 - Mercator                  */
                                            /* 3 - Lambert Conf./Tangent Cone*/
                                            /* 5 - Polar Stereographic       */

    int        sat_id;                      /* GINI creation entity          */
    int        sec_id;                      /* GINI sector ID                */
    int        sss_id;                      /* GINI channel ID               */

    int        nx;
    int        ny;
    int        one=1;
    int        pole;

    double     lonv;                        /* meridian parallel to y-axis */
    double     mrad;                        /* map radius */
    double     dx, dy;
    double     imgres;
    double     xdist;
    double     ydist;
    double     diff_lon;
    double     image_scale;
    double     lat1, lat2;
    double     lon1, lon2;
    double     lat_s;                       /* McIDAS standard latitude      */
    double     lat_t;                       /* lat. where res. is 'imgres'   */
    double     colat_t;
    double     colat_1;
    double     Kexp;
    double     temp;
    double     lin_0;                       /* line of pole */
    double     ele_0;                       /* element of pole */
    double     lin_e;                       /* image line over equator */
    double     ele_e;                       /* image element over equator */

    float      rlon_v;
    float      rlat_s;
    float      rlon_c;

#if DEBUG_ENTRY
    M0sxtrce( "in GiniToMcNav" );
#endif

    sat_id = (int) *( header +  1 );
    sec_id = (int) *( header +  2 );
    sss_id = (int) *( header +  3 );
    proj   = (int) *( header + 15 );

#if DEBUG_MCNAV
    (void) sprintf( dbg, "GiniToMcNav:: creating entity= %d", sat_id );
    M0sxtrce( dbg );
    (void) sprintf( dbg, "GiniToMcNav:: sector id= %d", sec_id );
    M0sxtrce( dbg );
    (void) sprintf( dbg, "GiniToMcNav:: map projection id= %d", proj );
    M0sxtrce( dbg );
#endif

    /*
    ** Get grid dimensions
    */

    nx = GetInt( header + 16, 2 );
    ny = GetInt( header + 18, 2 );

    /*
    ** Get the image resolution.
    */

    imgres = (int) header[41];                                   /* Res [km] */

#if DEBUG_MCNAV
    (void) sprintf( dbg, "GiniToMcNav:: image resolution [km]: %f", imgres );
    M0sxtrce( dbg );

    (void) sprintf( dbg, "GiniToMcNav:: # points:: x-dir: %d  y-dir: %d",
                    nx, ny );
    M0sxtrce( dbg );
#endif

    switch( proj ) {

      case 1:                                                    /* Mercator */

#if DEBUG_MCNAV
        M0sxtrce( "GiniToMcNav:: MERC projection" );
#endif

        /*
        ** Get the latitude and longitude of first and last "grid" points
        */

        lat1 = ((double) GetInt( header+20, 3 )) / 10000.0;
        lon1 = ((double) GetInt( header+23, 3 )) / 10000.0;
        lon1 *= -1;

#if DEBUG_MCNAV
        (void) sprintf( dbg, "GiniToMcNav:: lat1: %f  lon1: %f", lat1, lon1 );
        M0sxtrce( dbg );
#endif

        lat2 = ((double) GetInt( header+27, 3 )) / 10000.0;
        lon2 = ((double) GetInt( header+30, 3 )) / 10000.0;
        lon2 *= -1;

#if DEBUG_MCNAV
        (void) sprintf( dbg, "GiniToMcNav:: lat2: %f  lon2: %f", lat2, lon2 );
        M0sxtrce( dbg );
#endif

        /*
        ** Hack to catch incorrect sign of lon2 in header.
        */

        if ( lon1 > 0.0 && lon2 < 0.0 ) lon2 *= -1;

        /*
        ** Get the "Latin" parameter.  The ICD describes this value as:
        ** "Latin - The latitude(s) at which the Mercator projection cylinder
        ** intersects the earth."  It should read that this is the latitude
        ** at which the image resolution is that defined by octet 41.
        */

        lat_t = ((double) GetInt( header+38, 3 )) / 10000.0;

#if DEBUG_MCNAV
        (void) sprintf( dbg, "GiniToMcNav:: lat_t: %f", lat_t );
        M0sxtrce( dbg );
#endif

        lat_s = 0.0;                        /*
                                            ** Experimentation has shown that
                                            ** the "standard latitude" for the
                                            ** McIDAS MERCator navigation must
                                            ** be zero.
                                            */


        /*
        ** Equation for the line at the equator is a result of reverse
        ** engineering the McIDAS MERC nav module, nvxmerc.dlm.  Apparently,
        ** this formulation allows for oblique Mercator projections.  The
        ** "standard latitude" called for in nvxmerc.dlm appears to be the
        ** rotation of the projection cylinder for oblique projections.
        */

        /*
        ** Calculate the spacing needed by the MERCator navigation module
        */

        dy  = EARTH_RAD_METERS * cos(DEG_TO_RAD*lat_s) / (ny - 1);
        dy *= ( log( tan(DEG_TO_RAD*( (lat2-lat_s)/2.0 + 45.0 ) ) )
               -log( tan(DEG_TO_RAD*( (lat1-lat_s)/2.0 + 45.0 ) ) ) );

#if DEBUG_MCNAV
        (void) sprintf( dbg, "GiniToMcNav:: spacing at standard latitude: %f",
                        dy );
        M0sxtrce( dbg );
#endif

        /*
        ** Compute the line and element of the equator
        */

        lin_e = ny + EARTH_RAD_METERS * cos(DEG_TO_RAD*lat_s) / dy *
                log( tan( DEG_TO_RAD*(45.0 + (lat1-lat_s)/2.0) ) );
        ele_e = (nx - 1) / 2.0;

#if DEBUG_MCNAV
        (void) sprintf( dbg, "GiniToMcNav:: lin_e: %f  ele_e: %f",
                        lin_e, ele_e );
        M0sxtrce( dbg );
#endif

#if 0
        dy = 1000.0 * imgres / cos( DEG_TO_RAD*lat_t);        /* spacing [m] */
        dx = DEG_TO_RAD * EARTH_RAD_METERS * (lon1-lon2) / (ny-1);
        lin_e = EARTH_RAD_METERS * cos(DEG_TO_RAD*lat_t) / dy *
                     log( tan( DEG_TO_RAD*(45.0 + lat2/2.0) ) ) - 42000./dy;
#endif

        /*
        ** Compute the longitude of the center of the image
        */

        if ( lon1 < 0 ) lon1 += 360.0;
        if ( lon2 < 0 ) lon2 += 360.0;
        
        rlon_c = lon1 - (lon1 - lon2) / 2.0;

        if ( rlon_c >  180.0 ) rlon_c -= 360.0;
        if ( rlon_c < -180.0 ) rlon_c += 360.0;

        /*
        ** Convert the standard latitude and normal longitude to reals
        ** for use in the Fortran LALO function.
        */

        rlat_s = lat_s;

        /*
        ** Set the values in the navigation block
        */

        navcod[0] = lit_( "MERC", 4 );      /* MERC navigation module        */
        navcod[1] = (int) imgres * lin_e;   /* image line of the equator     */
        navcod[2] = (int) imgres * ele_e;   /* image element of the equator  */
        navcod[3] = ilalo_( &rlat_s );      /* std. latitude [DDDMMSS]       */
        navcod[4] = (int) dy / imgres;      /* spacing at std. lat. [m]      */
        navcod[5] = ilalo_( &rlon_c );      /* normal longitude [DDDMMSS]    */
        navcod[6] = EARTH_RAD_METERS;       /* Earth radius [m]              */
        navcod[7] = EARTH_ECCENTRICITY;     /* eccentricity * 1000000        */
        navcod[8] = 0;                      /* coord type >=0 -> planetodetic*/
        navcod[9] = 0;                      /* long. conv. >=0 -> west pos.  */

        break;

      case 3:                               /* Tangent Cone                  */
      case 5:                               /* Polar Stereographic           */

#if DEBUG_MCNAV
        if ( proj == 3 ) {
          M0sxtrce( "GiniToMcNav:: TANC projection" );
        } else {
          M0sxtrce( "GiniToMcNav:: PS projection" );
        }
#endif

        /*
        ** Check high bit of octet for North or South projection center
        */

        pole = ( header[36] > 127 ) ? -1 : 1;

        /*
        ** Compute McIDAS line/element of projection pole.
        */

        /*
        ** Get lat/lon of first grid point
        */

        lat1 = ((double) GetInt( header+20, 3 )) / 10000.0;
        lon1 = ((double) GetInt( header+23, 3 )) / 10000.0;

#if DEBUG_MCNAV
        (void) sprintf( dbg, "GiniToMcNav:: 1st point:: lat: %f  lon: %f",
                        lat1, lon1 );
        M0sxtrce( dbg );
#endif

        /*
        ** Get distance increment of grid
        */

        dx = ((double) GetInt( header+30, 3 )) / 10.;
        dy = ((double) GetInt( header+33, 3 )) / 10.;

#if DEBUG_MCNAV
        (void) sprintf( dbg, "GiniToMcNav:: dist inc  x-dir: %f  y-dir: %f",
                        dx, dy );
        M0sxtrce( dbg );
#endif

        if ( proj == 5 ) {
          lat_t = 60.0;            /* Fixed for polar stereographic */
          image_scale = (1. + sin(DEG_TO_RAD*lat_t))/
                        (1. + sin(DEG_TO_RAD*lat1));
          mrad = EARTH_RAD_METERS * image_scale * cos(DEG_TO_RAD*lat1);

        }

        if ( proj == 3 ) {
          /**  Get tangent latitude **/
          lat_t = ((double) GetInt( header+38, 3 )) / 10000.0;
          colat_t = 90. - lat_t;
          colat_1 = 90. - lat1;
          Kexp = cos(DEG_TO_RAD*colat_t);
          temp = tan(DEG_TO_RAD*colat_1*0.5)/tan(DEG_TO_RAD*colat_t*0.5);
          mrad = EARTH_RAD_METERS * tan(DEG_TO_RAD*colat_t) * pow(temp,Kexp);

#if DEBUG_MCNAV
          (void) sprintf( dbg, "GiniToMcNav:: standard latitude  %f", lat_t );
          M0sxtrce( dbg );
          (void) sprintf( dbg, "GiniToMcNav:: colat_t %f colat_1 %f",
                          colat_t, colat_1 );
          M0sxtrce( dbg );
          (void) sprintf( dbg, "GiniToMcNav:: Kexp %f temp %f mrad %f",
                          Kexp, temp, mrad );
          M0sxtrce( dbg );
#endif
        }

        /*
        ** Get normal longitude
        */

        lonv = ((double) GetInt( header+27, 3 )) / 10000.0;

#if DEBUG_MCNAV
        (void) sprintf( dbg, "GiniToMcNav:: lon parallel to y-axis (lov): %f",
                        lonv );
        M0sxtrce( dbg );
#endif

        if ( sec_id == 10 ) {
#if DEBUG_MCNAV
          (void) sprintf( dbg, "GiniToMcNav:: change lov from %f to 255.0000",
                          lonv );
          M0sxtrce( dbg );
#endif
          lonv = 255.0;
        }

        /* Convert to east longitude */
        if ( lonv < 0. ) lonv += 360.;
        if ( lon1 < 0. ) lon1 += 360.;

        diff_lon = lonv - lon1;

        if ( diff_lon >  180. )  diff_lon -= 360.;
        if ( diff_lon < -180. )  diff_lon += 360.;

#if DEBUG_MCNAV
        (void) sprintf( dbg, "GiniToMcNav:: diff_lon %f", diff_lon );
        M0sxtrce( dbg );
#endif

        if ( diff_lon > 0 ) {                  /*  grid origin west of lonv  */

          if ( proj == 3 ) diff_lon = Kexp*diff_lon;       /*  Tangent Cone  */

          xdist = mrad * sin(DEG_TO_RAD*diff_lon);
          ydist = mrad * cos(DEG_TO_RAD*diff_lon);

          ele_0 = 1 + xdist/dx;
          lin_0 = 1 - (ydist/dy - ny);

#if DEBUG_MCNAV
          (void) sprintf( dbg, "GiniToMcNav:: x %f %f %f",
                          xdist, dx, ele_0 );
          M0sxtrce( dbg );
          (void) sprintf( dbg, "GiniToMcNav:: y %f %f %f",
                          ydist, dy, lin_0 );
          M0sxtrce( dbg );
#endif

        }

        if ( diff_lon < 0 ) {                   /* grid origin east of lonv  */

          /**  This section is incomplete  **/

          xdist = mrad*sin(-DEG_TO_RAD*diff_lon);
          ydist = mrad*cos(-DEG_TO_RAD*diff_lon);

          ele_0 = 1 + xdist/dx;
          lin_0 = 1 - (ydist/dy - ny);
#if DEBUG_MCNAV
          (void) sprintf( dbg, "GiniToMcNav:: lin,ele %f %f",lin_0,ele_0 );
          M0sxtrce( dbg );
#endif
        }

        /*
        ** Convert to normal longitude to McIDAS convention
        */

        lonv = (lonv > 180.) ? (360.-lonv) : -lonv;

#if DEBUG_MCNAV
        (void) sprintf( dbg, "GiniToMcNav:: lonv %f",lonv );
        M0sxtrce( dbg );
#endif

        rlon_v = lonv;
        rlat_s = lat_t;

        if ( proj == 3 ) {
          navcod[ 0] = lit_( "TANC", 4 );             /* Tangent Cone        */
          navcod[ 1] = (int) imgres * (lin_0*10000);  /* LIN of pole *10000  */
          navcod[ 2] = (int) imgres * (ele_0*10000);  /* ELE of pole *10000  */
          navcod[ 3] = dx * 10. / imgres;             /* km/pixel    *10000  */
          navcod[ 4] = (int) (lat_t * 10000);         /* Std. lat.   *10000  */
          navcod[ 5] = (int) (lonv * 10000);          /* Std. lon.   *10000  */

        } else {
          navcod[ 0] = lit_( "PS  ", 4 );             /* PS -> Polar Stereo. */
          navcod[ 1] = (int) imgres * lin_0;          /* image LIN of pole   */
          navcod[ 2] = (int) imgres * ele_0;          /* image ELE of pole   */
          navcod[ 3] = ilalo_( &rlat_s );             /* Std. lat [DDDMMSS]  */
          navcod[ 4] = dx / imgres;                   /* Space @ std lat [m] */
          navcod[ 5] = ilalo_( &rlon_v );             /* Std. lon [DDDMMSS]  */
          navcod[ 6] = EARTH_RAD_METERS;              /* Earth Radius [m]    */
          navcod[ 7] = EARTH_ECCENTRICITY;            /* Eccentricity*1000000*/
          navcod[ 8] = 0;                             /* Planetodetic        */
          navcod[ 9] = 0;                             /* West is positive    */
          navcod[10] = pole * 900000;                 /* Pole proj. lat.     */

        }

        break;

      default:

        return FAILURE;

    }

     /*
     ** Done
     */

     return SUCCESS;

}

/******************************* GiniToMcDir *********************************/

int
GiniToMcDir( unsigned char *header, int *mcdir )

/*
** Name:       GiniToMcDir
**
** Purpose:    Convert a GINI file header to a McIDAS AREA directory
**
** Parameters: 
**             header  - GINI format PDB header (521 bytes)
**             mcdir   - McIDAS AREA directory
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
** History:    ???????? - Create by SSEC for GINI to AREA file converter
**             19990927 - Adapted for GINI ADDE 'ADIR' and 'AGET' servers
**
*/

{
    char   cmemo[80];                       /* AREA directory memo field     */

    int    ccyyddd;                         /* Julian date [CCYYDDD]         */
    int    time;                            /* Time [HHMMSS]                 */

    int    year;                            /* Year [YY]                     */
    int    mon;                             /* Month of Year                 */
    int    day;                             /* Day of Month                  */
    int    hour;                            /* Hour of Day                   */
    int    min;                             /* Minute of Hour                */
    int    sec;                             /* Second of Minute              */

    int    rc;                              /* Function return code          */
    int    sat_id;                          /* GINI creation entity          */
    int    sec_id;                          /* GINI sector ID                */
    int    sss_id;                          /* GINI channel ID               */

    static int one=1;

#if DEBUG_ENTRY
    M0sxtrce( "in GiniToMcDir" );
#endif

    /*
    ** Set some AREA directory constant values
    */

    mcdir[0] = 0;                               /* relative ADDE dset pos    */
    mcdir[1] = 4;                               /* always 4                  */

    /*
    ** Create a sensor source number based on creation entity and channel ID
    **
    ** GINI Physical Element/Channel ID
    **
    **                  1  0.65 micron VIS
    **                  2   3.9 micron Short IR
    **                  3   6.7 micron WV
    **                      6.5 micron WV
    **                  4  10.7 micron Thermal IR
    **                  5  12.0 micron Long IR
    **                  6  13.3 micron CO2
    **                  7   1.3 micron IR
    **                  8  Reserved
    **                     ...
    **                 12  Reserved
    **                 20  Derived products
    **                     ...
    **                 25  Derived products
    **                 40  Reserved
    **                 41  14.71 micron sounder image
    **                 42  14.37 micron sounder image
    **                 43  14.06 micron sounder image
    **                 44  13.64 micron sounder image
    **                 45  13.37 micron sounder image
    **                 46  12.66 micron sounder image
    **                 47  12.02 micron sounder image
    **                 48  11.03 micron sounder image
    **                 49   9.71 micron sounder image
    **                 50   7.43 micron sounder image
    **                 51   7.02 micron sounder image
    **                 52   6.51 micron sounder image
    **                 53   4.57 micron sounder image
    **                 54   4.52 micron sounder image
    **                 55   4.45 micron sounder image
    **                 56   4.13 micron sounder image
    **                 57   3.98 micron sounder image
    **                 58   3.75 micron sounder image
    **                 59  visible sounder image
    **
    **                 Unidata NEXRAD Level III Composite Images (sat_id=99)
    **
    **                 25  2 km National N0Z 248 nm Base Composite Reflectivity
    **                 26  4 km National NET Echo Tops
    **                 27  1 km National N0R Base Reflectivity Composite
    **                 28  1 km National NCR Composite Reflectivity
    **                 29  4 km National NVL Vertically Integrated Liquid Water
    **                 30  2 km National N1P 1-hour Precipitation
    **                 31  4 km National NTP Storm Total Precipitation
    **                 32  1 km National N0Q Base Reflectivity Composite
    */

    sat_id = (int) *( header + 1 );              /* GINI creation entity     */
    sec_id = (int) *( header + 2 );              /* GINI sector ID           */
    sss_id = (int) *( header + 3 );              /* GINI channel ID          */

    (void) sprintf( cmemo, "GINI:: Sat %2d  SecID %2d  ChID %2d",
                    sat_id, sec_id, sss_id );

#if DEBUG_MCDIR
    (void) sprintf( dbg, "GiniToMcDir:: %s", cmemo );
    M0sxtrce( dbg );
#endif

    switch ( sat_id ) {

      case 6:                                    /* Composite                */
        mcdir[2] = 10;
        break;

      case 7:                                    /* DMSP                     */
        mcdir[2] = 94;
        mcdir[56] = lit_( "DMSP", 4 );
        break;

      case 8:                                    /* GMS                      */
        mcdir[2] = 12;
        mcdir[56] = lit_( "GMS ", 4 );
        break;

      case 9:                                    /* METEOSAT (using 6)       */
        mcdir[2] = 56;
        mcdir[56] = lit_( "MSAT", 4 );
        break;

      case 10:                                   /* GOES-7                   */
        if ( sss_id == 1 )
          mcdir[2] = 32;
        else
          mcdir[2] = 33;

        mcdir[56] = lit_( "GOES", 4 );
        break;

      case 11:                                   /* GOES-8                   */
      case 12:                                   /* GOES-9                   */
      case 13:                                   /* GOES-10                  */
      case 14:                                   /* GOES-11                  */
      case 15:                                   /* GOES-12                  */

        if      ( (sss_id >=  1) && (sss_id <=  7) )

          mcdir[2] = 70 + 2*(sat_id-11);

        else if ( (sss_id >= 13) && (sss_id <= 18) ) 

          mcdir[2] = 0;

        else if ( (sss_id >= 41) && (sss_id <= 59) ) 

          mcdir[2] = 71 + 2*(sat_id-11);

        mcdir[56] = lit_( "GVAR", 4 );
        break;

      case 16:                                   /* GOES-13                  */
      case 17:                                   /* GOES-14 (20051011 guess) */
      case 18:                                   /* GOES-15 (20051011 guess) */

        if      ( (sss_id >=  1) && (sss_id <=  7) )

          mcdir[2] = 180 + 2*(sat_id-16);

        else if ( (sss_id >= 13) && (sss_id <= 18) ) 

          mcdir[2] = 0;

        else if ( (sss_id >= 41) && (sss_id <= 59) ) 

          mcdir[2] = 181 + 2*(sat_id-16);

        mcdir[56] = lit_( "GVAR", 4 );
        break;

      case 99:                         /* Unidata NEXRAD Level III Composite */
        mcdir[2] = 7;
        break;

    }

    /*
    ** Calculate the date from the Year, Month, and Day
    */

    year = (int) *( header + 8 );
    year += (year > 70) ? 1900 : 2000;
    mon  = (int) *( header + 9 );
    day  = (int) *( header +10 );

#if DEBUG_MCDIR
    (void) sprintf( dbg, "GiniToMcDir:: year: %d  mon: %d  day: %d",
                    year, mon, day );
    M0sxtrce( dbg );
#endif

    rc = Mcdmytocyd( day, mon, year, &ccyyddd );
 
    mcdir[3] = ccyyddd;                         /* nominal date [CCYYDDD]    */

    /*
    ** Calculate the Time
    */

    hour = (int) *( header + 11 );
    min  = (int) *( header + 12 );
    sec  = (int) *( header + 13 );

    time = 10000*hour + 100*min + sec;

    mcdir[4] = time;                             /* nominal time [HHMMSS]    */
    mcdir[5] = 1;                                /* upper left image line    */
    mcdir[6] = 1;                                /* upper left image element */

    /*
    ** Get the number of lines and elements in the image
    */

    mcdir[9] = GetInt( header + 16, 2 );         /* Elements                 */
    mcdir[8] = GetInt( header + 18, 2 );         /* Lines                    */

    /*
    ** Set some more AREA header values
    */

    mcdir[10] = 1;                               /* # bytes per data point   */
    mcdir[11] = (int) *( header + 41 );          /* line resolution          */
    mcdir[12] = (int) *( header + 41 );          /* element resolution       */
    mcdir[13] = 1;                               /* # spectral bands         */
    mcdir[14] = 0;                               /* length of line prefix    */
    mcdir[15] = 0;                               /* SSEC creation project #  */

    if ( sss_id < 33 ) {                         /* Spectral band map        */
      mcdir[18] = 1 << (sss_id-1);
    } else {
      mcdir[18] = 0;
      mcdir[19] = 1 << (sss_id-32-1);
    }

    (void) strncpy( (char *) &mcdir[24], cmemo, 32 );
 
    mcdir[33] = 4 * (IMG_DIR_LEN + NAV_COD_LEN); /* offset to start data     */
    mcdir[34] = 4 * IMG_DIR_LEN;                 /* offset to start of nav   */
        
    mcdir[45] = ccyyddd - 1900000;               /* start date [CCCDDD]      */
    mcdir[46] = mcdir[4];                        /* start time [HHMMSS]      */

    mcdir[51] = lit_( "VISR", 4 );               /* image source             */
    mcdir[52] = lit_( "BRIT", 4 );               /* calibration type         */

    mcdir[62] = 0;                               /* offset to start of cal   */

    return SUCCESS;

}

/******************************* GiniToMcCal *********************************/

int
GiniToMcCal( unsigned char *header, int *calcod )

/*
** Name:       GiniToMcCal
**
** Purpose:    Convert GINI calibration to McIDAS format
**
** Parameters: 
**             header  - GINI format PDB header (521 bytes)
**             calcod  - McIDAS navigation codicil
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
** History:    20020418 - Created to handle Unidata calibration blocks
**
*/

{

    char                unit[9];            /* unit listed in GINI Cal       */

    int                 iname;              /* name for Cal block            */
    int                 iunit;              /* unit for Cal block            */

    int                 i, j;               /* loop index                    */
    int                 band_id;            /* band number                   */
    int                 creator;            /* create entity                 */
    int                 scale=10000;        /* Cal scale value               */
    int                 idscal;             /*                               */
    int                 jscale=100000000;   /*                               */

    int                 dbz;                /* echo strength [dbZ]           */
    int dvip[]={-1, 30, 60, 70, 75, 80, 85};/* DVIP break points             */
    int                 navcal;             /* Nav/Cal indicator             */
    int                 noctet;             /* # octets in PDB               */
    int                 version;            /* PDB version for create entity */

    int                 minb;               /* min brightness values         */
    int                 maxb;               /* max brightness values         */
    int                 mind;               /* min data values               */
    int                 maxd;               /* max data values               */
    int                 temp;               /* temporary storage             */

#if DEBUG_ENTRY
    M0sxtrce( "in GiniToMcCal" );
#endif

    /*
    ** Extract information from the header (values are 1-based):
    **
    **         octet(s)  meaning
    **         ---------+---------------------------------------------
    **            43     Data compression indicator
    **            44     Version number for PDB for entity in octet #2
    **          45-46    Number of octets in the PDB
    **            47     Navigation/Calibration indicator:
    **                     0 -> Nav/Cal info not included (typical)
    **                     1 -> Nav/Cal info both included
    **                     2 -> Nav info only included
    **                     3 -> Cal info only included
    **          48-512   Blanks unless Nav or Cal included
    **          48-51    unit of calibration (8 characters)
    **            52     Number of calibration segments
    **          53-56    minimum display brightness
    **          57-60    maximum display brightness
    */

    creator = (int) *( header + 1 );         /* <<<<< UPC add 20051007 >>>>> */
    band_id = (int) *( header + 3 );

#if DEBUG_MCCAL
    (void) sprintf( dbg, "GiniToMcCal:: create id: %d, band id: %d",
                    creator, band_id );
    M0sxtrce( dbg );
#endif

    version  = header[43];
    noctet   = GetInt( header + 44, 2 );
    navcal   = header[46];

#if DEBUG_MCCAL
    (void) sprintf( dbg, "GiniToMcCal:: version: %d  noctet: %d  nvcal: %d",
                    version, noctet, navcal );
    M0sxtrce( dbg );
#endif

    if ( navcal == 128 ) {         /* Unidata Cal block found; unpack values */

      (void) memcpy( unit, (char *)(header+47), 8 );
      unit[8] = '\0';
      (void) Mcupcase( unit );

      calcod[0] = header[55];

#if DEBUG_MCCAL
      (void) sprintf( dbg, "GiniToMcCal:: Cal unit = %s  #image Cal segs = %d",
                      unit, calcod[0] );
      M0sxtrce( dbg );
#endif

      if        ( !strncmp( unit, "INCH", 4 ) ) {

        iname = lit_( "RAIN", 4 );
        iunit = lit_( "IN  ", 4 );

      } else if ( !strncmp( unit, "DBZ", 3 ) ) {

        iname = lit_( "ECHO", 4 );
        iunit = lit_( "DBZ ", 4 );

      } else if ( !strncmp( unit, "KFT", 3 ) ) {

        iname = lit_( "TOPS", 4 );
        iunit = lit_( "KFT ", 4 );

      } else if ( !strncmp( unit, "KG/M", 4 ) ) {

        iname = lit_( "VIL ", 4 );
        iunit = lit_( "mm  ", 4 );

      } else {

        iname = lit_( "    ", 4 );
        iunit = lit_( "    ", 4 );

      }

      if ( calcod[0] > 0 ) {

        for ( i = 0; i < calcod[0]; i++ ) {

          minb = GetInt( header + 56 + i*16, 4 ) / 10000;
          maxb = GetInt( header + 60 + i*16, 4 ) / 10000;
          mind = GetInt( header + 64 + i*16, 4 );
          maxd = GetInt( header + 68 + i*16, 4 );

          idscal = 1;
          while ( !(mind % idscal) && !(maxd % idscal) ) {
            idscal *= 10;
          }
          idscal /= 10;

          if ( idscal < jscale ) jscale = idscal;

#if DEBUG_MCCAL
          (void) sprintf( dbg, "GiniToMcCal:: minB: %d maxB: %d minD: %d maxD: 
%d idscal: %d jscale: %d",
                          minb, maxb, mind, maxd, idscal, jscale );
          M0sxtrce( dbg );
#endif

          calcod[1+i*8] = iname;
          calcod[2+i*8] = mind;
          calcod[3+i*8] = maxd;
          calcod[4+i*8] = minb;
          calcod[5+i*8] = maxb;
          calcod[6+i*8] = iunit;
          calcod[7+i*8] = 0;

        }

        if ( jscale > scale ) jscale = scale;
        scale /= jscale;

        /* <<<<< UPC mod 20101122 - allow fractional dBZs >>>>> */
        if ( !strncmp( (char *)&iname, "ECHO", 4 ) ) {
          if ( scale < 10 ) {
            jscale /= (10/scale);
            scale   = 10;
          }
        }

        if ( !strncmp( (char *)&iname, "RAIN", 4 ) ) {
          if ( scale < 100 ) {
            jscale /= (100/scale);
            scale   = 100;
          }
        }

        for ( i = 0; i < calcod[0]; i++ ) {
          calcod[2+i*8] /= jscale;
          calcod[3+i*8] /= jscale;
          calcod[7+i*8]  = scale;

#if DEBUG_MCCAL
          (void) sprintf( dbg, "GiniToMcCal:: minB: %d maxB: %d minD: %d maxD: 
%d jscale: %d scale: %d",
                          calcod[4+i*8], calcod[5+i*8],
                          calcod[2+i*8], calcod[3+i*8], jscale, scale );
          M0sxtrce( dbg );
#endif
        }

      } else {

        return FAILURE;

      }

      if ( !strncmp( (char *)&iname, "ECHO", 4 ) ) {

          /*
          ** Add Cal block for VIP
          */
 
          for ( i = 1; i < 7; i++ ) {

            j = 8 * calcod[0];
            calcod[1+j] = lit_( "VIP ", 4 );
            calcod[2+j] = i-1;
            calcod[3+j] = i-1;
            calcod[4+j] = dvip[i-1]+1;
            calcod[5+j] = dvip[i];
            calcod[6+j] = lit_( "VIP ", 4 );
            calcod[7+j] = 1;
            calcod[8+j] = 0;
            calcod[0]++;
          }

      }

#if 0
        /* <<<<< 20051010 UPC removed - this was under else block even though
                                        the code is clearly cal for Chiz's radar
                                        mosaics */
        case 26:                              /* Cal block for NET composite */

#if DEBUG_MCCAL
          M0sxtrce( "GiniToMcCal:: GINI Echo Tops" );
#endif
          calcod[ 0] = 1;
          calcod[ 1] = lit_( "TOPS", 4 );
          calcod[ 2] = 0;
          calcod[ 3] = 80;
          calcod[ 4] = 0;
          calcod[ 5] = 255;
          calcod[ 6] = lit_( "K FT", 4 );
          calcod[ 7] = 1;
          calcod[ 8] = 0;

          break;

        case 27:                              /* Cal block for N0R composite */
        case 28:                              /* Cal block for NCR composite */

#if DEBUG_MCCAL
          M0sxtrce( "GiniToMcCal:: GINI Reflectivity" );
#endif
          calcod[ 0] = 1;
          calcod[ 1] = lit_( "ECHO", 4 );
          calcod[ 2] = 0;
          calcod[ 3] = 105;
          calcod[ 4] = 0;
          calcod[ 5] = 255;
          calcod[ 6] = lit_( "dBZ ", 4 );
          calcod[ 7] = 1;
          calcod[ 8] = 30;

          break;

        case 29:                              /* AUX block for NVL composite */

#if DEBUG_MCCAL
          M0sxtrce( "GiniToMcCal:: GINI Vert. Int. Liq. H2O" );
#endif
          calcod[ 0] = 1;
          calcod[ 1] = lit_( "H2O ", 4 );
          calcod[ 2] = 0;
          calcod[ 3] = 85;
          calcod[ 4] = 0;
          calcod[ 5] = 255;
          calcod[ 6] = lit_( "mm  ", 4 );
          calcod[ 7] = 1;
          calcod[ 8] = 0;

          break;

        case 30:                              /* AUX block for N1P composite */

#if DEBUG_MCCAL
          M0sxtrce( "GiniToMcCal:: GINI 1-hour Precip." );
#endif
          calcod[ 0] = 1;
          calcod[ 1] = lit_( "RAIN", 4 );
          calcod[ 2] = 0;
          calcod[ 3] = 120;
          calcod[ 4] = 0;
          calcod[ 5] = 255;
          calcod[ 6] = lit_( "IN  ", 4 );
          calcod[ 7] = 10;
          calcod[ 8] = 0;

          break;

        case 31:                              /* AUX block for NTP composite */

#if DEBUG_MCCAL
          M0sxtrce( "GiniToMcCal:: GINI Story Total Precip." );
#endif
          calcod[ 0] = 1;
          calcod[ 1] = lit_( "RAIN", 4 );
          calcod[ 2] = 0;
          calcod[ 3] = 240;
          calcod[ 4] = 0;
          calcod[ 5] = 255;
          calcod[ 6] = lit_( "IN  ", 4 );
          calcod[ 7] = 10;
          calcod[ 8] = 0;

          break;
#endif


    } else {

      switch ( band_id ) {             /* CAL block/AUX block depend on band */

        case 0:                                  /* No CAL/AUX block for VIS */
#if DEBUG_MCCAL
          M0sxtrce( "GiniToMcCal:: GINI VIS band" );
#endif
          calcod[ 0] = 0;
          break;

        case 2:                                  /* CAL block for IR bands   */
        case 3:
        case 4:
        case 5:
        case 6:                              /* <<<<< UPC add 20051007 >>>>> */
        case 7:                              /* <<<<< UPC add 20051007 >>>>> */

          if ( band_id != 3 ) {                  /* 3.9, 10.7, 12.0 um IR    */

#if DEBUG_MCCAL
            M0sxtrce( "GiniToMcCal:: GINI IR band" );
#endif
            calcod[ 0] = 2;
            calcod[ 1] = lit_( "TEMP", 4 );
            calcod[ 2] = 2420;
            calcod[ 3] = 1630;
            calcod[ 4] = 176;
            calcod[ 5] = 255;
            calcod[ 6] = lit_( "K   ", 4 );
            calcod[ 7] = 10;
            calcod[ 8] = 0;
            calcod[ 9] = lit_( "TEMP", 4 );
            calcod[10] = 3300;
            calcod[11] = 2420;
            calcod[12] = 0;
            calcod[13] = 176;
            calcod[14] = lit_( "K   ", 4);
            calcod[15] = 10;
            calcod[16] = 0;

          } else {                               /* 6.8 um IR (WV)           */

#if DEBUG_MCCAL
            M0sxtrce( "GiniToMcCal:: GINI WV band" );
#endif
            calcod[ 0] = 1;
            calcod[ 1] = lit_( "TEMP", 4 );
            calcod[ 2] = 2630;
            calcod[ 3] = 2130;
            calcod[ 4] = 0;
            calcod[ 5] = 255;
            calcod[ 6] = lit_( "K   ", 4 );
            calcod[ 7] = 10;
            calcod[ 8] = 0;

          }

          break;

        default:

          return FAILURE;

      }

    }

    /*
    ** Regularize values in Cal block:
    **
    **        calcod[ 0] == number of cal segments
    **        calcod[ 1] == name of 1st cal segment
    **        calcod[ 2] == minD <-> minB in 1st cal segment
    **        calcod[ 3] == maxD <-> maxB in 1st cal segment
    **        calcod[ 4] == minB in 1st cal segment
    **        calcod[ 5] == maxB in 1st cal segment
    **        calcod[ 6] == unit represented in 1st cal segment 
    **        calcod[ 7] == scale factor for minD/maxD in 1st cal segment
    **        calcod[ 8] == offset for minD/maxD in 1st cal segment
    **
    **        calcld[ 9] == name of 2st cal segment
    **        calcod[10] == minD <-> minB in 2nd cal seg 2
    **          ...
    */

    for ( i = 0; i < calcod[0]; i++ ) {

      if ( calcod[4+(i*8)] > calcod[5+(i*8)] ) {

        temp = calcod[2+(i*8)];
        calcod[2+(i*8)] = calcod[3+(i*8)];
        calcod[3+(i*8)] = temp;
        temp = calcod[4+(i*8)];
        calcod[4+(i*8)] = calcod[5+(i*8)];
        calcod[5+(i*8)] = temp;

      }

    }

    /*
    ** GINI imagery don't have a RAW -> BRIT calibration block; add one.
    */

    maxb = -9999;
    for ( i = 0; i < calcod[0]; i++ ) {
      if ( calcod[4+(i*8)] > maxb ) maxb = calcod[4+(i*8)];
      if ( calcod[5+(i*8)] > maxb ) maxb = calcod[5+(i*8)];
    }

    i = 8 * calcod[0];
    calcod[1+i] = lit_( "BRIT", 4 );
    calcod[2+i] = 0;                         /* NB: this may look incorrect, */
    calcod[3+i] = 255;                       /*     but it isn't             */
    calcod[4+i] = 0;
    calcod[5+i] = maxb;
    calcod[6+i] = lit_( "BRIT", 4 );
    calcod[7+i] = 1;
    calcod[8+i] = 0;

    calcod[0]++;

    /*
    ** Done
    */

#if DEBUG_MCCAL
    i = 0;
    (void) sprintf( dbg, "GiniToMcCal:: calcod[%2d] = %6d  %4X", i, calcod[i], 
calcod[i] );
    M0sxtrce( dbg );

    for ( i = 1; i <= 8*calcod[0]; i++ ) {
      if ( ischar_( &calcod[i] ) ) {
        (void) memcpy( unit, (char *)&calcod[i], 4 );
        unit[4] = 0;
        (void) sprintf( dbg, "GiniToMcCal:: calcod[%2d] = %6s  %04x",
                        i, unit, calcod[i] );
      } else {
        (void) sprintf( dbg, "GiniToMcCal:: calcod[%2d] = %6d  %04x",
                        i, calcod[i], calcod[i] );
      }
      M0sxtrce( dbg );
    }
#endif

    return SUCCESS;

}

/****************************** GetGiniHeader ********************************/

int
GetGiniHeader( FILELIST *cur, unsigned char *header )

/*
** Name:       GetGiniHeader
**
** Purpose:    Read the GINI image header.  This skips the Product
**             Identification Block (PIB) which usually comes first.
**
** Parameters: 
**             cur     - FILELIST structure containing current image information
**                       name  - full qualified name of the file
**                       pos   - position to add to list
**                       hoff  - byte offset to beginning of image header
**                       doff  - byte offset to beginning of image data
**                       size  - number of lines in Zlib compressed block
**                       type  - file type
**                       time  - file data time [sec since 1970]
**             header  - returned image header
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
** History:    20000321 - Create by GINI to AREA file converter
**
*/

{
    char      buf[GINI_ZBUF_LEN];             /* working buffer              */
    char      head[GINI_HED_LEN+1];           /* working buffer              */
    char     *b;                              /* generic pointer             */
    char     *p;                              /* generic pointer             */

    int       rc;                             /* function return code        */

    int       zlibed=0;                       /* indicates zlib compression  */

    FILE     *fd;                             /* file descriptor             */


#if DEBUG_ENTRY
    M0sxtrce( "in GetGiniHeader" );
#endif

    /*
    ** Initialize
    */

    cur->hoff = 0;

    /*
    ** Read beginning of the file to see whether or not the PIB is present.
    **
    **        Bytes       Meaning
    **        -----------+------------------------------------
    **         0  - n     Product Identification Block
    **        n+1 - n+512 Product Definition Block
    **
    ** File types:
    **
    **        type  Origin
    **        ----  ----------------------------------------------
    **         0    NOAAPORT uncompressed; UPC uncompressed
    **         1    NOAAPORT Zlib compressed
    **         2    Unidata PNG compressed (uncompressible with pngg2gini)
    */

    fd = fopen ( cur->name, "rb" );

    if ( fd == (FILE *) NULL ) {
      return FAILURE;
    }

    rc = fread ( buf, (size_t) 1, sizeof(buf), fd );
    (void) fclose( fd );

    if ( rc != (int) sizeof(buf) ) {
#if DEBUG_HEADR
      (void) sprintf( dbg,"GetGiniHeader:: GINI read wanted: %d  got: %d",
                      sizeof(buf), rc);
      M0sxtrce( dbg );
#endif
      return FAILURE;
    }

    /*
    ** "For all NESDIS interface products the CCCC (originating station
    ** identifier) that follows the T1T2A1A2ii will be 'KNES' indicating
    ** that the product originated from the NESDIS interface in Camp
    ** Springs, Maryland."
    **
    ** For NEXRAD Level III composites created by the UPC, CCCC is set to
    ** 'CHIZ'.
    **
    ** If 'KNES'/'CHIZ' is included in the header, then the Product
    ** Identification Block has a non-zero length.  If it does not appear,
    ** then the PIB is assumed to not be present.
    */

    buf[sizeof(buf)-1] = 0;                /* NULL terminate                 */

    p = strstr( buf, "KNES" );
    if ( p == (char *) NULL ) p = strstr( buf, "CHIZ" );

    if ( p != (char *) NULL ) {            /* 'KNES' or 'CHIZ' found         */
      p = strstr( p, "\r\r\n" );           /* <<<<< UPC mod 20030710 >>>>>   */
      if ( p != (char *) NULL ) {          /* CR CR NL not found             */
        p        += 3;
        cur->hoff = p - buf;
      }
    } else {
      p = buf;
    }

    cur->doff = cur->hoff + GINI_PDB_LEN;  /* beginning of data              */

    if ( IsZlibHed( p ) ) {                /* <<<<< UPC add 20030710 >>>>>   */

      z_stream  d_stream;                  /* Zlib decompression stream      */

      cur->type = 1;                       /* Zlib compressed - type 1       */
      M0sxtrce( "GetGiniHeader:: Zlib-compressed GINI block found" );

      d_stream.zalloc    = (alloc_func) 0;
      d_stream.zfree     = (free_func) 0;
      d_stream.opaque    = (voidpf) 0;
      d_stream.next_in   = (Bytef *) p;
      d_stream.avail_in  = (uInt) (sizeof(buf)-1-cur->hoff);
      d_stream.next_out  = head;
      d_stream.avail_out = (uInt) sizeof(head);

      if ( inflateInit( &d_stream ) != Z_OK ) {
        M0sxtrce( "GetGiniHeader:: Zlib inflateInit error" );
        return FAILURE;
      }

      rc = inflate( &d_stream, Z_NO_FLUSH );
      if ( rc != Z_OK && rc != Z_STREAM_END ) {
        M0sxtrce( "GetGiniHeader:: Zlib inflate error" );
        return FAILURE;
      }

      if ( d_stream.total_out != GINI_HED_LEN ) {
        M0sxtrce( "GetGiniHeader:: Zlib inflated image header size error" );
        return FAILURE;
      }

      if ( inflateEnd( &d_stream ) != Z_OK ) {
        M0sxtrce( "GetGiniHeader:: Zlib inflateEnd error" );
        return FAILURE;
      }

      head[sizeof(head)-1] = '\0';

      p = strstr( head, "KNES" );
      if ( p == (char *) NULL ) p = strstr( head, "CHIZ" );
  
      if ( p != (char *) NULL ) {            /* 'KNES' or 'CHIZ' found       */
        p = strstr( p, "\r\r\n" ); 
        if ( p != (char *) NULL ) {          /* CR CR NL not found           */
          p += 3;
        }
      } else {
        p = head;
      }

      cur->doff = cur->hoff + d_stream.total_in;     /* beginning of data    */

#if UNIDATA
    } else if ( ispnghed_((unsigned char *)p+GINI_PDB_LEN) ) {/* <<<<< UPC add 
20070121 >>>>> */

      cur->type = 2;                       /* PNG compressed - type 2        */
      M0sxtrce( "GetGiniHeader:: PNG-compressed GINI block found" );

#endif
    } else {

      cur->type = 0;                       /* Uncompressed - type 0          */
      M0sxtrce( "GetGiniHeader:: uncompressed GINI block found" );

    }

    (void) memmove( header, p, GINI_PDB_LEN );

#if DEBUG_HEADR
    (void) sprintf( dbg, "GetGiniHeader:: cur->hoff: %d", cur->hoff );
    M0sxtrce( dbg );
#endif

    return SUCCESS;
}

/******************************** IsZlibHed **********************************/

int
IsZlibHed( unsigned char *buf )

/*
** Name:       IsZlibHed
**
** Purpose:    Check a two-byte sequence to see if it indicates the start of
**             a zlib-compressed buffer
**
** Parameters:
**             buf     - buffer containing at least two bytes
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
*/

{

#if DEBUG
    M0sxtrce( "in IsZlibHed" );
#endif

    unsigned char b0 = *buf;
    unsigned char b1 = *(buf+1);

    /*
    ** These tests were extracted from the 'inflate.c' routine that is
    ** distributed with the zlib distribution.
    */

    if ( (b0 & 0xf) == Z_DEFLATED ) {
      if ( (b0 >> 4) + 8 <= DEF_WBITS ) {
        if ( !(((b0 << 8) + b1) % 31) ) {
          return SUCCESS;
        }
      }
    }

    return FAILURE;

}

Attachment: giniadir.cp
Description: Binary data

Attachment: giniaget.cp
Description: Binary data

/**** $Id: servutil.c,v 1.11 2010/08/30 17:35:42 tomy Tst $ ****/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
/*
 * This is part of a rotten hack to get around a GNUC compiler bug on
 * IRIX when passing small structures to functions (check the call to
 * "inet_ntoa" later in the source code for more information).
 */
#if defined(__sgi__) && defined(__GNUC__) && \
((_MIPS_SIM == _ABIN32) || (_MIPS_SIM == _ABI64))
extern in_addr_t        inet_addr(const char *);
extern char *           inet_ntoa();
#else
#include <arpa/inet.h>
#endif
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#include <sys/stat.h>
#include <glob.h>
#include <regex.h>
#include "mcidas.h"
#include "mcidasp.h"
#include "servutil.h"

/* <<<<< UPC add 20081202 - add needed function prototypes >>>>> */
Fint lit_( const char *, FsLen );

int LineRes = 0;
int ElemRes = 0;

static char dbg[MAX_ERR_LEN];    /* debug message */

/******************************** GetFileList ********************************/

int
GetFileList( CRITERIA *request, FILELIST **list )

/*
** Name:       GetFileList
**
** Purpose:    Search directory 'dir' for files matching 'mask'
**
** Parameters: 
**             dir     - directory to search
**             request - criteria file has to match to be acceptable
**             list    - list of files matching 'mask'
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
*/

{
    int         i;                          /* generic counter               */
    int         len;                        /* length of string variable     */
    int         rc;                         /* status return flag            */
    int         indxl=-1;                   /* replacable location in string */
    int         indxu=-1;                   /* replacable location in string */

    char       *filename;                   /* file mask used for glob       */
    char        id[MAX_ID_LEN];             /* station ID                    */
    char        ptype[MAX_ID_LEN];          /* dataset descriptor            */
    char       *p;                          /* generic character pointer     */

    /* <<<<< UPC add 20051005 - Add date holders >>>>> */
    char        date[9];                    /* CCYYMMDD  [UTC]               */
    char        jdate[8];                   /* CCYYJJJ   [UTC]               */
    char        hour[3];                    /* HH                            */
    char        minute[3];                  /* MM                            */
    char        second[3];                  /* SS                            */
    char        hhmmss[7];                  /* HHMMSS                        */
    char        ctime[9];                   /* HH:MM:SS                      */
    time_t      now;                        /* current time                  */
    
    glob_t      fmatches;                   /* array of glob file matches    */

    FILELIST    cur={0};                    /* FILELIST struct of image info */

    struct stat file_stat;                  /* 'stat' information            */

#if DEBUG
    M0sxtrce( "in GetFileList" );
#endif

    /*
    ** Set request file mask if it is blank.
    */

    if ( !request->filemask ) {

      (void) strcpy( request->filemask, "*" );

    }

    /*
    ** If a LIST of station IDs is requested, let GetIDList handle it.
    */

    if ( !strncmp( request->id, "LIST", 4 ) ) {

      return GetIDList( request, list );

    }

    /*
    ** Create the space for the file name mask
    */

    len  = strlen(request->dirmask) + strlen(request->filemask) + 2;
    len += MAX_TOK_LEN * NumToken(request->dirmask);
    len += MAX_TOK_LEN * NumToken(request->filemask);
    
    filename = malloc( len );
    if ( filename == (char *) NULL ) {
      M0sxtrce( "GetFileList:: error mallocing filename" );
      return FAILURE;
    }
    
    p = request->dirmask + strlen(request->dirmask) - 1;

    (void) strcpy( filename, request->dirmask );

    if ( *p != '/' ) {
      (void) strcat( filename, "/" );
    }
    (void) strcat( filename, request->filemask );

    /*
    ** Get current time in UTC
    */

    now = time((time_t *)0);

    {

       struct tm *timeptr = gmtime(&now);
       struct tm tmptimeptr;

       /*
        * Copy "*timeptr" to "tmptimeptr" as a workaround for
        * possible bugs in "strftime" (e.g. Solaris 2.3).
        */
       tmptimeptr = *timeptr;

       strftime( date,   sizeof date,   "%Y%m%d", &tmptimeptr );
       strftime( jdate,  sizeof jdate,    "%Y%j", &tmptimeptr );
       strftime( hour,   sizeof hour,       "%H", &tmptimeptr );
       strftime( minute, sizeof minute,     "%M", &tmptimeptr );
       strftime( second, sizeof second,     "%S", &tmptimeptr );
       strftime( hhmmss, sizeof hhmmss, "%H%M%S", &tmptimeptr );
       strftime( ctime,  sizeof ctime,      "%T", &tmptimeptr );

    }

    /*
    ** Substitute replacables in fully qualified filename.
    */

    (void) strcpy( id, request->id );
    (void) Mclocase( id );
    (void) strcpy( ptype, request->ptype );
    (void) Mclocase( ptype );

    indxu = ReplaceToken( filename, "\\TYPE",   request->ptype );
    indxl = ReplaceToken( filename, "\\type",   ptype );
    indxu = ReplaceToken( filename, "\\PROD",   request->ptype );
    indxl = ReplaceToken( filename, "\\prod",   ptype );
    indxu = ReplaceToken( filename, "\\ID",     request->id );
    indxl = ReplaceToken( filename, "\\id",     id );
    indxu = ReplaceToken( filename, "\\CURDAY", date );
    indxl = ReplaceToken( filename, "\\curday", date );

    (void) sprintf( dbg, "GetFileList:: file directory matching mask: %s",
                    filename );
    M0sxtrce( dbg );

    /*
    ** Generate a list of all files matching the filename mask
    */

    rc = glob( filename, NULL, NULL, &fmatches );

    if ( rc == GLOB_NOSPACE ||
#if defined(__FreeBSD__) || defined(__APPLE__)
         rc == GLOB_NOCHECK || rc == GLOB_ABEND )
#else
         rc == GLOB_NOMATCH || rc == GLOB_ABORTED )
#endif
    {
      (void) sprintf( dbg, "GetFileList:: no files found matching: %s",
                      filename );
      M0sxtrce( dbg );
      free( filename );
      return FAILURE;
    }

    (void) sprintf( dbg, "GetFileList:: matching pattern list length: %d",
                    fmatches.gl_pathc );
    M0sxtrce( dbg );

    /*
    ** Select all matches EXCEPT directories
    */

    for ( i = 0; i < fmatches.gl_pathc; i++ ) {

      (void) stat( fmatches.gl_pathv[i], &file_stat );

      if ( !S_ISDIR( file_stat.st_mode ) ) {    /* Object not a directory    */

        (void) strcpy( cur.name, fmatches.gl_pathv[i] );
        cur.doff  = MISS;                       /* byte offset to image data */
        cur.size  = file_stat.st_size;          /* file size in bytes        */
        cur.time  = MISS;                       /* file data time            */
        cur.pos++;

        rc = PushFileByName( &cur, list );
        if ( rc == FAILURE ) {
          M0sxtrce( "GetFileList:: error adding to file linked list" );
          return FAILURE;
        }

        (void) sprintf( dbg, "GetFileList:: Position %x %x %x %d filename: %s", 
                        list, *list, &list, cur.pos, cur.name );
        M0sxtrce( dbg );

      }

    }

    free( filename );
    globfree( &fmatches );

    return SUCCESS;
}

/********************************* GetIDList *********************************/

int
GetIDList( CRITERIA *request, FILELIST **list )

/*
** Name:       GetIDList
**
** Purpose:    Get a list of files by station ID
**
** Parameters: 
**             request - criteria file has to match to be acceptable
**             list    - list of files matching 'mask'
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
*/

{
    int        i, j;                        /* counters                      */
    int        idloc=0;                     /* flag for where ID is found    */
    int        idislist;                    /* flag for request->id as LIST  */
    int        len, lennew;                 /* length of string variable     */
    int        pos;                         /* dataset position              */
    int        rc;                          /* status return flag            */
    int        indxl=-1;                    /* replacable location in string */
    int        indxu=-1;                    /* replacable location in string */

    char      *dirmask;                     /* character pointers            */
    char      *filename;                    /* file mask used for glob       */
    char      *filemask;                    /* file mask used for regexec    */
    char      *idmask;                      /* ID mask string                */
    char      *idold;                       /* concat. list of station IDs   */
    char      *idnew;                       /* concat. list of station IDs   */
    char      *idtmp;                       /* working string                */
    char      *p;                           /* generic character pointer     */

    /* <<<<< UPC add 20051005 - Add date holders >>>>> */
    char        date[9];                    /* CCYYMMDD  [UTC]               */
    char        jdate[8];                   /* CCYYJJJ   [UTC]               */
    char        hour[3];                    /* HH                            */
    char        minute[3];                  /* MM                            */
    char        second[3];                  /* SS                            */
    char        hhmmss[7];                  /* HHMMSS                        */
    char        ctime[9];                   /* HH:MM:SS                      */
    time_t      now;                        /* current time                  */
    
    char       id[MAX_ID_LEN];              /* station ID                    */
    char       reqid[MAX_ID_LEN];           /* requested station ID          */
    char       ptype[MAX_ID_LEN];           /* dataset descriptor            */

    glob_t     dmatches;                    /* array of glob dir matches     */
    glob_t     fmatches;                    /* array of glob file matches    */

    regex_t    dirreg;                      /* compiled reg expr: dirmask    */
    regex_t    filereg;                     /* compiled reg expr: filemask   */
    regex_t    idreg;                       /* compiled reg expr: idmask     */
    size_t     nmatch=2;                    /* number of matches possible    */
    regmatch_t pmatch[2];                   /* struct with match locations   */

    FILELIST   cur={0};                     /* FILELIST struct of image info */
    FILELIST   last={0};                    /* FILELIST struct of image info */
    FILELIST  *temp=NULL;                   /* list of files in directory    */

    struct stat file_stat;                  /* 'stat' information            */

#if DEBUG
    M0sxtrce( "in GetIDList" );
#endif

    /*
    ** Create a working buffer
    */

    len  = strlen(request->dirmask) + strlen(request->filemask) + 2;
    len += MAX_TOK_LEN * NumToken(request->dirmask);
    len += MAX_TOK_LEN * NumToken(request->filemask);
    
    filename = malloc( len );
    if ( filename == (char *) NULL ) {
       M0sxtrce( "GetIDList:: error mallocing space for filename" );
       return FAILURE;
    }
    
    filemask = malloc( len );
    if ( filemask == (char *) NULL ) {
       M0sxtrce( "GetIDList:: error mallocing space for filemask" );
       return FAILURE;
    }
    
    /*
    ** Save information that request->id == LIST
    */

    if ( strcmp( request->id, "LIST" ) ) {
      (void) strcpy( reqid, request->id );
      idislist = 0;
    } else {
      (void) strcpy( reqid, "*" );
      idislist = 1;
    }

    /*
    ** Create the full file pathname string
    */

    p = request->dirmask + strlen(request->dirmask) - 1;

    (void) strcpy( filename, request->dirmask );
    if ( *p != '/' ) {
      (void) strcat( filename, "/" );
    }
    (void) strcat( filename, request->filemask );

    (void) strcpy( filemask, filename );

    /*
    ** Find out if \ID or \id is specified as a token
    */

    if ( (strstr(request->dirmask, "\\ID") != (char *) NULL) ||
         (strstr(request->dirmask, "\\id") != (char *) NULL)    ) {
      idloc = 1;

    } else {
      if ( (strstr(request->filemask, "\\ID") != (char *) NULL) ||
           (strstr(request->filemask, "\\id") != (char *) NULL)    ) {
        idloc = 2;
      }
    }
         
    /*
    ** Get current time in UTC
    */

    now = time((time_t *)0);

    {

       struct tm *timeptr = gmtime(&now);
       struct tm tmptimeptr;

       /*
        * Copy "*timeptr" to "tmptimeptr" as a workaround for
        * possible bugs in "strftime" (e.g. Solaris 2.3).
        */
       tmptimeptr = *timeptr;

       strftime( date,   sizeof date,   "%Y%m%d", &tmptimeptr );
       strftime( jdate,  sizeof jdate,    "%Y%j", &tmptimeptr );
       strftime( hour,   sizeof hour,       "%H", &tmptimeptr );
       strftime( minute, sizeof minute,     "%M", &tmptimeptr );
       strftime( second, sizeof second,     "%S", &tmptimeptr );
       strftime( hhmmss, sizeof hhmmss, "%H%M%S", &tmptimeptr );
       strftime( ctime,  sizeof ctime,      "%T", &tmptimeptr );

    }

    /*
    ** Substitute replacables in fully qualified filename.
    */

    (void) strcpy( id, reqid );
    (void) Mclocase( id );
    (void) strcpy( ptype, request->ptype );
    (void) Mclocase( ptype );

    indxu = ReplaceToken( filename, "\\TYPE",   request->ptype );
    indxl = ReplaceToken( filename, "\\type",   ptype );
    indxu = ReplaceToken( filename, "\\PROD",   request->ptype );
    indxl = ReplaceToken( filename, "\\prod",   ptype );
    indxu = ReplaceToken( filename, "\\ID",     reqid );
    indxl = ReplaceToken( filename, "\\id",     id );
    indxu = ReplaceToken( filename, "\\CURDAY", date );
    indxl = ReplaceToken( filename, "\\curday", date );

    (void) sprintf( dbg, "GetIDList:: file name matching mask: %s",
                    filename );
    M0sxtrce( dbg );

    /*
    ** Now get a list of files, one per station ID.
    **
    ** There are three different approaches in the following:
    **
    ** ID location    Approach
    ** -----------    --------
    ** directory      1) get list of directories with an ID in their name
    ** (fastest)      2) use glob to get a sorted list of files in each
    **                   directory
    **                3) choose last file name from the glob list for each
    **                   directory; this will be the entry returned in 'list'
    **
    ** file name      1) generate a name mask that will be used to match
    ** (slow!)           the station ID in the list of files
    **                2) use glob to generate a list of files that match
    **                   the file mask
    **                3) use the name mask to select one file for each ID 
    **                   from the many that may have been generated by glob
    **                   
    ** none           1) use glob to generate a list of files
    ** (glacial!)     2) report full list of files (ugh!!)
    **
    */    

    switch ( idloc ) {

      case 1:

        M0sxtrce( "GetIDList:: ID specified in dirmask" );

        len  = strlen( request->dirmask);
        len += MAX_TOK_LEN * NumToken( request->dirmask );

        dirmask = malloc( len );
        if ( dirmask == (char *) NULL ) {
          M0sxtrce( "GetIDList:: error mallocing dirmask" );
          free( filename );
          free( filemask );
          return FAILURE;
        }

        (void) strcpy( dirmask, request->dirmask );

        p = dirmask + strlen( dirmask ) - 1;
        if ( *p == '/' ) *p = 0;

        indxu = ReplaceToken( dirmask, "\\ID", "[A-Z][A-Z][A-Z]" );
        if ( indxu == -1 )
          indxl = ReplaceToken( dirmask, "\\id", "[A-Z][A-Z][A-Z]" );

        indxu = ReplaceToken( dirmask, "\\TYPE", request->ptype );
        if ( indxu == -1 )
          indxl = ReplaceToken( dirmask, "\\type", request->ptype );
  
        indxu = ReplaceToken( dirmask, "\\PROD", request->ptype );
        if ( indxu == -1 )
          indxl = ReplaceToken( dirmask, "\\prod", request->ptype );
  
        indxu = ReplaceToken( dirmask, "\\CURDAY", date );
        if ( indxu == -1 )
          indxl = ReplaceToken( dirmask, "\\curday", date );

        rc = regcomp ( &dirreg, (const char *) dirmask, REG_EXTENDED );
        if ( rc != 0 ) {
          M0sxtrce( "GetIDList:: regcomp failed for dirreg" );
          free( dirmask  );
          free( filename );
          free( filemask );
          return FAILURE;
        }
  
        rc = glob( dirmask, GLOB_NOSORT, NULL, &dmatches );
        if ( rc == GLOB_NOSPACE ||
#if defined(__FreeBSD__) || defined(__APPLE__)
           rc == GLOB_NOCHECK || rc == GLOB_ABEND )
#else
           rc == GLOB_NOMATCH || rc == GLOB_ABORTED )
#endif
        {
          (void) sprintf( dbg, "GetIDList:: no files found matching: %s",
                          dirmask );
          M0sxtrce( dbg );
          free( filename );
          free( filemask );
          globfree( &dmatches );
          return FAILURE;
        }

        (void) sprintf( dbg, "GetIDList:: glob for dirmask returns %d",
                        dmatches.gl_pathc );
        M0sxtrce( dbg );

        for ( i = 0; i < dmatches.gl_pathc; i++ ) {

          (void) stat( dmatches.gl_pathv[i], &file_stat );

          if ( S_ISDIR( file_stat.st_mode ) ) {     /* Want only directories */

            (void) sprintf( dbg, "GetIDList:: match %2d: %s",
                            i, dmatches.gl_pathv[i] );
            M0sxtrce( dbg );

            (void) strcpy( filemask, dmatches.gl_pathv[i] );
            (void) strcat( filemask, "/" );
            (void) strcat( filemask, request->filemask );

            indxu = ReplaceToken( filemask, "\\TYPE", request->ptype );
            indxl = ReplaceToken( filemask, "\\type", ptype );
            indxu = ReplaceToken( filemask, "\\PROD", request->ptype );
            indxl = ReplaceToken( filemask, "\\prod", ptype );
            indxu = ReplaceToken( filemask, "\\ID",   reqid );
            indxl = ReplaceToken( filemask, "\\id",   id );

            (void) sprintf( dbg, "GetIDList:: filemask: %s", filemask );
            M0sxtrce( dbg );

            rc = glob( filemask, (int) NULL, NULL, &fmatches );
            if ( rc == GLOB_NOSPACE ||
#if defined(__FreeBSD__) || defined(__APPLE__)
              rc == GLOB_NOCHECK || rc == GLOB_ABEND )
#else
              rc == GLOB_NOMATCH || rc == GLOB_ABORTED )
#endif
            {
              (void) sprintf( dbg, "GetIDList:: no files found matching: %s",
                              dirmask );
              M0sxtrce( dbg );
#if 0
              free( filename );
              free( filemask );
              globfree( &fmatches );
              return FAILURE;
#endif
              continue;
            }

            if ( fmatches.gl_pathc > 0 ) {
            
              j = fmatches.gl_pathc - 1;
              (void) stat( fmatches.gl_pathv[j], &file_stat );

              while ( S_ISDIR( file_stat.st_mode ) ) {   /* skip directories */
                j--;
                if ( j < 0 ) break;
                (void) stat( fmatches.gl_pathv[j], &file_stat );
              }

              if ( j >= 0 ) {
                (void) sprintf( dbg, "GetIDList:: adding to list: %s", 
                                fmatches.gl_pathv[j] );
                M0sxtrce( dbg );
                (void) strcpy( cur.name, fmatches.gl_pathv[j] );
                cur.doff  = MISS;               /* byte offset to image data */
                cur.size  = file_stat.st_size;  /* file size in bytes        */
                cur.time  = MISS;               /* file data time            */
                cur.pos++;

                rc = PushFileByName( &cur, list );
                if ( rc == FAILURE ) {
                  M0sxtrce( "GetIDList:: error saving file list" );
                  free( filename );
                  free( filemask );
                  globfree( &dmatches );
                  globfree( &fmatches );
                  return FAILURE;
                }

              }

            }

          }
          globfree( &fmatches );

        }
  
        globfree( &dmatches );
        break;

      case 2:

        M0sxtrce( "GetIDList:: ID specified in filemask" );

        lennew = len;

        idmask = malloc( lennew );
        if ( idmask == (char *) NULL ) {
           M0sxtrce( "GetIDList:: error mallocing space for idmask" );
           free( filename );
           free( filemask );
           return FAILURE;
        }
    
        idnew = malloc( lennew );
        if ( idnew == (char *) NULL ) {
           M0sxtrce( "GetIDList:: error mallocing space for idnew" );
           free( filename );
           free( filemask );
           free( idmask   );
           return FAILURE;
        }
    
        idold = malloc( lennew );
        if ( idold == (char *) NULL ) {
           M0sxtrce( "GetIDList:: error mallocing space for idold" );
           free( filename );
           free( filemask );
           free( idmask   );
           free( idnew    );
           return FAILURE;
        }
    
        idtmp = malloc( lennew );
        if ( idold == (char *) NULL ) {
           M0sxtrce( "GetIDList:: error mallocing space for idold" );
           free( filename );
           free( filemask );
           free( idmask   );
           free( idnew    );
           free( idold    );
           return FAILURE;
        }

        /*
        ** Substitute replaceables in the masks to be used for regexec.
        */

        indxu = ReplaceToken( filemask, ".", "\\." );
        indxu = ReplaceToken( filemask, "*", ".*" );
        indxu = ReplaceToken( filemask, "\\TYPE", request->ptype );
        indxl = ReplaceToken( filemask, "\\type", ptype );
        indxu = ReplaceToken( filemask, "\\PROD", request->ptype );
        indxl = ReplaceToken( filemask, "\\prod", ptype );

        (void) strcpy( idmask, filemask );
        indxu = ReplaceToken( idmask, "\\ID", "(   )" );
        if ( indxu == -1 )
          indxl = ReplaceToken( idmask, "\\id", "(   )" );

        (void) sprintf( dbg, "GetIDList:: station ID matching mask: %s",
                        filemask );
        M0sxtrce( dbg );

        indxu = ReplaceToken( filemask, "\\ID", "([A-Z][A-Z][A-Z])" );
        if ( indxu == -1 )
          indxl = ReplaceToken( filemask, "\\id", "([A-Z][A-Z][A-Z])" );

        (void) sprintf( dbg, "GetIDList:: file name matching mask: %s",
                        filemask );
        M0sxtrce( dbg );

        (void) strcpy( idold, "   " );
        *idnew = 0;

        /*
        ** Compile the regular expression that will be used to get the
        ** station ID
        */

        rc = regcomp ( &filereg, (const char *) filemask, REG_EXTENDED );
        if ( rc != 0 ) {
          M0sxtrce( "GetIDList:: regcomp failed for filereg" );
          free( filename );
          free( filemask );
          free( idmask   );
          free( idnew    );
          free( idold    );
          return FAILURE;
        }

        /*
        ** Compile the regular expression that will be used to test the
        ** station ID
        */

        rc = regcomp ( &idreg, (const char *) idmask, REG_EXTENDED );
        if ( rc != 0 ) {
          M0sxtrce( "GetIDList:: regcomp failed for idreg" );
          free( filename );
          free( filemask );
          free( idmask   );
          free( idnew    );
          free( idold    );
          return FAILURE;
        }

        /*
        ** Generate a list of all files matching the filename mask
        */

        rc = glob( filename, GLOB_NOSORT, NULL, &fmatches );

        if ( rc == GLOB_NOSPACE ||
#if defined(__FreeBSD__) || defined(__APPLE__)
           rc == GLOB_NOCHECK || rc == GLOB_ABEND )
#else
           rc == GLOB_NOMATCH || rc == GLOB_ABORTED )
#endif
        {
          (void) sprintf( dbg, "GetIDList:: no files found matching: %s",
                          filename );
          M0sxtrce( dbg );
          free( filename );
          free( filemask );
          free( idmask   );
          free( idnew    );
          free( idold    );
          globfree( &fmatches );
          return FAILURE;
        }

        /*
        ** Eliminate any matches that are directories
        */

        (void) sprintf( dbg, "GetIDList:: matching pattern list length: %d",
                      fmatches.gl_pathc );
        M0sxtrce( dbg );

        for ( i = 0; i < fmatches.gl_pathc; i++ ) {

          (void) stat( fmatches.gl_pathv[i], &file_stat );

          if ( !S_ISDIR( file_stat.st_mode ) ) {          /* Want only files */

            (void) strcpy( cur.name, fmatches.gl_pathv[i] );
            cur.doff  = MISS;                   /* byte offset to image data */
            cur.size  = file_stat.st_size;      /* file size in bytes        */
            cur.time  = MISS;                   /* file data time            */
            cur.pos++;

            (void) sprintf( dbg, "GetIDList:: filename: %s", cur.name );
            M0sxtrce( dbg );

            rc = PushFileByName( &cur, &temp );
            if ( rc == FAILURE ) {
              M0sxtrce( "GetIDList:: error saving temp file list" );
              free( filename );
              free( filemask );
              free( idmask   );
              free( idnew    );
              free( idold    );
              globfree( &fmatches );
              return FAILURE;
            }
          }

        }

        /*
        ** If request is for a LIST of IDs AND the \ID replaceable was specified
        ** then use regexec to generate a list of file names one per NEXRAD
        ** station.
        */

        /*
        ** Compare the first file name in the list of potential matches
        ** to the regular expression in filereg.
        */

        cur = *temp;
        pos = 1;

        while ( cur.next != (FILELIST *) NULL ) {

          if ( regexec( &idreg, cur.name, nmatch, pmatch, 0 ) ) {

            /*
            ** Save name on list
            */
            cur.pos = pos++;
            rc = PushFileByName( &cur, list );

            (void) sprintf( dbg,
                "GetIDList:: name: %s  count: %d", cur.name, cur.pos );
            M0sxtrce ( dbg );

            /*
            ** Get the station ID from the name
            */

            if ( regexec( &filereg, cur.name, nmatch, pmatch, 0 ) ) {
              M0sxtrce( "GetIDList:: error matching filemask" );
              free( filename );
              free( filemask );
              free( idmask   );
              free( idnew    );
              free( idold    );
              globfree( &fmatches );
              return FAILURE;
            }

            /*
            ** Save previously used ID mask
            */

            if ( cur.pos >= 2 ) {
              lennew += 4;

              (void) strcpy( idtmp, idmask );
              idmask = realloc( idmask, lennew );
              (void) strcpy( idmask, idtmp );

              (void) strcpy( idtmp, idnew );
              idnew = realloc( idnew, lennew );
              (void) strcpy( idnew, idtmp );

              idold = realloc( idold, lennew );
              idtmp = realloc( idtmp, lennew );

              (void) strcpy( idold, idnew );
              (void) strcat( idnew, "|" );
            }

            /*
            ** Update ID mask with newly found station ID
            */

            len = strlen( idnew );
            (void) strncpy( idnew+len, pmatch[1].rm_so + cur.name, 3 );
            *(idnew + len + 3) = 0;
            indxu = ReplaceToken( idmask, idold, idnew );
            (void) sprintf( dbg, "GetIDList:: idmask: %s", idmask );
            M0sxtrce( dbg );

            /*
            ** Compile the ID matching regular expression using new ID list
            */

            if ( regcomp ( &idreg, (const char *) idmask, REG_EXTENDED ) ) {
              M0sxtrce( "GetIDList:: regcomp failed for idreg" );
              free( filename );
              free( filemask );
              free( idmask   );
              free( idnew    );
              free( idold    );
              globfree( &fmatches );
              return FAILURE;
            }

          }

          cur  = *cur.next;

        }

        free( idmask   );
        free( idnew    );
        free( idold    );
        globfree( &fmatches );
        break;

      default:

        M0sxtrce( "GetIDList:: ID not specified anywhere" );

        rc = glob( filename, GLOB_NOSORT, NULL, &fmatches );
        if ( rc == GLOB_NOSPACE ||
#if defined(__FreeBSD__) || defined(__APPLE__)
           rc == GLOB_NOCHECK || rc == GLOB_ABEND )
#else
           rc == GLOB_NOMATCH || rc == GLOB_ABORTED )
#endif
        {
          (void) sprintf( dbg, "GetIDList:: no files found matching: %s",
                          filename );
          M0sxtrce( dbg );
          free( filename );
          free( filemask );
          globfree( &fmatches );
          return FAILURE;
        }

        (void) sprintf( dbg, "GetIDList:: glob for filename returns %d",
                        fmatches.gl_pathc );
        M0sxtrce( dbg );

        for ( i = 0; i < fmatches.gl_pathc; i++ ) {

          (void) stat( fmatches.gl_pathv[i], &file_stat );

          if ( !S_ISDIR( file_stat.st_mode ) ) {          /* Want only files */

            (void) sprintf( dbg, "GetIDList:: match %2d: %s", i, 
fmatches.gl_pathv[i] );
            M0sxtrce( dbg );

            (void) strcpy( cur.name, fmatches.gl_pathv[i] );
            cur.doff  = MISS;                   /* byte offset to image data */
            cur.size  = file_stat.st_size;      /* file size in bytes        */
            cur.time  = MISS;                   /* file data time            */

            rc = PushFileByName( &cur, list );
            if ( rc == FAILURE ) {
              M0sxtrce( "GetIDList:: error saving file list" );
              free( filename );
              free( filemask );
              globfree( &fmatches );
              return FAILURE;
            }

          }

        }

        globfree( &fmatches );
        break;

    }

    free( filename );
    free( filemask );

    return SUCCESS;
}

/******************************** GetImgCards ********************************/

int
GetImgCards( FILELIST *cur, int *aradir, int *navcod, int *auxblk, int *cards, 
char *err )

/*
** Name:       GetImgCards
**
** Purpose:    Build the required ADDE comment cards containing the center
**             point and res information
**
** Parameters:
**             cur    - FILELIST structure containing current image information
**                      name  - full qualified name of the file
**                      pos   - position to add to list
**                      doff  - byte offset to beginning of image data
**                      size  - file size in bytes
**                      ctime - file creation time [sec since 1970]
**                      time  - file data time [sec since 1970]
**             aradir - McIDAS area directory for name
**             navcod - McIDAS navigation codicil
**             auxblk - McIDAS auxiliary block
**             cards  - McIDAS comment cards returned as integers
**             err    - error string from ReadImgDir
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
*/

{

    char  one_card[MAX_CARD_LEN-1];    /* a single comment card        */

    float elem;                        /* center element of image      */
    float lat;                         /* center latitude of image     */
    float line;                        /* center line of image         */
    float lon;                         /* center longitude of image    */
    float resx;                        /* elem res (km) at center      */
    float resy;                        /* line res (km) at center      */

    int   add;                         /* UPCAddCard function return   */
    int   num_cards=0;                 /* number of cards built        */
    int   rc;                          /* function return status       */

    /*
    ** Get center line/element of image
    */

#if DEBUG
    M0sxtrce( "in GetImgCards" );
#endif

    line = (aradir[8] * aradir[11]) / 2.0;
    elem = (aradir[9] * aradir[12]) / 2.0;

#if DEBUG
    (void) sprintf( dbg, "GetImgCards:: center point: line: %f elem: %f ",
                    line, elem );
    M0sxtrce( dbg );
#endif

    /*
    ** Get center lat/lon of image
    */

    LineRes = aradir[11];
    ElemRes = aradir[12];

    rc = NavImgToEarth( cur, navcod, line, elem, &lat, &lon, err );

    if ( rc != FAILURE ) {

      /*
      ** Build cards for center point
      */

      (void) sprintf( one_card, CEN_LAT_CARD, lat );
      num_cards++;

      add = UPCAddCard( one_card, MAX_CARD_LEN, cards, num_cards );

      if ( add == FAILURE ) {
        (void) strcpy( err, "Unable to allocate memory" );
        return FAILURE;
      }

      (void) sprintf( one_card, CEN_LON_CARD, lon );
      num_cards++;

      add = UPCAddCard(one_card, MAX_CARD_LEN, cards, num_cards);

      if ( add == FAILURE ) {
        (void) strcpy( err, "Unable to allocate memory" );
        return FAILURE;
      }

    } else {                                /* center point failure */

      return FAILURE;

    }

    /*
    ** Get the resolution at the center point of the image
    */

    rc = GetImgRes( cur, navcod, line, elem, &resx, &resy, err );

    if ( rc != FAILURE ) {

      /*
      ** Build Latitude/Longitude cards for the resolution
      */
      (void) sprintf( one_card, RES_X_CARD, (int)(resx+0.5) );
      num_cards++;

      add = UPCAddCard( one_card, MAX_CARD_LEN, cards, num_cards );

      if ( add == FAILURE ) {
        (void)strcpy(err, "Unable to allocate memory");
        return FAILURE;
      }

      (void) sprintf( one_card, RES_Y_CARD, (int)(resy+0.5) );
      num_cards++;

      add = UPCAddCard( one_card, MAX_CARD_LEN, cards, num_cards );

      if ( add == FAILURE ) {
        (void) strcpy( err, "Unable to allocate memory" );
        return FAILURE;
      }

      /*
      ** Build Computed Latitude/Longitude cards for the resolution
      */
      (void) sprintf( one_card, CRES_X_CARD, resx );
      num_cards++;

      add = UPCAddCard( one_card, MAX_CARD_LEN, cards, num_cards );

      if ( add == FAILURE ) {
        (void)strcpy(err, "Unable to allocate memory");
        return FAILURE;
      }

      (void) sprintf( one_card, CRES_Y_CARD, resy );
      num_cards++;

      add = UPCAddCard( one_card, MAX_CARD_LEN, cards, num_cards );

      if ( add == FAILURE ) {
        (void) strcpy( err, "Unable to allocate memory" );
        return FAILURE;
      }

    } else {

        /*
        ** resolution failure
        */
        return FAILURE;
    }

    /*
    ** Get the valid unit type for this image
    */

    rc = GetImgUnits( aradir, auxblk, cards, &num_cards, err );

    /*
    ** Change mcword 64 in the area directory to reflect the number
    ** of comment cards
    */

    aradir[63] = aradir[63] + num_cards;

    return SUCCESS;

}

/********************************* GetImgRes *********************************/

int
GetImgRes( FILELIST *cur, int *navcod, float line, float elem,
               float *resx, float *resy, char *err )

/*
** Name:       GetImgRes
**
** Purpose:    Calculate image resolution at center point
**
** Parameters: 
**             cur    - FILELIST structure containing current image information
**                      name  - full qualified name of the file
**                      pos   - position to add to list
**                      size  - file size in bytes
**                      ctime - file creation time [sec since 1970]
**                      time  - file data time [sec since 1970]
**             navcod - navigation codicil
**             line   - input image line
**             elem   - input image element
**             resx   - output x-resolution at center of image (km)
**             resy   - output y-resolution at center of image (km)
**             err    - error string returned
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
*/

{

    static char lastname[MAX_NAME_LEN]="";  /* filename last time in func. */

    double azimuth;                         /* directional azimuth (unused) */
    double range1;                          /* range from center down       */
    double range2;                          /* range from down 1 line to
                                                 over 1 element             */

    Freal  dum1;                            /* dummy variable               */
    Freal  dum2;                            /* dummy variable               */
    Freal  lat;                             /* lat to measure distance      */
    Freal  lat2;                            /* lat to measure distance      */
    Freal  lon;                             /* lon to measure distance      */
    Freal  lon2;                            /* lon to measure distance      */
    Freal  fline;                           /* line position for res calc   */
    Freal  felem;                           /* elem position for res calc   */

    int    i;                               /* loop counter                 */
    int    llflag;                          /* variable for nv1ini          */
    int    rc;                              /* function status              */
    int    two=2;                           /* the number two (2)           */

#if DEBUG
    M0sxtrce( "in GetImgRes" );
#endif

    if ( strcmp(lastname, cur->name) != 0 ) {

      llflag = lit_( "LL  ", 4 );
      rc     = nv1ini_( &two, &llflag );

      if ( rc != 0 ) {
        (void) sprintf( err,
           "GetImgRes:: Error in McIDAS navigation. NV1INI returns %d", rc );
        M0sxtrce( err );
        return FAILURE;
      }

      (void) strcpy( lastname, cur->name );

    }

    fline = line;
    felem = elem;

    (void) sprintf( dbg, "GetImgRes:: fline %f felem %f ", fline, felem );
    M0sxtrce( dbg );

    rc = nv1sae_( &line, &elem, &dum1, &lat, &lon, &dum2 );

    if ( rc != 0 ) {
      (void) strcpy( err,
          "GetImgRes:: Unable to transform line/element to lat/lon" );
      return FAILURE;
    }

    /*
    ** Move down one line and find the lat/lon
    */

    fline = fline + LineRes;

    rc = nv1sae_( &fline, &felem, &dum1, &lat2, &lon2, &dum2 );

    if ( rc != 0 ) {
      (void) strcpy( err,
          "GetImgRes:: Unable to transform line/element to lat/lon" );
      return FAILURE;
    }

#if DEBUG
    (void) sprintf( dbg, "GetImgRes:: %f %f lat2 %f lon2 %f rc %d",
                    fline, felem, lat2, lon2, rc );
    M0sxtrce( dbg );
#endif

    /*
    ** Find range from center to down one line
    */

    rc = lltora_( &lat, &lon, &lat2, &lon2, &range1, &azimuth );

    if ( rc != 0 ) {
      (void) sprintf( err,
          "GetImgRes:: Error in McIDAS navigation. LLTORA returns %d", rc );
      return FAILURE;
    }

    /*
    ** Move over one element and find the lat/lon
    */

    fline = fline - LineRes;
    felem = felem + ElemRes;

    rc = nv1sae_( &fline, &felem, &dum1, &lat2, &lon2, &dum2 );

    if ( rc != 0 ) {
      (void) strcpy( err,
          "GetImgRes:: Unable to transform line/element to lat/lon" );
      return FAILURE;
    }

    /*
    ** Find range from center to down one line
    */

    rc = lltora_( &lat, &lon, &lat2, &lon2, &range2, &azimuth );

    if ( rc != 0 ) {
      (void) sprintf( err,
          "GetImgRes:: Error in McIDAS navigation. LLTORA returns %d", rc );
      return FAILURE;
    }

    /*
    ** Image resolution that is not equal in the LINe and ELEment dimensions
    */

    *resy = range1;
    *resx = range2;

    (void) sprintf( dbg, "GetImgRes:: resx: %f  resy: %f", range2, range1 );
    M0sxtrce( dbg );

    return SUCCESS;

}


/******************************* NavEarthToImg *******************************/

int
NavEarthToImg( FILELIST *cur, int *navcod, float lat, float lon,
                     float *line, float *elem, char *err )

/*
** Name:       NavEarthToImg
**
** Purpose:    Convert image lat/lon into line/elem
**
** Parameters: 
**             cur    - FILELIST structure containing current image information
**                      name  - full qualified name of the file
**                      pos   - position to add to list
**                      size  - file size in bytes
**                      ctime - file creation time [sec since 1970]
**                      time  - file data time [sec since 1970]
**             navcod - input latitude
**             lat    - input latitude
**             lon    - input longitude
**             line   - output image line of lat/lon
**             elem   - output image element of lat/lon
**             err    - error string returned
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
*/

{

    static char lastname[MAX_NAME_LEN]="";  /* filename last time in func.   */

    float  dum;                             /* dummy variable                */

    int    rc;                              /* function status               */
    int    len;                             /* byte len of navcod            */
    int    llflag;                          /* variable for nv1ini           */
    int    one=1;                           /* the number one (1)            */
    int    two=2;                           /* the number two (2)            */

#if DEBUG
    M0sxtrce( "in NavEarthToImg" );
#endif

    if ( strcmp( lastname, cur->name ) != 0 ) {

      /*
      ** Initialize McIDAS nav transforms
      */

      rc = nvprep_( &one, navcod );

      if ( rc != 0 ) {
        (void) sprintf( err, "Error in McIDAS navigation. NVPREP returns %d", 
rc );
        return FAILURE;
      }

      llflag = lit_( "LL  ", 4 );
      rc     = nv1ini_( &two, &llflag );

      if (rc != 0) {
        (void) sprintf( err, "Error in McIDAS navigation. NV1INI returns %d", 
rc );
        return FAILURE;
      }

      (void) strcpy( lastname, cur->name );

    }

    rc = nv1eas_( &lat, &lon, &dum, line, elem, &dum );

    if ( rc != 0 ) {
      (void) strcpy( err, "Unable to transform lat/lon to line/element" );
      return FAILURE;
    }

    return SUCCESS;

}

/******************************* NavImgToEarth *******************************/

int
NavImgToEarth( FILELIST *cur, int *navcod, float line, float elem,
                     float *lat, float *lon, char *err )

/*
** Name:       NavImgToEarth
**
** Purpose:    Convert image line/elem into lat/lon
**
** Parameters: 
**             cur    - FILELIST structure containing current image information
**                      name  - full qualified name of the file
**                      pos   - position to add to list
**                      size  - file size in bytes
**                      ctime - file creation time [sec since 1970]
**                      time  - file data time [sec since 1970]
**             navcod - McIDAS navigation codicil
**             line   - input image line
**             elem   - input image element
**             lat    - output latitude of line/elem
**             lon    - output longitude of line/elem
**             err    - error string returned
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
*/

{

    static char lastname[MAX_NAME_LEN]=""; /* filename last time in func. */

    float dum1;                            /* dummy variable        */
    float dum2;                            /* dummy variable        */

    int   rc;                              /* function status       */
    int   llflag;                          /* variable for nv1ini   */
    int   one=1;                           /* the number one (1)    */
    int   two=2;                           /* the number two (2)    */

#if DEBUG
    M0sxtrce( "in NavImgToEarth" );
#endif

    if ( strcmp(lastname, cur->name) != 0 ) {

      /*
      ** Initialize McIDAS nav transforms
      */

      rc = nvprep_( &one, navcod );

      if ( rc != 0 ) {
        (void) sprintf( err, "Error in McIDAS navigation. NVPREP returns %d",
                        rc );
        return FAILURE;
      }

      llflag = lit_( "LL  ", 4 );
      rc     = nv1ini_( &two, &llflag );

      if ( rc != 0 ) {
        (void) sprintf( err, "NavImgToEarth:: Error in McIDAS navigation NV1INI 
returns %d", rc );
        M0sxtrce( err );
        return FAILURE;
      }

      (void) strcpy( lastname, cur->name );

    }

    rc = nv1sae_( &line, &elem, &dum1, lat, lon, &dum2 );

    if ( rc != 0 ) {
      (void) strcpy( err, "NavImgToEarth:: Unable to transform line/element to 
lat/lon" );
      M0sxtrce( err );
      return FAILURE;
    }

    return SUCCESS;

}

/********************************* NumToken **********************************/

int
NumToken( char *string )

/*
** Name:       NumToken
**
** Purpose:    Counts number of "replaceable" tokens in string
**
** Parameters:
**             string  - string to be checked
** Returns:
**             ntok -> number of tokens found
**
*/

{
    char      *p, *e;                       /* string pointers               */
    int        ntok=0;                      /* token counter                 */

    p = string;
    e = p + strlen( string ) - 1;

    while ( (p <= e)  &&
            ((strstr(p,"\\") != (char *) NULL) ||
             (strstr(p,"*")  != (char *) NULL) ||
             (strstr(p,"?")  != (char *) NULL)   ) ) {
      ntok++;
      p++;
    }

    return ntok;

}

/********************************* PushFile **********************************/

int
PushFile( FILELIST *cur, FILELIST **list )

/*
** Name:       PushFile
**
** Purpose:    Add a name and pos to a linked list without any sorting
**
** Parameters: 
**             name - filename to add to list
**             pos  - position to add to list
**             list - list to which to add item
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
*/

{

#if DEBUG
    M0sxtrce( "in PushFile" );
#endif

    FILELIST *new=NULL;

    /*
    ** malloc a new node
    */

    new = (FILELIST *) malloc( sizeof(FILELIST) );

    if ( new == NULL ) {
      return FAILURE;
    }

    /*
    ** Fill the new struct with values
    */

    *new = *cur;

    /*
    ** Insert new element into the list
    */

    if ( (*list) == NULL ) {
      new->next = NULL;
    } else {
      new->next = *list;
    }

    *list = new;

    return SUCCESS;

}

/******************************* PushFileByName ******************************/

int
PushFileByName( FILELIST *cur, FILELIST **list )

/*
** Name:       PushFileByName
**
** Purpose:    Add an image to a linked list sorted alphebetically by name
**
** Parameters: 
**             cur   - FILELIST structure containing info for current file:
**                     name  - full qualified name of the file
**                     pos   - position to add to list
**                     size  - file size in bytes
**                     ctime - file creation time [sec since 1970]
**                     time  - file data time [sec since 1970]
**             list  - list to which to add item
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
*/

{

    int strmatch;                           /* return from 'strcmp'          */

    FILELIST *after;                        /* pointer to list of files      */
    FILELIST *insert;                       /* pointer to list of files      */
    FILELIST *new=NULL;                     /* pointer to list of files      */

#if DEBUG
    M0sxtrce( "in PushFileByName" );
#endif

    /*
    ** malloc a new node
    */

    new = (FILELIST *) malloc( sizeof(FILELIST) );

    if ( new == NULL ) {
      return FAILURE;
    }

    /*
    ** Fill the new struct with values
    */

    *new = *cur;

    /*
    ** This is the first element in the list
    */

    if ( (*list) == NULL ) {
      new->next = NULL;
      *list = new;
      return SUCCESS;
    }

    /*
    ** Compare file basenames
    */

    strmatch = strcmp( Mcbasename(new->name), Mcbasename((*list)->name) );

    /*
    ** Insert the new element at the head
    */

    if ( strmatch <= 0 ) {
      new->next = *list;
      *list = new;
      return SUCCESS;
    }

    /*
    ** Insert the new element at the appropriate place in the list
    */

    insert = *list;

    while (1) {

      after = insert->next;

      /*
      ** end of list?
      */

      if (after == NULL) break;

      strmatch = strcmp(Mcbasename(new->name), Mcbasename(after->name));

      /*
      ** If the name is greater than the current name, we insert here
      */

      if ( strmatch <= 0 ) break;

      insert = after;
    }

    insert->next = new;
    new->next = after;

    return SUCCESS;

}

/****************************** PushFileByTime *******************************/

int
PushFileByTime( FILELIST *cur, FILELIST **list )

/*
** Name:       PushFileByTime
**
** Purpose:    Add an image to a linked list sorted chronologically
**             such that the most recent image at the head of the list
**
** Parameters: 
**             cur   - FILELIST structure containing current file information:
**                     name  - filename to add to list
**                     pos   - position number of name
**                     size  - file size in bytes
**                     ctime - file creation time [sec since 1970]
**                     time  - file data time [sec since 1970]
**             list  - list to which to add item
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
*/

{

    FILELIST *after;
    FILELIST *insert;
    FILELIST *new=NULL;

#if DEBUG
    M0sxtrce( "in PushFileByTime" );
#endif

    /*
    ** malloc a new node
    */

    new = (FILELIST *) malloc( sizeof(FILELIST) );
    if ( new == NULL ) return FAILURE;

    /*
    ** Fill the new struct with values
    */

    *new = *cur;

    /*
    ** This is the first element in the list
    */

    if ( (*list) == NULL ) {

      new->next = NULL;
      *list = new;
      return SUCCESS;

    }

    /*
    ** Insert the new element at the head if its time is newer.
    */

    if ( (*list)->time < cur->time ) {

        new->next = *list;
        *list = new;
        return SUCCESS;

    }

    /*
    ** Insert the new element at the appropriate place in the list
    */

    insert = *list;

    while (1) {

      /*
      ** End of list?
      */

      after = insert->next;
      if ( after == NULL ) break;

      /*
      ** If the time is greater than the current, insert it here
      */

      if ( after->time < cur->time ) break;

      insert = after;

    }

    insert->next = new;
    new->next = after;

    return SUCCESS;

}

/********************************** PopFile **********************************/

int
PopFile( FILELIST *cur, FILELIST **list )

/*
** Name:       PopFile
**
** Purpose:    Remove an image from a linked list
**
** Parameters: 
**             cur   - FILELIST structure containing current file information:
**                     name  - filename to remove from list
**                     pos   - position to remove from list
**                     size  - file size in bytes
**                     ctime - file creation time [sec since 1970]
**                     time  - file data time [sec since 1970]
**             list  - list remove which to remove item
**
** Returns:
**             SUCCESS 1
**
*/

{

#if DEBUG
    M0sxtrce( "in PopFile" );
#endif

    /*
    ** Copy an element from 'list' to 'cur' and move to next struct in 'list'
    */

    *cur = *(*list);

    (*list) = (*list)->next;

    return SUCCESS;

}

/****************************** UPCCheckImgBounds 
*******************************/

int
UPCCheckImgBounds( READPARM *read, int nlines, int nelems )

/*
** Name:       UPCCheckImgBounds
**
** Purpose:    check to see if the image limits requested will encompass any
**             data. if not, failure is returned
**
** Parameters: 
**             read   - READPARM struct containing read specs
**             nlines - number of lines requested
**             nelems - number of elements requested
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
*/

{

    int rc=SUCCESS;                         /* return code, assume success */

#if DEBUG
    M0sxtrce( "in UPCCheckImgBounds" );
#endif

    /*
    ** Check top
    */

    if ( (read->ul_line / read->line_res) + nlines <= 0 ) {
      rc = FAILURE;
    }

    /*
    ** Check bottom
    */

    if ( read->ul_line >= read->maxlin ) {
      rc = FAILURE;
    }

    /*
    ** Check left
    */

    if ( (read->ul_elem / read->elem_res) + nelems <= 0 ) {
      rc = FAILURE;
    }

    /*
    ** Check right
    */

    if ( read->ul_elem >= read->maxele ) {
      rc = FAILURE;
    }

    return (rc);

}

/****************************** ReadCodLengths *******************************/

int
ReadCodLengths( char *source, int *callen, int *navlen, int *auxlen )

/*
** Name:       ReadCodLengths
**
** Purpose:    Return cal and nav codicil byte lengths based on input
**             source type
**
** Parameters: 
**             source - McIDAS source type (AAA, GVAR, TIRO, ...)
**             callen - byte length of calibration codicil
**             navlen - byte length of navigation  codicil
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**
*/

{

#if DEBUG
    M0sxtrce( "in ReadCodLengths" );
#endif

    /*
    ** Sort through known McIDAS source types
    */

    if (strcmp(source, "AAA ") == 0) {
      *callen = 512;
      *navlen = 512;
      *auxlen = 0;
    }

    else if (strcmp(source, "GVAR") == 0) {
      *callen = 512;
      *navlen = 512 * 5;
      *auxlen = 0;
    }

    else if (strcmp(source, "TIRO") == 0) {
      *callen = 512;
      *navlen = 512;
      *auxlen = 0;
    }

    else if (strcmp(source, "NEXR") == 0) {
      *callen = 0;
      *navlen = 512;
      *auxlen = 256;
    }
    else if (strcmp(source, "NIDS") == 0) {
      *callen = 0;
      *navlen = 512;
      *auxlen = 256;
    }

    else {                        /* unknown source type, try default values */
      *callen = 512;
      *navlen = 512;
      *auxlen = 0;
      return FAILURE;
    }

    return SUCCESS;

}

/******************************* GetConfigInfo *******************************/

int
GetConfigInfo( const char *ctype, const char *cfile, CONFIG *cfg, char *err )

/*
** Name:       GetConfigInfo
**
** Purpose:    Read Unidata ADDE server configuration files and return
**             information
**
** Parameters: 
**             ctype - dataset group name
**             cfile - configuration file name
**             cfg   - CONFIG structure containing:
**                     dataloc  - location of data files
**                     infoname - name of auxiliary information file
**                     filemask - mask for data file names
**                     ipmask   - mask of acceptable IP addresses
**             err   - error return message
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
*/

{

    const char *pathname=NULL;              /* file path/name mask           */

    char       *p, *q;                      /* generic character pointers    */

    int         nargs=0;                    /* #arguments found in info file */
    int         rc;                         /* status return flag            */


#if DEBUG
    M0sxtrce( "in GetConfigInfo" );
#endif

    /*
    ** If a configuration file was specified, extract information from
    ** it.  Otherwise, get configuration information from MASK= keyword.
    */

    if ( *cfile != 0 ) {                        /* search configuration file */

      const char *value;
      char       *fullname;                 /* fully qualified pathname      */
      char        line[256];                /* lines from configuration file */
      char        dmask[256];               /* directory mask keyword        */
      char        fmask[256];               /* file mask keyword             */
      char        imask[256];               /* info file mask keyword        */
      char        pmask[256];               /* IP address mask keyword       */
      int         argh;                     /* handle for arg fetchers       */
      int         parsed_len;               /* byte length of arg block      */
      FILE       *fd=NULL;                  /* file descriptor               */

      M0sxtrce( "GetConfigInfo:: INFO= specified" );

      fullname = (char *) Mcpathname( cfile );
      fd = fopen( fullname, "r" );
      if ( fd == (FILE *) NULL ) {
        (void) sprintf( err, "error opening configuration file" );
        return FAILURE;
      }

      (void) sprintf( dmask, "%s_DIR",  ctype );
      (void) sprintf( fmask, "%s_FILE", ctype );
      (void) sprintf( imask, "%s_INFO", ctype );
      (void) sprintf( pmask, "%s_IP",   ctype );
       
      /*
      ** Read and process lines until EOF
      */

      while ( fgets(line, 256, fd) != (char *) NULL ) {

        if ( (p = strstr( line, "\n")) != (char *)NULL ) *p = 0;
        if ( (p = strstr( line, "#" )) != (char *)NULL ) *p = 0;

        if ( (p = strstr( line, "=" )) != (char *)NULL ) {

          argh = Mcargparse( (const char *) line, NULL, &parsed_len );

          if ( Mcargnum( argh, "DIRMASK") > 0 ) {

            rc = Mcargstr( argh, "DIR.MASK", 1, "./", &value );
            strcpy( cfg->dataloc, value );
            nargs++;

          } else if ( Mcargnum( argh, "FILEMASK" ) > 0 ) {

            rc = Mcargstr( argh, "FILE.MASK", 1, "*", &value );
            strcpy( cfg->filemask, value );
            nargs++;

          } else if ( Mcargnum( argh, "INFOFILE" ) > 0 ) {

            rc = Mcargstr( argh, "INFO.FILE", 1, "NOWRAD.DIR", &value );
            strcpy( cfg->infoname, value );
            nargs++;

          } else if ( Mcargnum( argh, "IPMASK" ) > 0 ) {

            rc = Mcargstr( argh, "IP.MASK", 1, "*", &value );
            strcpy( cfg->ipmask, value );
            nargs++;

          } else if ( Mcargnum( argh, dmask ) > 0 ) {

            rc = Mcargstr( argh, dmask, 1, "./", &value );
            (void) strcpy( cfg->dataloc, value );
            nargs++;

          } else if ( Mcargnum( argh, fmask ) > 0 ) {

            rc = Mcargstr( argh, fmask, 1, "*", &value );
            (void) strcpy( cfg->filemask, value );
            nargs++;

          } else if ( Mcargnum( argh, imask ) > 0 ) {

            rc = Mcargstr( argh, imask, 1, "NOWRAD.DIR", &value );
            (void) strcpy( cfg->infoname, value );
            nargs++;

          } else if ( Mcargnum( argh, pmask ) > 0 ) {

            rc = Mcargstr( argh, pmask, 1, "*", &value );
            (void) strcpy( cfg->ipmask, value );
            nargs++;
          }

          rc = Mcargfree( argh );

        }

      }

      fclose( fd );

    } else {                                /* information in MASK= keyword */

      M0sxtrce( "GetConfigInfo:: MASK= specified" );

      rc = Mcargstr( 0, "MASK", 1, " ", &pathname );

      if ( rc > 0 ) {

        (void) sprintf( dbg, "GetConfigInfo:: pathname: %s", (char *) pathname 
);
        M0sxtrce( dbg );

        p = (char *) pathname;
        while ( (q=strstr(p, "/")) != (char *) NULL ) {
          p = q+1;
        }

        (void) strcpy( cfg->filemask, p );
        *p = 0;
        (void) strcpy( cfg->dataloc, pathname );
        (void) strcpy( cfg->ipmask, "*" );
        nargs = 3;

      }

    }

    /*
    ** Return status based on how many arguments were found in infor file
    */

    if ( nargs >= 1 ) 
      return SUCCESS;
    else
      return FAILURE;

}

/***************************** GetUnitsAndScale ******************************/

int
GetUnitsAndScale( char *ctype, char *units, int *scale )

/*
** Name:       GetUnitsAndScale
**
** Purpose:    Define unit names and scales for NEXRAD Level III products
**             and WSI NOWrad (tm) imagery
**
** Parameters:
**             ctype - input cal type (RAW, BRIT, ...)
**             units - output physical units (" " K, ALB, %, ...)
**             scale - output physical scale (1, 10, 100, ...)
**
** NOTE: this function always succeeds
**
** Returns:
**             SUCCESS 1
**             FAILURE 0
**/

{

#if DEBUG
    M0sxtrce( "in GetUnitsAndScale" );
#endif

    /*
    ** Check the ctype against known values
    */

    if ( strncmp(ctype, "ALB ", 4) == 0 ) {
      (void) strncpy( units, " %  ", 4 );
      *scale = 10;

    } else if ( strncmp(ctype, "RAD ", 4) == 0 ) {
      (void) strncpy( units, "MW**", 4 );
      *scale = 1000;

      if ( strncmp(ctype, "MSAT", 4) == 0 )
        (void) strncpy( units, "WP**", 4 );

    } else if ( strncmp(ctype, "TEMP", 4) == 0 ) {
      (void) strncpy( units, " K  ", 4 );
      *scale = 10;

    } else if ( strncmp(ctype, "ECHO", 4) == 0 ) {
      (void) strncpy( units, "dbZ ", 4 );
      *scale = 10;         /* <<<<< UPC mod 20100521 - floating point ECHO 
>>>>> */

    } else if ( strncmp(ctype, "TOPS", 4) == 0 ) {
      (void) strncpy( units, "K FT", 4 );
      *scale = 1;

    } else if ( strncmp(ctype, "RAIN", 4) == 0 ) {
      (void) strncpy( units, "IN  ", 4 );
      *scale = 100;

    } else if ( strncmp(ctype, "H2O", 3) == 0 ) {  /* <<<< UPC mod 20030216 
>>>>> */
      (void) strncpy( units, "MM  ", 4 );
      *scale = 100;        /* <<<<< UPC mod 20100825 - floating point H2O >>>>> 
*/

    } else if ( strncmp(ctype, "VEL", 3) == 0 ) {
      (void) strncpy( units, "KT  ", 4 );
      *scale = 1;

    } else if ( strncmp(ctype, "SRMV", 4) == 0 ) {
      (void) strncpy( units, "KT  ", 4 );
      *scale = 1;

    } else if ( strncmp(ctype, "DBA", 3) == 0 ) {
      M0sxtrce( "GetUnitsAndScale:: matched DBA" );
      (void) strncpy( units, "DBA ", 4 );
      *scale = 10;

    } else if ( strncmp(ctype, "DZD", 3) == 0 ) {
      M0sxtrce( "GetUnitsAndScale:: matched DZD" );
      (void) strncpy( units, "DB  ", 4 );
      *scale = 10;

    } else if ( strncmp(ctype, "DCC", 3) == 0 ) {
      M0sxtrce( "GetUnitsAndScale:: matched DCC" );
      (void) strncpy( units, "CCOF", 4 );
      *scale = 100;

    } else if ( strncmp(ctype, "DKD", 3) == 0 ) {
      M0sxtrce( "GetUnitsAndScale:: matched DKD" );
      (void) strncpy( units, "DGKM", 4 );
      *scale = 10;

    } else if ( strncmp(ctype, "HC", 2) == 0 ) {
      M0sxtrce( "GetUnitsAndScale:: matched HC" );
      (void) strncpy( units, "CLAS", 4 );
      *scale = 1;

    } else if ( strncmp(ctype, "HHC", 3) == 0 ) {
      M0sxtrce( "GetUnitsAndScale:: matched HHC" );
      (void) strncpy( units, "CLAS", 4 );
      *scale = 1;

    } else if ( strncmp(ctype, "DPR", 3) == 0 ) {
      M0sxtrce( "GetUnitsAndScale:: matched DPR" );
      (void) strncpy( units, "INHR", 4 );
      *scale = 1;

    } else if ( strncmp(ctype, "DAA", 3) == 0 ) {
      M0sxtrce( "GetUnitsAndScale:: matched DAA" );
      (void) strncpy( units, "IN  ", 4 );
      *scale = 100;

    } else if ( strncmp(ctype, "DSA", 3) == 0 ) {
      M0sxtrce( "GetUnitsAndScale:: matched DSA" );
      (void) strncpy( units, "IN  ", 4 );
      *scale = 10;

    } else {                                /* default no unit and scale=1   */
      M0sxtrce( "GetUnitsAndScale:: did not match any ctype" );
      (void) strncpy( units, "    ", 4 );
      *scale = 1;

    }

    return SUCCESS;

}

/********************************** UPCAddCard *******************************/

int
UPCAddCard( char *one_card, int byte_len, int *cards, int num_cards )

/*
** Name:       UPCAddCard
**
** Purpose:    Add a character string comment card to a buffer of integers
**
** Parameters: 
**             one_card  - text string comment card
**             byte_len  - max byte length of text string comment card
**             cards     - integer array of comment cards consisting of text
**             num_cards - number of card in the intger array
**
** Returns:
**             SUCCESS 1
**
*/

{

    int i;                                  /* loop variable                 */
    int len;                                /* byte length on one_card       */
    int off;                                /* offset into cards array       */

#if DEBUG
    M0sxtrce( "in UPCAddCard" );
#endif

    /*
    ** Copy the comment card to the proper position in the integer array
    */

    off = (byte_len) * (num_cards-1);

    len = strlen(one_card);

    /*
    ** Blank out the card trailer and zero the integer array
    */

    for( i = len; i <= byte_len-1; i++ )
        one_card[i] = ' ';

    for( i = off; i <= off+byte_len-1; i++ )
        cards[i] = 0;

    movb_( &byte_len, (void *)one_card, (void *)cards, &off );

    return SUCCESS;

}

/********************************** UPCSendDir *******************************/

int
UPCSendDir( int *aradir )

/*
** Name:       UPCSendDir
**
** Purpose:    Send a McIDAS AREA directory from server to client
**
** Parameters: 
**             aradir - McIDAS area directory to send
**
** Returns:
**             SUCCESS 1
**
*/

{

    int dirbytes;        /* number of bytes to send    */

#if DEBUG
    M0sxtrce( "in UPCSendDir" );
#endif

    /*
    ** initialize byte lengths to send
    */

    dirbytes  = IMG_DIR_LEN * 4;

    /*
    ** Swap bytes in synthesized AREA directory
    */

    M0swbyt4( &aradir[0], 20 );

    if (ischar_(&aradir[20]) == 0) M0swbyt4( &aradir[20], 1 );

    M0swbyt4( &aradir[21],  3 );
    M0swbyt4( &aradir[32], 19 );
    M0swbyt4( &aradir[53],  1 );
    M0swbyt4( &aradir[58],  6 );

    /*
    ** Send the directory to the client
    */

    m0sxsend_( &dirbytes, aradir );

    sprintf(dbg, "UPCSendDir:: sent %d bytes of dir", dirbytes);
    M0sxtrce(dbg);

    /*
    ** Swap bytes back in directory
    */

    M0swbyt4( &aradir[0], 20 );

    if (ischar_(&aradir[20]) == 0) M0swbyt4( &aradir[20], 1 );

    M0swbyt4( &aradir[21],  3 );
    M0swbyt4( &aradir[32], 19 );
    M0swbyt4( &aradir[53],  1 );
    M0swbyt4( &aradir[58],  6 );

    return SUCCESS;

}

/********************************* UPCSendCards ******************************/

int
UPCSendCards( int *aradir, int *cards )

/*
** Name:       UPCSendCards
**
** Purpose:    Send McIDAS comment cards from server to client
**
** Parameters: 
**             aradir - McIDAS area directory
**             cards  - McIDAS comment cards to send
**
** Returns:
**             SUCCESS 1
**
*/

{

    int cardbytes;                          /* number of card bytes to send  */

#if DEBUG
    M0sxtrce( "in UPCSendCards" );
#endif

    cardbytes = aradir[63] * MAX_CARD_LEN;

    m0sxsend_( &cardbytes, cards );

    return SUCCESS;

}

/********************************** UPCSendAux *******************************/

int
UPCSendAux( int *auxblk, int len_aux )

/*
** Name:       UPCSendAux
**
** Purpose:    Send a McIDAS supplemental block from server to client
**
** Parameters: 
**             auxblk  - McIDAS codicil
**             len_aux - length of codicil
**
** Returns:
**             SUCCESS 1
**
*/

{

    int i;                                  /* loop variable                 */

#if DEBUG
    M0sxtrce( "in UPCSendAux" );
#endif

    /*
    ** Send the entire AUX block
    */

    m0sxsend_( &len_aux, auxblk );

    return SUCCESS;

}

/********************************** UPCSendCod *******************************/

int
UPCSendCod( int *cod, int len_cod )

/*
** Name:       UPCSendCod
**
** Purpose:    Send a McIDAS codicil (nav or cal) from server to client
**
** Parameters: 
**             cod     - McIDAS codicil
**             len_cod - length of codicil
**
** Returns:
**             SUCCESS 1
**
*/

{

    int i;                                  /* loop variable                 */

#if DEBUG
    M0sxtrce( "in UPCSendCod" );
#endif

    /*
    ** Swap bytes in words without characters
    */

    for ( i = 0; i < len_cod/4; i++ )
      if ( ischar_( &cod[i] ) == 0 ) M0swbyt4( &cod[i], 1 );

    /*
    ** Send the entire codicil
    */

    m0sxsend_( &len_cod, cod );

    /*
    ** Swap bytes in words without characters back
    */

    for ( i = 0; i < len_cod/4; i++ )
      if ( ischar_( &cod[i] ) == 0 ) M0swbyt4( &cod[i], 1 );

    return SUCCESS;

}

/********************************* UPCSendLine *******************************/

int
UPCSendLine( char *buf, int len_buf )

/*
** Name:       UPCSendLine
**
** Purpose:    Send a line of image data from server to client
**
** Parameters:
**             buf     - data to send
**             len_buf - length of data to send
**             bpp     - bytes per pixel in buf
**
** Returns:
**             SUCCESS 1
**
*/

{

#if DEBUG
    M0sxtrce( "in UPCSendLine" );
#endif

    m0sxsend_( &len_buf, (int *)buf );

    return SUCCESS;

}

/********************************* UPCSendZeros ******************************/

int
UPCSendZeros( char *buf, int len_buf )

/*
** Name:       UPCSendZeros
**
** Purpose:    Send a stream of zeros from server to client
**
** Parameters:
**             buf     - buffer to fill with zeros
**             len_buf - byte number of zeros to send
**
** SUCCESS 1
**
*/

{

#if DEBUG
    M0sxtrce( "in UPCSendZeros" );
#endif

    (void) memset( (void *) buf, 0, len_buf );
    (void) UPCSendLine( buf, len_buf );

    return SUCCESS;

}

/******************************* ReplaceToken ********************************/

int
ReplaceToken( char *mask, char *token, char *replace )

/*
** Name:       ReplaceToken
**
** Purpose:    Substitute string for replacable tokens in a string
**
** Parameters:
**             mask    - string in which to replace token
**             token   - token to replace
**             replace - replacement for token
**
** Note:       Valid tokens begin with the '\' character.
**
** Return:     >=0 first location of replacable in string
**             -1  FAILURE
**
** History: 19990520 - Created for McIDAS-X 7.50
**          19991201 - Changed logic for replacement
**
*/

{

    char *m, *p;                            /* generic character pointers    */
    char *string;                           /* working string                */

    int   loc=-1;                           /* replacable location in string */
    int   rlen;                             /* replacement string length     */
    int   tlen;                             /* token length                  */

#if DEBUG
    M0sxtrce( "in ReplaceToken" );
#endif

    string = malloc( strlen(mask) );

    if ( string == (char *)NULL ) {
      M0sxtrce( "ReplaceToken:: string token memory allocation error" );
      return loc;

    }

    m    = mask;
    rlen = strlen( replace );
    tlen = strlen( token );

    while ( (p = strstr(m,token)) != (char *)NULL ) {

      if ( loc == -1 ) loc = p - mask;

      (void) strcpy( string, p+tlen );
      *p = 0;
      (void) strcat( mask, replace );
      (void) strcat( mask, string );
      (void) sprintf( dbg, "ReplaceToken:: mask: %s", mask );
      M0sxtrce( dbg );
      m = p + rlen;

    }

    (void) free( string );
    return loc;

}

/******************************* AllowedAccess *******************************/

int
AllowedAccess( CONFIG cfg, servacct *record )

/*
** Name:       AllowedAccess
**
** Purpose:    Check to see if client requesting access to a data set
**             has been allowed to do so.
**
** Parameters:
**             cfg    - structure containing directory, file, and IP mask
**                      for data set access
**             record - token to replace
**
** Note:       Valid tokens begin with the '\' character.
**
** Return:     1 = SUCCESS
**             0 = FAILURE
**
** History: 19990907 - Created for McIDAS-X 7.60
**
*/

{

    char              *ipaddr;              /* string IP address of client   */
    char              *ipmask;              /* string matching regular expr. */

    int                rc;                  /* status return flag            */

    regex_t            idreg;               /* compiled reg expr: ipmask     */
    size_t             nmatch=4;            /* number of matches possible    */
    regmatch_t         pmatch[4];           /* struct with match locations   */

    struct in_addr     addr_int;            /* for inet_ntoa routine         */
    struct sockaddr_in name;                /* will receive the peer name    */
    int                namelen=sizeof(name);/* string length                 */


#if DEBUG
    M0sxtrce( "in AllowedAccess" );
#endif

    /* 
    ** Check to see if the request is from the local machine.  If
    ** so, then there is no need to get its IP address or check
    ** the configuration file IPMASK to see if it is allowed.
    */

    if ( record->server_address == m0lbaddrn_() ) {
      (void) sprintf( dbg, "AllowedAccess:: client is local machine" );
      M0sxtrce( dbg );
      return SUCCESS;
    }

    /*
    ** Check to see if the IPMASK is a global allow (i.e. a '*').
    ** If so, then allow access.
    */

    if ( strcmp( cfg.ipmask, "*" ) == 0 ) {
      (void) sprintf( dbg, "AllowedAccess:: IP mask does not restrict access" );
      M0sxtrce( dbg );
      return SUCCESS;
    }

    /*
    ** The request is from a different machine.  Get its IP address.
    */

    (void) getpeername( 0, (struct sockaddr *) &name, &namelen );
    record->client_address = name.sin_addr.s_addr;
    addr_int.s_addr = name.sin_addr.s_addr;

    /*
    ** Turn the file name matching regular expression into a string
    ** matching regular expression
    */
   
    ipmask = malloc( 2 * strlen( cfg.ipmask ) );

    if ( ipmask == (char *) NULL ) {
      (void) sprintf( dbg, "AllowedAccess:: error allocating IP mask memory" );
      M0sxtrce( dbg );
      return FAILURE;
    }

    (void) strcpy( ipmask, cfg.ipmask );

    rc = ReplaceToken( ipmask, ".", "\\." );
    rc = ReplaceToken( ipmask, "*", ".*" );

    (void) sprintf( dbg, "AllowedAccess:: string matching IP mask: %s",
                    ipmask );
    M0sxtrce( dbg );

    /*
    ** Turn the decimal IP address of the client into a string of
    ** the "dotted" form (XXX.XXX.XXX.XXX)
    */

#if defined(__sgi__) && defined(__GNUC__) && \
((_MIPS_SIM == _ABIN32) || (_MIPS_SIM == _ABI64))
    ipaddr = inet_ntoa( *(uint64_t *) &addr_int );
#else
    ipaddr = inet_ntoa( addr_int );
#endif

    if ( !ipaddr ) {
      (void) sprintf( dbg, "AllowedAccess:: inet_ntoa failed for IP address" );
      M0sxtrce( dbg );
      return FAILURE;
    }

    /*
    ** Compile the regular expression that will be used in the IP
    ** comparison and then do the comparison.
    */

    rc = regcomp(  &idreg, (const char *) ipmask, REG_EXTENDED );

    free( ipmask );

    if ( rc != 0 ) {
      (void) sprintf( dbg, "AllowedAccess:: regcomp failed for idreg" );
      M0sxtrce( dbg );
      return FAILURE;
    }

    if ( regexec( &idreg, ipaddr, nmatch, pmatch, 0 ) ) {
      (void) sprintf( dbg, "AllowedAccess:: client %s denied data set access",
                      ipaddr );
      M0sxtrce( dbg );
      regfree( &idreg );
      return FAILURE;
    }

    /*
    ** Access is allowed.
    */

    (void) sprintf( dbg, "AllowedAccess:: client %s is allowed data set access",
                    ipaddr );
    M0sxtrce( dbg );

    regfree( &idreg );
    return SUCCESS;
}

NOTE: All email exchanges with Unidata User Support are recorded in the Unidata inquiry tracking system and then made publicly available through the web. If you do not want to have your interactions made available in this way, you must let us know in each email you send to us.