[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;
}