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

20000727: UPC mods to 7.70 code



>From: Russ Dengel <address@hidden>
>Organization: SSEC
>Keywords: Keywords: 200007271758.e6RHwaT24691 McIDAS-X 7.70 UPC mods

Russ,

>I'm on your mods to the station data base code for DEC machines.
>Can you send me your copies of stncpy.c and stations.h.

The first file should be stnqry.c.  I have attached these files at the end of
this email.  My mods are demarked or described by '<<<<< UPC ...'.  Please
let me know if you have any questions.

>I will get these into our code ASAP.  You can assume that the code will be
>changed identically unless I let you know.

Thanks!

--------------------------- stnqry.c -----------------------------------------
/*
 * Copyright(c) 1998, Space Science and Engineering Center, UW-Madison
 * Refer to "McIDAS Software Acquisition and Distribution Policies"
 * in the file  mcidas/data/license.txt
 */

/**** $Id: stnqry.c,v 1.9 2000/04/14 18:39:53 chadj Tst $ *** */

#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "mcidas.h"
#include "stations.h"
#include "m0arg.h"

#define MAXQUERIES      255

#ifndef TRUE
#define TRUE    1
#define FALSE   0
#endif

/* function prototypes */

static int              M0STNQuery (STATION_QRY *q_info, STATION ***stn_info,
                                        int *num_sta);
int                     M0STNGetDatatypes (char ***names, int *num);
static int              FinalQuery (char *file, int *locs, int num_locs, 
                                        STATION ***stations, int *num_stn,
                                        STATION_QRY *q_info);

static STATION *        ReadAsciiStation (char *buf);
int                     QueryUserDB (char *file, STATION_QRY *q_info, 
                                     STATION ***stn_info, int *num_sta);
static int              QueryCoreDB (char *file, STATION_QRY *q_info, 
                                     STATION ***stn_info, int *num_sta);
static int              idn_ok (STATION_QRY *q_info, STATION *stn, int idx);
static int              id_ok (STATION_QRY *q_info, STATION *stn, int idx);
static int              userid_ok (STATION_QRY *q_info, STATION *stn, int idx);
static int              latlon_ok (STATION_QRY *q_info, STATION *stn);
static int              country_ok (STATION_QRY *q_info, STATION *stn);
static int              state_ok (STATION_QRY *q_info, STATION *stn);
static int              name_ok (STATION_QRY *q_info, STATION *stn);
static int              datatype_ok (STATION_QRY *q_info, STATION *stn);
static int              validdate_ok (STATION_QRY *q_info, STATION *stn);
static int              HaveStation (STATION **stn_array, int num,STATION *stn,
                                     STATION_QRY *query);
static STATION *        PluckStation (char *file, int loc);
static int              GetNextHandle(void);
static STATION *        NewStation (void);
static void             FreeStation (STATION *stn);
static int              LoadDatatypes (void);
static int              LoadTypesIndex (STNTYPESINDEX **t_index);
static int              LoadCoreHeaders (void);
static int              AddTypename (char *t_name, char ***names, int *num);
static int              DatatypeMatch (STATION *st1, STATION *st2);
int                     LoadUserDatatypes (const char *file);
void                    print_query (STATION_QRY *q);
void                    print_station (STATION *s);

/* functions not defined in mcidas.h */

float   flalo (Fint *);

/* variables used by various functions internal to these routines */
static  STATION         **result_stations[MAXQUERIES];  /* list of stations
                                                         * matching request */
static  int             result_num_stn[MAXQUERIES];     /* number of stations
                                                         * matching request */
static  int             next[MAXQUERIES];               /* next station to 
                                                         * return */
static  int             num_types=-1;                   /* number of defined
                                                         * datatypes */
static  char            **type_names=0;                 /* datatype names */
static  int             num_user_types=0;               /* number of user 
                                                         * defined datatypes */
static  char            **user_type_names=0;            /* names of user
                                                         * datatypes */
static  STNHEADER       *HEADER = 0;    /* core file header */
static  STNDATAOFFSET   *OFFSETS= 0;    /* core file data offset header */
static  STNSIZES        *SIZES  = 0;    /* core file data sizes header */
static  STNINDEXHEADER  *INDEX  = 0;    /* core file index header */

static  char            *user_file="STNDB.USER";
static  char            *site_file="STNDB.SITE";
static  char            *core_file="STNDB.CORE";

static  STATION         **invalid_stations;     /* list of invalid stations
                                                 * due to invalid dates */
static  int             invalid_num_stn;        /* number of invalid stations */

/*
 * These are the Fortran instantiations of the STATION and STATION_QRY structs.
 * These are the structures filled and retrieved in the fortran
 * jacket routines mcstnopenquery_() and mcstngetnextstation_(). Because they
 * are global "Fortran" variables, any fortran application can get at the 
 * variables in the structures by including the file stations.inc.
*/

F_STATION       fstn_;
F_STATION_QRY   fstnqry_;

/*
 * <<<<< UPC mod 20000710: cast second parameter to Mcread to off_t
 *                         cast third parameter to Mcread to size_t
 *
 * Change was made to reflect the fact that off_t and size_t sizes
 * may be different than the size of an int on 64-bit systems like
 * DEC OSF/1.
 *
 * >>>>>
 */

/*
*$ Name:
*$      M0STNSetUserFile - Initializes the name of the user station file
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      int
*$      M0STNSetUserFile(const char *file)
*$
*$ Input:
*$      file    - name of user file
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$      0      - OK
*$      -1     - file is null
*$
*$ Remarks:
*$      The user station file is an ASCII text file containing station
*$      definitions. The default file is STNDB.USER.
*$
*$ Categories:
*$      system
*$      utility
*$
*$ Filename:
*$      stnqry.c
*/
int
M0STNSetUserFile (const char *file)
{
    if (file == (char *)NULL) return -1;

    user_file = (char *)file;

    return 0;
}

/*
*$ Name:
*$      M0STNSetSiteFile - Initializes the name of the site station file
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      void
*$      M0STNSetSiteFile(const char *file)
*$
*$ Input:
*$      file    - name of site file
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$      0      - OK
*$      -1     - filename is null
*$
*$ Remarks:
*$      The site station file is an ASCII text file containing station
*$      definitions. The default file is STNDB.SITE.
*$
*$ Categories:
*$      system
*$      utility
*$
*$ Filename:
*$      stnqry.c
*/
int
M0STNSetSiteFile (const char *file)
{
    if (file == (char *)NULL) return -1;

    site_file = (char *)file;

    return 0;
}

/*
*$ Name:
*$      M0STNSetCoreFile - Initializes the name of the core station file
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      int
*$      M0STNSetCoreFile(const char *file)
*$
*$ Input:
*$      file    - name of core file
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$      0      - OK
*$      -1     - filename is null
*$
*$ Remarks:
*$      The core station file is a binary file containing station definitions.
*$      The default file is STNDB.CORE. It can not be directly modified.
*$
*$ Categories:
*$      system
*$      utility
*$
*$ Filename:
*$      stnqry.c
*/
int
M0STNSetCoreFile (const char *file)
{
    if (file == (char *)NULL) return -1;

    core_file = (char *)file;

    return 0;
}

/*
*$ Name:
*$      McSTNSetQueryDefaults - Initializes the query structure to default 
*$                              values
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      int
*$      McSTNSetQueryDefaults(STATION_QRY *query)
*$
*$ Input:
*$      none
*$
*$ Input and Output:
*$      query   - pointer to already allocated station query structure
*$
*$ Output:
*$      none
*$
*$ Return values:
*$        0     - success
*$       -1     - failed to allocate memory for query structure
*$       -2     - failed to get the current day
*$
*$ Remarks:
*$      This function fills a query structure with the default values for a
*$      query. For example, the valid date range is set to the current date, 
*$      and the lat/lon ranges are set to MCMISSING4 to cover the entire globe.
*$
*$      The query structure must already be instantiated before calling this 
*$      function.
*$
*$ Categories:
*$      system
*$      utility
*$
*$ Filename:
*$      stnqry.c
*/

int
McSTNSetQueryDefaults (STATION_QRY *query)
{

    int ok;             /* return code */
    int today;          /* today's julian day */

    ok = Mcgetday (&today);
    if (ok < 0) return -2;

    if (query == (STATION_QRY *)NULL)  return -1;
    
    memset (query, 0, sizeof (STATION_QRY));
    query->minlat = MCMISSING4;
    query->maxlat = MCMISSING4;
    query->minlon = MCMISSING4;
    query->maxlon = MCMISSING4;

    query->valid.bdate = today;
    query->valid.edate = today;

    return 0;
}

/*
*$ Name:
*$      mcstnsetquerydefaults - Initializes the query to default values
*$
*$ Interface:
*$      #include 'stations.inc'
*$
*$      integer function
*$      mcstnsetquerydefaults()
*$
*$ Input:
*$      none
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$        0     - success
*$       -2     - failed to get the current day
*$
*$ Remarks:
*$      This function fills a query structure with the default values for a
*$      query. For example, the valid date range is set to the current date, 
*$      and the lat/lon ranges are set to MCMISSING4 to cover the entire globe.
*$
*$      The query structure is the common block FSTNQRY.
*$
*$ Categories:
*$      system
*$      utility
*$
*$ Filename:
*$      stnqry.c
*/
Fint
mcstnsetquerydefaults_ (void)
{

    int ok;             /* return variable */
    int today;          /* today's julian day */

    ok = Mcgetday (&today);
    if (ok < 0) return -2;

    memset (&fstnqry_, 0, sizeof (F_STATION_QRY));
    fstnqry_.q_minlat = MCMISSING4;
    fstnqry_.q_maxlat = MCMISSING4;
    fstnqry_.q_minlon = MCMISSING4;
    fstnqry_.q_maxlon = MCMISSING4;

    memset (fstnqry_.q_id, ' ', F_IDLEN);
    memset (fstnqry_.q_userid, ' ', F_IDLEN);
    memset (fstnqry_.q_state, ' ', F_STATELEN);
    memset (fstnqry_.q_country, ' ', F_COUNTRYLEN);
    memset (fstnqry_.q_county, ' ', F_COUNTYLEN);
    memset (fstnqry_.q_match, ' ', F_NAMELEN);

    fstnqry_.q_mindate = today;
    fstnqry_.q_maxdate = today;

    return 0;
}

int
M0STNGetDatatypes (char ***names, int *num)
{

    int         ok;
    int         i;

    char        **nms;
    int         n;


    ok = LoadDatatypes ();
    if (ok < 0) return -1;

    nms = *names;

    if (num_types > 0)
    {
        n = num_types;
        nms = malloc (sizeof (char *) * n);
        if (nms == (char **)0) return -1;

        for (i = 0 ; i < num_types ; i++)
        {
            nms[i] = type_names[i];
        }
    }

    if (num_user_types > 0)
    {
        int     match;
        int     j;

        for (i = 0 ; i < num_user_types ; i++)
        {
            match = FALSE;
            for (j = 0 ; j < *num ; j++)
            {
                if (strcmp (user_type_names[i], nms[j]) == 0)
                {
                    match = TRUE;
                    break;
                }
            }
            if (match) continue;

            (n)++;
            nms = realloc (nms, sizeof (char *) * n);
            if (nms == (char **)0) return -1;
            nms[n - 1] = user_type_names[i];
        }
    }

    *names      = nms;
    *num        = n;

    return 0;
}

static int
LoadDatatypes (void)
{

#define NUM_TYPE_LOC    16
#define TYPENAME_LOC    1024

    int         ok;

    /* have we already loaded the types */
    if (num_types != -1) return 0;

    num_types = 0;

    /* load up the datatypes defined in the core file */
    ok = LoadCoreHeaders ();
#if 0
    if (ok < -1) return -1;
#endif

    /* load up the datatypes defined in the user files */
    ok = LoadUserDatatypes (site_file);

    ok = LoadUserDatatypes (user_file);

    return 0;
}

int
LoadUserDatatypes (const char *file)
{
    FILE        *pFile;
    char        *pathname;
    char        *p;
    int         num_user;
    int         ok;
    int         i;

    num_user = 0;

    pathname = (char *) Mcpathname (file);
    if (pathname == (char *)NULL) return -1;

    /* open the file, return 0 if can't open */
    if ((pFile = fopen (pathname, "r")) == (FILE *)NULL) return -1;

    i = 0;
    while (!feof (pFile))
    {
        char            buf[255];
        int             handle;
        char            *ctmp;

        if (fgets (buf, 255, pFile) == (char *)NULL) 
        {
            fclose (pFile);
            return i;
        }

        /* remove any line feed, otherwise the McIDAS arg parser pukes */
        p = buf + strlen (buf);
        while (p > buf)
        {
            if (*p == '\n')
            {
                *p='\0';
                break;
            }
            p--;
        }

        handle = Mcargparse (buf, (const McArgSyntax *)NULL, (int *)NULL);
        if (handle < 0) continue;

        ok = Mcargstr (handle, (const char *)NULL, 0, (char *)NULL, 
                                                        (const char **)&ctmp);
        if (ok < 0 || ctmp == (char *)NULL || strcmp (ctmp, "DATATYPE") != 0)
        {
            Mcargfree (handle);
            fclose (pFile);
            return i;
        }

        ok = Mcargstr (handle, (const char *)NULL, 1, (char *)NULL,
                                                        (const char **)&ctmp);
        if (ok < 0 || ctmp == (char *)NULL)
        {
            Mceprintf ("STATION ERROR: error with DATATYPE in %s\n", file);
            Mcargfree (handle);
            continue;
        }

        num_user_types++;
#       ifdef DEBUG_STATION
            Mcdprintf ("LoadUserDatatypes: num_types = %d\n", num_user_types);
#       endif
        user_type_names = (char **)realloc (user_type_names, num_user_types * 
sizeof (char *));
        if (user_type_names == (char **)NULL)
        {
            Mcargfree (handle);
            fclose (pFile);
            return -1;
        }
        user_type_names[num_user_types-1] = strdup (ctmp);
        i++;

        Mcargfree (handle);
        
    }

    fclose (pFile);
    return i;
}

/*
*$ Name:
*$      McSTNOpenQuery - Opens a query to the station database
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      int
*$      McSTNOpenQuery(STATION_QRY *query)
*$
*$ Input:
*$      query   - pointer to station query structure
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       >-1    - handle to successful query
*$       -1     - maximum number of queries reached
*$       -2     - query failed
*$
*$ Remarks:
*$      This function opens a query to the station database. The query
*$      structure must be filled with information describing the station(s) you
*$      are looking for before calling this function. Use the function
*$      McSTNSetQueryDefualts to initialize the query structure. Upon 
*$      successful completion, a handle to the query is returned.
*$
*$      Call McSTNCloseQuery to close this query after all stations have been
*$      retrieved. This frees memory allocated for the query and releases this
*$      handle so it can be used again.
*$
*$      No more than 255 queries can be active simultaneously.
*$
*$ Categories:
*$      system
*$      utility
*$
*$ Filename:
*$      stnqry.c
*/
int
McSTNOpenQuery (STATION_QRY *q_info)
{

static int      first_time = 1;

int             handle;         /* handle to results of this query */
int             ok;
int             i;

    print_query (q_info);
    if (first_time)
    {
        memset (result_stations, 0, sizeof (STATION **) * MAXQUERIES);
        memset (result_num_stn, 0, sizeof (int) * MAXQUERIES);
        first_time = 0;
    }

    invalid_stations = 0;
    invalid_num_stn = 0;

    /* get the next available handle for this query */
    handle = GetNextHandle();
    if (handle < 0) return -1;

    /* run the query */
    ok = M0STNQuery (q_info, &result_stations[handle], &result_num_stn[handle]);
    if (ok < 0)
    {
        return -2;
    }

    /* free the invalid stations list */
    for (i = 0 ; i < invalid_num_stn ; i++)
    {
        FreeStation (invalid_stations[i]);
    }
    if (invalid_stations) free (invalid_stations);
    invalid_num_stn = 0;

    next[handle] = 0;

    return handle;
}

/*
*$ Name:
*$      McSTNGetNumStations - Return the number of stations matching a query
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      int
*$      McSTNGetNumStations(int handle)
*$
*$ Input:
*$      handle  - handle to the query opened with McSTNOpenQuery.
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       >= 0   - number of stations matching query
*$       -1     - invalid handle
*$
*$ Remarks:
*$      The query must first be opened by calling McSTNOpenQuery.
*$
*$ Categories: 
*$      system 
*$      utility 
*/
int
McSTNGetNumStations (int handle)
{

    if (handle < 0 || handle >= MAXQUERIES) return -1;

    return result_num_stn[handle];
}

/*
*$ Name:
*$      mcstngetnumstations - Return the number of stations matching a query
*$
*$ Interface:
*$      integer function
*$      mcstngetnumstations(integer handle)
*$
*$ Input:
*$      handle  - handle to the query opened with mcstnopenquery.
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       >= 0   - number of stations matching query
*$       -1     - invalid handle
*$
*$ Remarks:
*$      The query must first be opened by calling mcstnopenquery.
*$
*$ Categories:
*$      system
*$      utility
*/
Fint
mcstngetnumstations_ (Fint *f_handle)
{
    return (Fint) McSTNGetNumStations (*f_handle);
}

/*
*$ Name:
*$      McSTNGetNextStation - Return the next station matching a query
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      STATION *
*$      McSTNGetNextStation(int handle)
*$
*$ Input:
*$      handle  - handle to the query opened with McSTNOpenQuery.
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0 (NULL) - no more stations to retrieve or invalid handle
*$     <>0        - pointer to the station
*$
*$ Remarks:
*$      The query must first be opened by calling McSTNOpenQuery.
*$
*$      The function returns a pointer to a structure containing all
*$      information known about the station.
*$
*$      Continue calling this function to retrieve all stations that match the
*$      query. McSTNGetNumStations will give you the number of stations matching
*$      a particular query.
*$
*$ Categories: 
*$      system 
*$      utility 
*/
STATION *
McSTNGetNextStation (int handle)
{

    STATION     *s;
    int         i;

    if (next[handle] >= result_num_stn[handle]) return (STATION *)NULL;

    i = next[handle];
    s = result_stations[handle][i];
    next[handle]++;

    return s;
}

/*
*$ Name:
*$      McSTNCloseQuery - Closes a station database query
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      int
*$      McSTNCloseQuery(int handle)
*$
*$ Input:
*$      handle  - handle to the query opened with McSTNOpenQuery.
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - success
*$       -1     - invalid handle
*$       
*$
*$ Remarks:
*$      This function should be called when you are finished retrieving all
*$      stations for a query opened with McSTNOpenQuery. It frees dynamically
*$      memory and releases the handle so it can be used again.
*$
*$ Categories: 
*$      system 
*$      utility 
*/
int
McSTNCloseQuery (int handle)
{

    int         i;
    int         num;

    if (handle < 0 || handle >= MAXQUERIES) return -1;

    if (result_stations[handle] == (STATION **)0 ||
        result_num_stn[handle] == 0) 
    {
        return 0;
    } 

    num = McSTNGetNumStations (handle);

    /* Free all the stations in the result */
    for (i = 0 ; i < num ; i++)
    {
        FreeStation (result_stations[handle][i]);
    }

    /* Free the array containing the list of pointers to the stations */
    free (result_stations[handle]);

    result_num_stn[handle] = 0;
    result_stations[handle] = (STATION **)0;

    return 0;
}

/*
*$ Name:
*$      mcstnclosequery - Closes a station database query
*$
*$ Interface:
*$      integer function
*$      mcstnclosequery(integer handle)
*$
*$ Input:
*$      handle  - handle to the query opened with mcstnopenquery.
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - success
*$       -1     - invalid handle
*$       
*$
*$ Remarks:
*$      This function should be called when you are finished retrieving all
*$      stations for a query opened with mcstnopenquery. It frees dynamically
*$      memory and releases the handle so it can be used again.
*$
*$ Categories: 
*$      system 
*$      utility 
*/
Fint
mcstnclosequery_ (Fint *f_handle)
{
    return (Fint) McSTNCloseQuery ((int) *f_handle);
}

/*
 * Returns the next available handle to a query
 *
 * returns
 *   >= 0       next available handle
 *   <  0       no handle available
*/
static int
GetNextHandle(void)
{
int     handle;

    for (handle = 0 ; handle < MAXQUERIES ; handle++)
    {
        if (result_num_stn[handle] == 0 && 
            result_stations[handle] == (STATION **)0) return handle;
    }

    return -1;
}

/*
*$ Name:
*$      M0STNQuery - Queries the station database
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      static int
*$      M0STNQuery(STATION_QRY *q_info, STATION ***stn_info, int *num_sta)
*$
*$ Input:
*$      q_info  - pointer to the query structure
*$
*$ Input and Output:
*$      stn_info - pointer to array of pointers to stations matching query
*$      num_sta  - pointer to number of stations matching query
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - success
*$       -1     - invalid handle
*$       
*$
*$ Remarks:
*$      This function queries the user and core station definition files and
*$      builds a list of stations matching the query.
*$
*$ Categories: 
*$      system 
*$      utility 
*/
static int
M0STNQuery (STATION_QRY *q_info, STATION ***stn_info, int *num_sta)
{

    char        *pathname;
    int         num_user;
    int         ok;

    num_user = 0;
    *num_sta = 0;
    *stn_info = (STATION **)NULL;

    pathname = (char *)Mcpathname (user_file);
    if (pathname != (char *)NULL) 
    {
        ok = QueryUserDB (pathname, q_info, stn_info, &num_user);
    }

    pathname = (char *)Mcpathname (site_file);
    if (pathname != (char *)NULL) 
    {
        ok = QueryUserDB (pathname, q_info, stn_info, &num_user);
    }

    pathname = (char *)Mcpathname (core_file);
    if (pathname == (char *)NULL)
    {
        *num_sta = num_user;
        return 0;
    }

    ok = QueryCoreDB (pathname, q_info, stn_info, &num_user);

    *num_sta = num_user;

    return 0;
}

/*
*$ Name:
*$      QueryUserDB - Queries a user station definition file
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      static int
*$      QueryUserDB(char *file, STATION_QRY *q_info, STATION ***stn_info, 
*$                  int *num_sta)
*$
*$ Input:
*$      file    - name of the user definition file
*$      q_info  - pointer to the query structure
*$
*$ Input and Output:
*$      stn_info - pointer to array of pointers to stations matching query
*$      num_sta  - pointer to number of stations matching query
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - success
*$       -1     - unable to open file
*$       -3     - memory allocation failed
*$       
*$
*$ Remarks:
*$      This function queries the user station definition files and builds a 
*$      list of stations matching the query.
*$
*$ Categories: 
*$      system 
*$      utility 
*/
int
QueryUserDB (char *file, STATION_QRY *q_info, STATION ***stn_info, int *num_stn)
{

    FILE        *pfile;
    STATION     **stn_array;
    int         i;

    if ( (pfile = fopen (file, "r")) == (FILE *)NULL)
    {
        return -1;
    }

    stn_array = *stn_info;
    i = *num_stn;

    while (!feof (pfile))
    {
        char            buf[1024];
        char            *p;
        char            *q;
        short           comment_line;
        STATION         *stn;
        int             j;
        int             i_match;
        int             ok;

        p = fgets (buf, 1024, pfile);
        if (p == (char *)NULL) break;

        /* skip blank lines */
        if (*p == '\n') continue;

        /* look for a line beginning with a comment character "#" */
        comment_line = FALSE;
        q = strstr (p, "#");
        if (q != (char *)NULL)
        {
            comment_line = TRUE;
            while (q != (char *)NULL && q > p)
            {
                q--;
                if (*q != ' ' && *q != '\t') 
                {
                    comment_line = FALSE;
                    break;
                }
            }
        }

        /* if we have a comment, get the next line */
        if (comment_line) continue;

        /* get the station information */
        stn = ReadAsciiStation (buf);
        if (stn == (STATION *)NULL) 
        {
            Mceprintf ("Error with station definition in user file:\n");
            Mceprintf ("  %s\n", buf);
            continue;
        }
        i_match = 0;
        for (j = 0 ; j < q_info->n_idn ; j++)
        {
            if (!idn_ok (q_info, stn, j)) continue;
            i_match = 1;
            break;
        }

        /* station ID */
        for (j = 0 ; j < q_info->n_id ; j++)
        {
            if (!id_ok(q_info, stn, j)) continue;
            i_match = 1;
            break;
        }
        if (!i_match && (q_info->n_id > 0 || q_info->n_idn))
        {
#           ifdef DEBUG_STATION
                Mcdprintf ("failed id\n");
#           endif
            FreeStation (stn);
            continue;
        }

        /* station user ID */
        for (j = 0 ; j < q_info->n_userid ; j++)
        {
            if (!userid_ok(q_info, stn, j)) continue;
            i_match = 1;
            break;
        }
        if (!i_match && q_info->n_userid > 0)
        {
#           ifdef DEBUG_STATION
                Mcdprintf ("failed user id\n");
#           endif
            FreeStation (stn);
            continue;
        }

        /* check lat/lon bounds */
        if (!latlon_ok (q_info, stn))
        {
#           ifdef DEBUG_STATION
                Mcdprintf ("failed lat/lon\n");
#           endif
            FreeStation (stn);
            continue;
        }

        /* country of station */
        if (!country_ok (q_info, stn))
        {
#           ifdef DEBUG_STATION
                Mcdprintf ("failed country\n");
#           endif
            FreeStation (stn);
            continue;
        }

        /* state of station */
        if (!state_ok (q_info, stn))
        {
#           ifdef DEBUG_STATION
                Mcdprintf ("failed state\n");
#           endif
            FreeStation (stn);
            continue;
        }

        /* station name */
        if (!name_ok (q_info, stn))
        {
#           ifdef DEBUG_STATION
                Mcdprintf ("failed station name\n");
#           endif
            FreeStation (stn);
            continue;
        }

        /* station type */
        if (!datatype_ok (q_info, stn))
        {
#           ifdef DEBUG_STATION
                Mcdprintf ("failed datatype\n");
#           endif
            FreeStation (stn);
            continue;
        }

        /*
         * we have a match on a station, see if we already have this 
         * station in the array from the user database, otherwise reallocate a 
         * new STATION pointer to the STATION array and store this station
         */
        ok = HaveStation (stn_array, *num_stn, stn, q_info);
        if (ok == 1)
        {
            FreeStation (stn);
            continue;
        }
        if (ok == 2) continue;

        /* 
         * we have a new station, reallocate a new STATION pointer to
         * the STATION array and assign it the new station pointer
         */
#       ifdef DEBUG_STATION
            Mcdprintf (" %05d", stn->idn);
            if (stn->id) Mcdprintf (" %s\n", stn->id);
#       endif
        stn_array = (STATION **)realloc (stn_array, 
                                                  sizeof (STATION *) * (i + 1));
        stn_array[i] = stn;
        i++;
    }

    *stn_info = stn_array;

#   ifdef DEBUG_STATION
        Mcdprintf ("Scanning %s results in  %d matching stations\n", file, 
                                                                  i - *num_stn);
#   endif

    *num_stn = i;

    fclose (pfile);
    return 0;
}

/*
*$ Name:
*$      QueryCoreDB - Queries a core station definition file
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      static int
*$      QueryCoreDB(char *file, STATION_QRY *q_info, STATION ***stn_info, 
*$                  int *num_sta)
*$
*$ Input:
*$      file    - name of the core definition file
*$      q_info  - pointer to the query structure
*$
*$ Input and Output:
*$      stn_info - pointer to array of pointers to stations matching query
*$      num_sta  - pointer to number of stations matching query
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - success
*$       -1     - unable to open file
*$       -3     - incorrect version of station DB
*$       -4     - failed memory allocation
*$       -5     - failed to load core headers
*$       -6     - failed to load core datatypes index
*$       
*$
*$ Remarks:
*$      This function queries the core station definition files and builds a 
*$      list of stations matching the query.
*$
*$ Categories: 
*$      system 
*$      utility 
*/
static int
QueryCoreDB (char *file, STATION_QRY *q_info, STATION ***stn_info, int *num_stn)
{

#define NSTATIONS               HEADER->n_stations

#define ID_ELEM_SIZE            (SIZES->id)
#define ID_SIZE                 (NSTATIONS * ID_ELEM_SIZE)
#define IDN_ELEM_SIZE           (SIZES->idn)
#define IDN_SIZE                (NSTATIONS * IDN_ELEM_SIZE)
#define ARGOS_ELEM_SIZE         (SIZES->argos)
#define ARGOS_SIZE              (NSTATIONS * ARGOS_ELEM_SIZE)
#define LATLON_ELEM_SIZE        (SIZES->lat)
#define LATLON_SIZE             (NSTATIONS * LATLON_ELEM_SIZE)
#define ELE_ELEM_SIZE           (SIZES->ele)
#define ELE_SIZE                (NSTATIONS * ELE_ELEM_SIZE)
#define CO_ELEM_SIZE            (SIZES->country)
#define CO_SIZE                 (NSTATIONS * CO_ELEM_SIZE)
#define ST_ELEM_SIZE            (SIZES->state)
#define ST_SIZE                 (NSTATIONS * ST_ELEM_SIZE)
#define NAME_ELEM_SIZE          (SIZES->description)
#define NAME_SIZE               (NSTATIONS * NAME_ELEM_SIZE)
#define NTYPE_ELEM_SIZE         (sizeof (int))
#define NTYPE_SIZE              (NSTATIONS * NTYPE_ELEM_SIZE)
#define TYPE_OFF_ELEM_SIZE      (sizeof (int))
#define TYPE_OFF_SIZE           (NSTATIONS * TYPE_OFF_ELEM_SIZE)
#define NCOMM_ELEM_SIZE         (sizeof (int))
#define NCOMM                   (NSTATIONS * NCOMM_ELEM_SIZE)

#define ID_LOC                  (OFFSETS->id)
#define IDN_LOC                 (OFFSETS->idn)
#define ARGOS_LOC               (OFFSETS->argos)
#define LAT_LOC                 (OFFSETS->lat)
#define LON_LOC                 (OFFSETS->lon)
#define ULAT_LOC                (OFFSETS->uplat)
#define ULON_LOC                (OFFSETS->uplon)
#define ELE_LOC                 (OFFSETS->ele)
#define UELE_LOC                (OFFSETS->upele)
#define CO_LOC                  (OFFSETS->country)
#define ST_LOC                  (OFFSETS->state)
#define NAME_LOC                (OFFSETS->description)
#define NTYPE_LOC               (OFFSETS->n_types)
#define TYPE_OFF_LOC            (OFFSETS->types)
#define NCOMM_LOC               (OFFSETS->n_comments)
#define COMM_LOC                (OFFSETS->comments)

    int         *locs;
    int         num_locs;
    int         ok;
    int         i;

    char        *id = 0;
    char        *state = 0;
    char        *country = 0;
    char        *desc = 0;
    int         *idn = 0;
    int         *lat = 0;
    int         *lon = 0;
    char        *match_str = 0;
    int         *req_types = 0;         /* holds indices of requested types */
    int         n_req_types = 0;        /* number of requested types */
    int         *last_idx=0;

    static STNTYPESINDEX        *type_index = 0;

#   ifdef DEBUG_STATION
        Mcdprintf ("In QueryCoreDB file %s\n", file);
#   endif

    if (!HEADER)
    {
        ok = LoadCoreHeaders ();
        if (ok < 0) return -1;
    }

    if (HEADER->version != 1) return -1;

    /* malloc an array of locations */
    locs = (int *)malloc (NSTATIONS * sizeof (int));
    if (!locs) return -4;
    num_locs = 0;

    /* we don't store userid's in the core file, so return if this was
     * requested
     */
    if (q_info->userid) return 0;

    for (i=0; i<NSTATIONS; i++)
    {
        int             i_match;
        int             j;

        /* if id was search condition, see if this station matches ID  */

        i_match = 0;
        for (j = 0 ; j < q_info->n_id ; j++)
        {
            char        *p;

            if (!id)
            {
                id = (char *)malloc (ID_SIZE);
                if (!id) return -4;
                Mcread (file, (off_t) ID_LOC, (size_t) ID_SIZE, (void *)id);
            } 
            p = id + i * ID_ELEM_SIZE;
            if (strcmp (q_info->id[j], p) != 0) continue;
            i_match = 1;
        }

#if 0
        if (q_info->n_id > 0 && !i_match) continue;
#endif

        /* if idn was search condition, see if this station matches IDN */
        for (j = 0 ; j < q_info->n_idn ; j++)
        {

            if (!idn)
            {
                idn = (int *)malloc (IDN_SIZE);
                if (!idn) return -4;
                Mcread (file, (off_t) IDN_LOC, (size_t) IDN_SIZE, (void *)idn);
                M0swbyt4 (idn, NSTATIONS);
            }
            if (q_info->idn[j] != idn[i]) continue;
            i_match = 1;
        }

        if ((q_info->n_idn > 0 || q_info->n_id > 0) && !i_match) continue;

        /* 
         * if lat/lon was search condition, see if station is within 
         * those bounds 
         */
#if 0
        if ((q_info->minlat != MCMISSING4) || (q_info->minlon != MCMISSING4))
        {

            if (!lat || !lon)
            {
                lat = (int *)malloc (LATLON_SIZE);
                lon = (int *)malloc (LATLON_SIZE);
                if (!lat || !lon) return -4;
                {
                    Mcread (file, (off_t) LAT_LOC, (size_t) LATLON_SIZE, (void 
*)lat);
                    Mcread (file, (off_t) LON_LOC, (size_t) LATLON_SIZE, (void 
*)lon);
                }

                M0swbyt4 (lat, NSTATIONS);
                M0swbyt4 (lon, NSTATIONS);
            }

            if (lat[i] < q_info->minlat ||
                lat[i] > q_info->maxlat ||
                lon[i] < q_info->minlon ||
                lon[i] > q_info->maxlon)
            {
                continue;
            }
        }
#endif

        if (q_info->minlat != MCMISSING4 || q_info->maxlat != MCMISSING4 ||
            q_info->minlon != MCMISSING4 || q_info->maxlon != MCMISSING4)
        {
            STATION *tmpstn;

            if (!lat || !lon)
            {
                lat = (int *)malloc (LATLON_SIZE);
                lon = (int *)malloc (LATLON_SIZE);
                if (!lat || !lon) return -4;
                {
                    Mcread (file, (off_t) LAT_LOC, (size_t) LATLON_SIZE, (void 
*)lat);
                    Mcread (file, (off_t) LON_LOC, (size_t) LATLON_SIZE, (void 
*)lon);
                }

                M0swbyt4 (lat, NSTATIONS);
                M0swbyt4 (lon, NSTATIONS);
            }

            tmpstn = NewStation ();
            tmpstn->lat = lat[i];
            tmpstn->lon = lon[i];

            ok = latlon_ok (q_info, tmpstn);
            FreeStation (tmpstn);
            if (!ok) continue;
        }
#if 0
        if (q_info->minlat != MCMISSING4 || q_info->maxlat != MCMISSING4)
        {
            if (!lat)
            {
                lat = (int *)malloc (LATLON_SIZE);
                if (!lat) return -4;

                Mcread (file, (off_t) LAT_LOC, (size_t) LATLON_SIZE, (void 
*)lat);
                M0swbyt4 (lat, NSTATIONS);
            }

            if (q_info->minlat != MCMISSING4 && lat[i] < q_info->minlat)
            {
                continue;
            }
            if (q_info->maxlat != MCMISSING4 && lat[i] > q_info->maxlat)
            {
                continue;
            }
        }

        if (q_info->minlon != MCMISSING4 || q_info->maxlon != MCMISSING4)
        {
            if (!lon)
            {
                lon = (int *)malloc (LATLON_SIZE);
                if (!lon) return -4;

                Mcread (file, (off_t) LON_LOC, (size_t) LATLON_SIZE, (void 
*)lon);
                M0swbyt4 (lon, NSTATIONS);
            }

            if (q_info->minlon != MCMISSING4 && lon[i] < q_info->minlon)
            {
                continue;
            }
            if (q_info->maxlon != MCMISSING4 && lon[i] > q_info->maxlon)
            {
                continue;
            }
        }
#endif

        /* if state was search condition, see if station is in that state */
        if (q_info->state)
        {
            char        *p;

            if (!state)
            {
                state = (char *)malloc (ST_SIZE);
                if (!state) return -4;
                Mcread (file, (off_t) ST_LOC, (size_t) ST_SIZE, (void *)state);
            }
            p = state + i * ST_ELEM_SIZE;

            if (strcmp (q_info->state, p) != 0) continue;
        }

        /* if country was search condition, see if station is in that country */
        if (q_info->country)
        {
            char        *p;

            if (!country)
            {
                country = (char *)malloc (CO_SIZE);
                if (!country) return -4;
                Mcread (file, (off_t) CO_LOC, (size_t) CO_SIZE, (void 
*)country);
            }
            p = country + i * CO_ELEM_SIZE;
            if (strcmp (q_info->country, p) != 0) continue;
        }

        /*
         * if match on station description, see if station matches that desc 
         * the check is done case insensitive
         */
        if (q_info->match)
        {
            char        *p;
            char        *t_str;

            if (!desc)
            {
                desc = (char *)malloc (NAME_SIZE);
                if (!desc) return -4;
                Mcread (file, (off_t) NAME_LOC, (size_t) NAME_SIZE, (void 
*)desc);
                match_str = strdup (q_info->match);
                Mclocase (match_str);
            }
            p = desc + i * NAME_ELEM_SIZE;
            t_str = strdup (p);
            Mclocase (t_str);
            if (strstr (t_str, match_str) == 0)
            {
                free (t_str);
                continue;
            }
            free (t_str);
        }

        /* 
         * if datatype was search condition, load the datatype index and
         * see if this station is of that type
         */
        if (q_info->n_datatypes > 0)
        {
            int q_type;
            int match;

            match = FALSE;

            /* load the types index from the file if it hasn't been load yet */
            if (!type_index)
            {
                ok = LoadTypesIndex (&type_index);
#               ifdef DEBUG_STATION
                    Mcdprintf ("QueryCoreDB: LoadTypesIndex return %d\n", ok);
#               endif
                if (ok < 0) return -7;
            }

            /* load up the indices of the requested types */
            if (n_req_types == 0)
            {
                req_types = (int *)malloc (q_info->n_datatypes * sizeof (int));

                /* locate the index of the typename we are asked for */
                for (q_type = 0 ; q_type < q_info->n_datatypes ; q_type++)
                {
                    int t_idx;
                    for (t_idx = 0 ; t_idx < HEADER->n_types ; t_idx++)
                    {
                        if (strcmp (q_info->typenames[q_type], 
                                    type_names[t_idx]) == 0) break;
                    }
    
                    /* 
                     * if the requested datatype is a type that isn't defined 
                     * in the file, then go on to the next type
                     */
                    if (t_idx >= HEADER->n_types) continue;

                    req_types[n_req_types] = t_idx;
                    n_req_types++;
                }
            }

            if (last_idx == (int *)0)
            {
                last_idx = (int *)calloc (n_req_types * sizeof (int), 
                                      sizeof (int));
                if (last_idx == (int *)NULL) return -1;
            }
            else
            {
#if 0
                if (i < last_idx[q_type])
                {
#endif
                    free (last_idx);
                    last_idx = (int *)calloc (n_req_types * sizeof (int), 
                                          sizeof (int));
                    if (last_idx == (int *)NULL) return -1;
#if 0
                }
#endif
            }

            /* look for a match on at least one of the datatypes */

            for (q_type = 0 ; q_type < n_req_types ; q_type++)
            {
                int     rt;

                rt = req_types[q_type];

                while (last_idx[q_type] < type_index->n_list[rt])
                {
                    if (i == type_index->stn_list[rt][last_idx[q_type]])
                    {
                        match = TRUE;
                        break;
                    }

                    /*
                     * if we've passed this station in the index, allow the
                     * index to catch up
                     */
                    if (i > type_index->stn_list[rt][last_idx[q_type]]) 
                    {
                        last_idx[q_type]++;
                        continue;
                    }

                    /* get the next station */
                    break;
                }

                if (match) break;
            }

            if (!match) continue;
        }


        /* this station matches the request */

        locs[num_locs] = i;
        num_locs++;
    }

    /* free any arrays we may have allocated above */

    if (id)             free (id);
    if (idn)            free (idn);
    if (country)        free (country);
    if (state)          free (state);
    if (desc)           free (desc);
    if (lat)            free (lat);
    if (lon)            free (lon);
    if (match_str)      free (match_str);

    /* 
     * if no stations matched the search criteria, then do nothing
     */
    if (num_locs == 0) 
    {
        if (locs)       free (locs);
        return 0;
    }

    ok = FinalQuery (file, locs, num_locs, stn_info, num_stn, q_info);

    /* free the locations array */
    if (locs)           free (locs);

    return 0;
}

/*
*$ Name:
*$      FinalQuery - Pluck the stations from file and add to station list
*$                   checking for duplicates
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      static int
*$      FinalQuery(char *file, int *locs, int num_locs, STATION ***stations,
*$                 int *num_stn, STATION_QRY *q_info)
*$
*$ Input:
*$      file    - name of the core definition file
*$      locs    - array of offsets into file for stations matching query
*$      num_locs- number of offsets
*$      q_info  - pointer to query info
*$
*$ Input and Output:
*$      stations - pointer to array of pointers to stations matching query
*$      num_stn  - pointer to number of stations matching query
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - success
*$       -4     - failed memory allocation
*$       
*$
*$ Remarks:
*$
*$ Categories: 
*$      system 
*$      utility 
*/
static int
FinalQuery (char *file, int *locs, int num_locs, STATION ***stations, 
            int *num_stn, STATION_QRY *q_info)
{

    int         num_user;
    int         total;
    int         i;
    int         ok;
    STATION     **stn_array;
    STATION     *stn;

#   ifdef DEBUG_STATION
        Mcdprintf ("In FinalQuery\n");
#   endif
    /* 
     * if no stations matched the preliminary search criteria, 
     * then do nothing
     */
    if (num_locs == 0) return 0;

    stn_array = *stations;
    num_user = *num_stn;
    total = num_user;

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

        /* pull all the information for this station out of the file */
        stn = PluckStation (file, locs[i]);
        if (stn == (STATION *)0) return -4;

        /*
         * we have a match on a station, see if we already have this 
         * station in the array from the user database, otherwise reallocate a 
         * new STATION pointer to the STATION array and store this station
         */
        ok = HaveStation (stn_array, num_user, stn, q_info);
        if (ok == 1)
        {
            FreeStation (stn);
            continue;
        }
        if (ok == 2) continue;

#       ifdef DEBUG_STATION
            Mcdprintf (" %05d", stn->idn);
            if (stn->id) Mcdprintf (" %s", stn->id);
            if (stn->description) Mcdprintf (" %s\n", stn->description);
#       endif
        stn_array = (STATION **)realloc (stn_array,
                                         sizeof (STATION *) * (total + 1));
        stn_array[total] = stn;
        total++;

    }

#   ifdef DEBUG_STATION
        Mcdprintf ("Scanning %s results in  %d matching stations\n", file, 
                                                              total - num_user);
#   endif
    *num_stn = total;
    *stations = stn_array;

    return 0;
}

/*
*$ Name:
*$      HaveStation - Check if a station already exists in the list
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      static int
*$      HaveStation(STATION **stn_array, int num, STATION *stn, 
*$                  STATION_QRY *query)
*$
*$ Input:
*$      stn_array - array of pointers to stations to check against
*$      num       - number of stations in array
*$      stn       - pointer to station to check
*$      query     - pointer to query struct
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - station doesn't exist
*$       1      - station already exists 
*$       2      - added to the invalid list
*$       
*$
*$ Remarks:
*$
*$ Categories: 
*$      system 
*$      utility 
*/
static int
HaveStation (STATION **stn_array, int num, STATION *stn, STATION_QRY *query)
{

    int i;

    /* if we have a valid date, then check all other paramters */
    if (!validdate_ok (query, stn))
    {

#       ifdef DEBUG_STATION
            Mcdprintf ("Failed valid dates\n");
#       endif

        /* otherwise add it to the invalid dates list of stations */

        invalid_stations = (STATION **)realloc (invalid_stations,
                                   sizeof (STATION *) * (invalid_num_stn + 1));
        invalid_stations[invalid_num_stn] = stn;
        invalid_num_stn++;

        return 2;
    }

    /* first look for this station in the station list */
    for (i=0; i<num; i++)
    {
        if (stn->idn && stn->id && stn_array[i]->id)
        {
            if (stn->idn == stn_array[i]->idn &&
                strcmp (stn->id, stn_array[i]->id) == 0 &&
                DatatypeMatch (stn,stn_array[i])) return 1;
        }
        if (stn->idn == 0 && stn_array[i]->id)
        {
            if (strcmp (stn->id, stn_array[i]->id) == 0 &&
                DatatypeMatch (stn, stn_array[i])) return 1;
        }
        if (stn->id == (char *)0)
        {
            if (stn->idn == stn_array[i]->idn &&
                DatatypeMatch (stn, stn_array[i])) return 1;
        }
    }

    /* next look for it in the list of stations that have invalid dates */
    /* this is so we can redefine a station in the user database with a */
    /* different valid date and it will override the definition in the  */
    /* of this station in the core file and won't be returned to the    */
    /* caller of the station DB API */

    stn_array = invalid_stations;
    num = invalid_num_stn;

    for (i=0; i<num; i++)
    {
        if (stn->idn && stn->id && stn_array[i]->id)
        {
            if (stn->idn == stn_array[i]->idn &&
                strcmp (stn->id, stn_array[i]->id) == 0 &&
                DatatypeMatch (stn,stn_array[i])) return 1;
        }
        if (stn->idn == 0 && stn_array[i]->id)
        {
            if (strcmp (stn->id, stn_array[i]->id) == 0 &&
                DatatypeMatch (stn, stn_array[i])) return 1;
        }
        if (stn->id == (char *)0)
        {
            if (stn->idn == stn_array[i]->idn &&
                DatatypeMatch (stn, stn_array[i])) return 1;
        }
    }

    return 0;
}

static int
DatatypeMatch (STATION *st1, STATION *st2)
{
    int i;
    int j;

    /* neither stations have any datatypes defined */

    if (st1->n_datatypes == 0 && st2->n_datatypes == 0) return 1;

    /* go through the datatypes */

    for (i = 0 ; i < st1->n_datatypes ; i++)
    {
        for (j = 0 ; j < st2->n_datatypes ; j++)
        {
            if (strcmp (st1->typenames[i], st2->typenames[j]) == 0) return 1;
        }
    }

    return 0;
}

/*
*$ Name:
*$      PluckStation - Pluck a station from the core station definition file
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      static STATION *
*$      PluckStation(char *file, int loc)
*$
*$ Input:
*$      file    - name of core stations definition file
*$      loc     - location (offset) of station in core file
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - error
*$       <>0    - pointer to station plucked from file
*$       
*$
*$ Remarks:
*$
*$ Categories: 
*$      system 
*$      utility 
*/
static STATION *
PluckStation (char *file, int loc)
{

    int         ok;
    int         tmp;
    int         offset;
    char        ctmp[255];
    STATION     *stn;


    stn = NewStation();
    if (stn == (STATION *)NULL) return (STATION *)NULL;

    /* station id */
    offset = ID_LOC + (loc * ID_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) ID_ELEM_SIZE, (void *)ctmp);
    if (ctmp && ctmp[0] != '\0') stn->id = strdup (ctmp);

    /* station idn */
    offset = IDN_LOC + (loc * IDN_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) IDN_ELEM_SIZE, (void *)&tmp);
    (void) M0swbyt4 (&tmp, 1);
    stn->idn = tmp;

    /* station ARGOS ID */
    offset = ARGOS_LOC + (loc * ARGOS_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) ARGOS_ELEM_SIZE, (void *)&tmp);
    (void) M0swbyt4 (&tmp, 1);
    stn->ARGOS = tmp;

    /* station latitude */
    offset = LAT_LOC + (loc * LATLON_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) LATLON_ELEM_SIZE, (void *)&tmp);
    (void) M0swbyt4 (&tmp, 1);
    stn->lat = tmp;

    /* station longitude */
    offset = LON_LOC + (loc * LATLON_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) LATLON_ELEM_SIZE, (void *)&tmp);
    (void) M0swbyt4 (&tmp, 1);
    stn->lon = tmp;

    /* station upper latitude */
    offset = ULAT_LOC + (loc * LATLON_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) LATLON_ELEM_SIZE, (void *)&tmp);
    (void) M0swbyt4 (&tmp, 1);
    stn->uplat = tmp;

    /* station upper longitude */
    offset = ULON_LOC + (loc * LATLON_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) LATLON_ELEM_SIZE, (void *)&tmp);
    (void) M0swbyt4 (&tmp, 1);
    stn->uplon = tmp;

    /* station elevation */
    offset = ELE_LOC + (loc * ELE_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) ELE_ELEM_SIZE, (void *)&tmp);
    (void) M0swbyt4 (&tmp, 1);
    stn->ele = tmp;

    /* station upper elevation */
    offset = UELE_LOC + (loc * ELE_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) ELE_ELEM_SIZE, (void *)&tmp);
    (void) M0swbyt4 (&tmp, 1);
    stn->upele = tmp;

    /* station country */
    offset = CO_LOC + (loc * CO_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) CO_ELEM_SIZE, (void *)ctmp);
    if (ctmp) stn->country = strdup (ctmp);

    /* station state */
    offset = ST_LOC + (loc * ST_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) ST_ELEM_SIZE, (void *)ctmp);
    if (ctmp) stn->state = strdup (ctmp);

    /* station datatypes */
    {
        int     bytes;
        int     n_types;
        int     *types;
        int     types_offset;
        int     types_idx;
        int     *t;

        /* read the number of types for this station */
        offset = NTYPE_LOC + (loc * NTYPE_ELEM_SIZE);
        ok = Mcread (file, (off_t) offset, (size_t) NTYPE_ELEM_SIZE, (void 
*)&n_types);
        (void) M0swbyt4 (&n_types, 1);

        if (n_types != 0)
        {
            /* read the offset to the types for this station */
            offset = TYPE_OFF_LOC + (loc * TYPE_OFF_ELEM_SIZE);
            ok = Mcread (file, (off_t) offset, (size_t) TYPE_OFF_ELEM_SIZE, 
(void *)&types_offset);
            (void) M0swbyt4 (&types_offset, 1);

            /* now read the types for this station */
            /* the types are a list of numbers that index into the array 
             * of strings which names the datatypes */
            bytes = n_types * sizeof (int);
            types = (int *)malloc (bytes);
            if (types == (int *)NULL) return (STATION *)NULL;

            ok = Mcread (file, (off_t) types_offset, (size_t) bytes, types);
            (void) M0swbyt4 (types, n_types);

            stn->typenames = calloc (n_types * sizeof (char *), sizeof (char 
*));
            stn->n_datatypes = n_types;

            t = types;
            for (types_idx = 0 ; types_idx < n_types ; types_idx++)
            {
                stn->typenames[types_idx] = type_names[*t];
                t++;
            }

            free (types);
        }
    }
    
    /* station description */
    offset = NAME_LOC + (loc * NAME_ELEM_SIZE);
    ok = Mcread (file, (off_t) offset, (size_t) NAME_ELEM_SIZE, (void *)ctmp);
    if (ctmp) stn->description = strdup (ctmp);

    return stn;
}

static int
idn_ok (STATION_QRY *q_info, STATION *stn, int idx)
{

    if (q_info->idn[idx] == 0) return 1;

    if (q_info->idn[idx] == stn->idn) return 1;

    return 0;
}

static int
validdate_ok (STATION_QRY *q_info, STATION *stn)
{

    /* both query  beginning and end date are before the station was active */
    if (q_info->valid.bdate < stn->valid.bdate &&
        q_info->valid.edate < stn->valid.bdate) return 0;

    /* both query beginning and end date are after the station was active */
    if (q_info->valid.bdate > stn->valid.edate &&
        q_info->valid.edate > stn->valid.edate) return 0;

    /* 
     * otherwise the requested date fits somewhere in the time the station
     * was active
     */
    return 1;
}

static int
latlon_ok (STATION_QRY *q_info, STATION *stn)
{

    static int  domain = 0;
    static int  last_lone = MCMISSING4;
    static int  last_lonw = MCMISSING4;

    int         lonw;
    int         lone;

    if (q_info->minlat != MCMISSING4 || q_info->maxlat != MCMISSING4)
    {
        if (q_info->minlat != MCMISSING4 && stn->lat < q_info->minlat)
        {
            return 0;
        }
        if (q_info->maxlat != MCMISSING4 && stn->lat > q_info->maxlat)
        {
            return 0;
        }
    }

    /* do we need to check lon bounds */

    if (q_info->maxlon == MCMISSING4 &&
        q_info->minlon == MCMISSING4) return 1;
    
    lonw = q_info->maxlon;
    lone = q_info->minlon;

    if (lone == MCMISSING4) lone = -1800000;
    if (lonw == MCMISSING4) lonw =  1800000;

/*                      now determine what type of range the longitude
 *                      boundaries exist for.  this is very important
 *                      because this information is used to
 *                      determine if a station is within the
 *                      defined box.
 *
 *    ---western hemi---0---eastern hemi---180---western hemi---0---
 *                      |                   |                   |
 *      #--domain=1 --# |                   |                   |
 *                      |                   |                   |
 *              #--domain=1 --#             |                   |
 *                      |                   |                   |
 *                      | #-- domain=1 --#  |                   |
 *                      |                   |                   |
 *                      |           #-- domain=2 --#            |
 *                      |                   |                   |
 *                      |                 #---------domain=3 --------#
 *                      |                   |                   |
 *                  #-------- domain=4 --------#                |
 *                      |                   |                   |
 */

    if (!domain || lone != last_lone || lonw != last_lonw)
    {
        if (lonw > lone) domain = 1;
        else if (lonw < 0 && lonw < lone && lone >= 0) domain = 2;
        else if (lonw < 0 && lonw < lone && lone < 0) domain = 3;
        else if (lonw >= 0 && lonw < lone && lone > 0) domain = 4;
    }

    switch(domain)
    {
        case 1: 
            if (lonw >= stn->lon && stn->lon >= lone) return 1;
            break;
        case 2:
            if (lonw <= stn->lon && stn->lon >= lone) return 1;
            break;
        case 3:
            if (stn->lon >= 0 || 
                (lonw >= stn->lon || stn->lon >= lone)) return 1;
            break;
        case 4:
            if (stn->lon <= 0 ||
                (lonw >= stn->lon || stn->lon >= lone)) return 1;
            break;
        default: return 0;
    }
#if 0
    if (q_info->minlon != MCMISSING4 || q_info->maxlon != MCMISSING4)
    {
        if (q_info->minlon != MCMISSING4 && stn->lon < q_info->minlon)
        {
            return 0;
        }
        if (q_info->maxlon != MCMISSING4 && stn->lon > q_info->maxlon)
        {
            return 0;
        }
    }
#endif

    return 0;
}

static int
id_ok (STATION_QRY *q_info, STATION *stn, int idx)
{

    /* id not requested */
    if (!q_info->id[idx]) return 1;

    /* id requested, but no id for station */
    if (!stn->id) return 0;

    if (strcmp (q_info->id[idx], stn->id) == 0) return 1;

    return 0;
}

static int
userid_ok (STATION_QRY *q_info, STATION *stn, int idx)
{

    /* user id not requested */
    if (!q_info->userid[idx]) return 1;

    /* id requested, but no id for station */
    if (!stn->userid) return 0;

    if (strcmp (q_info->userid[idx], stn->userid) == 0) return 1;

    return 0;
}

static int
country_ok (STATION_QRY *q_info, STATION *stn)
{

    /* country not requested */
    if (!q_info->country) return 1;

    /* country requested, but no country for station */
    if (!stn->country) return 0;

    if (strcmp (q_info->country, stn->country) == 0) return 1;

    return 0;
}

static int
state_ok (STATION_QRY *q_info, STATION *stn)
{

    /* state not requested */
    if (!q_info->state) return 1;

    /* state requested, but no state for station */
    if (!stn->state) return 0;

    if (strcmp (q_info->state, stn->state) == 0) return 1;

    return 0;
}

static int
name_ok (STATION_QRY *q_info, STATION *stn)
{

    char *s1;
    char *s2;
    int  match = 0;

    /* name not requested */
    if (!q_info->match) return 1;

    /* match string requested, but no state for station */
    if (!stn->description) return 0;

    /* duplicate strings and convert to common case */
    s1 = strdup (stn->description);
    s2 = strdup (q_info->match);

    if (s1 && s2)
    {
        Mcupcase (s1);
        Mcupcase (s2);

        if (strstr (s1, s2)) match = 1;

        free (s1);
        free (s2);
    }

    if (match) return 1;

    return 0;
}

static int
datatype_ok (STATION_QRY *q_info, STATION *stn)
{
    int i;
    int j;

    /* datatype not requested */
    if (!q_info->n_datatypes) return TRUE;

    for (i = 0 ; i < q_info->n_datatypes ; i++)
    {
        for (j = 0 ; j < stn->n_datatypes ; j++)
        {
            if (strcmp (q_info->typenames[i], stn->typenames[j]) == 0)
            {
                return TRUE;
            }
        }
    }

    return FALSE;
}


/*
*$ Name:
*$      ReadAsciiStation - Parse the station from a line of a user definition
*$                         file
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      static STATION *
*$      ReadAsciiStation(char *buf)
*$
*$ Input:
*$      buf     - buffer containing text of station definition
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - error (failed to parse buffer or memory allocation error)
*$       <>0    - pointer to station parsed from buffer
*$       
*$
*$ Remarks:
*$
*$ Categories: 
*$      system 
*$      utility 
*/
static STATION *
ReadAsciiStation (char *buf)
{

        int     handle;
        int     num_stn_types;
        int     num_stn_comments;
        int     j;
        int     ok;
        char    *ctmp;
        char    *p;
        STATION *stn;

        /* 
         * remove any line feeds and any tab characters, otherwise the McIDAS 
         * arg parser pukes
         */
        p = buf + strlen (buf);
        while (p > buf)
        {
            if (*p == '\n')
            {
                *p = '\0';
            }
            if (*p == '\t')
            {
                *p = ' ';
            }
            p--;
        }

        /* use the McIDAS arg parser to parse the line */
#       ifdef DEBUG_STATION
            Mcdprintf ("User Defined: %s\n", buf);
#       endif
        handle = Mcargparse (buf, (McArgSyntax *)NULL, (int *)NULL);
        if (handle < 0)
        {
            return (STATION *)NULL;
        }

        /* if the station doesn't have any identification, return */
        if (Mcargnum (handle, "ID") < 1 &&
            Mcargnum (handle, "IDN") < 1 &&
            Mcargnum (handle, "ARGOS") < 1) 
        {
            Mceprintf ("Error in station file:\n");
            Mceprintf ("  station doesn't have an ID, IDN, or ARGOS "
                       "identification\n");
            Mcargfree (handle);
            return (STATION *)NULL;
        }

        /* allocate a new station structure */
        stn = NewStation ();
        if (stn == (STATION *)NULL) return stn;

        /* station IDN */
        ok = Mcargint (handle, "IDN", 1, 0, 0, 999999, &stn->idn,
                                                        (const char **)NULL);

        /* station ID */
        ok = Mcargstr (handle, "ID", 1, (char *)NULL, (const char **)&ctmp);
        if (ctmp) stn->id = strdup (ctmp);

        /* station USERID (if provided) replaces ID */
        ok = Mcargstr (handle, "USE.RID", 1, (char *)NULL, 
                                                        (const char **)&ctmp);
        if (ctmp) 
        {
            stn->userid = strdup (ctmp);
        }

        /* station lat/lon */
        ok = Mcargill (handle, "LAT.LON", 1, MCMISSING4, 999, -999, &stn->lat,
                                                        (const char **)NULL);
        ok = Mcargill (handle, "LAT.LON", 2, MCMISSING4, 999, -999, &stn->lon,
                                                        (const char **)NULL);

        /* station lat/lon */
        ok = Mcargill (handle, "ULAT.LON", 1, MCMISSING4, 999, -999, 
                                      &stn->uplat, (const char **)NULL);
        ok = Mcargill (handle, "ULAT.LON", 2, MCMISSING4, 999, -999, 
                                      &stn->uplon, (const char **)NULL);

        /* country of station */
        ok = Mcargstr (handle, "CO", 1, (char *)NULL, (const char **)&ctmp);
        if (ctmp) stn->country = strdup (ctmp);

        /* state of station */
        ok = Mcargstr (handle, "ST", 1, (char *)NULL, (const char **)&ctmp);
        if (ctmp) stn->state = strdup (ctmp);

        /* station name */
        ok = Mcargstr (handle, "NAME", 1, (char *)NULL, (const char **)&ctmp);
        if (ctmp) stn->description = strdup (ctmp);

        /* station ELE */
        ok = Mcargint (handle, "ELE", 1, 0, 0, MCMISSING4, &stn->ele,
                                                        (const char **)NULL);
        /* station UELE */
        ok = Mcargint (handle, "UELE", 1, 0, 0, MCMISSING4, &stn->upele,
                                                        (const char **)NULL);
        /* beginning valid date */
        ok = Mcargiyd (handle, "VAL.ID", 1, BOTIME, EOTIME, BOTIME, 
                                   &(stn->valid.bdate), (const char **)NULL);

        /* ending valid date */
        ok = Mcargiyd (handle, "VAL.ID", 2, EOTIME, EOTIME, EOTIME, 
                                   &(stn->valid.edate), (const char **)NULL);

        /* station types */
        num_stn_types = Mcargnum (handle, "DATA.TYPE");
        if (num_stn_types > 0)
        {
            stn->n_datatypes = num_stn_types;
            stn->typenames = (char **)malloc (sizeof (char **) * 
                                                            stn->n_datatypes);
            if (stn->typenames == (char **)NULL) return (STATION *)NULL;
        }

        for (j=0; j<num_stn_types; j++)
        {
            int i_loc;

            Mcargstr (handle, "DATA.TYPE", j+1, (char *)NULL,
                                                        (const char **)&ctmp);
            i_loc = AddTypename (ctmp, &user_type_names, &num_user_types);
            if (i_loc < 0)
            {
                Mcargfree (handle);
                return (STATION *)NULL;
            }

            stn->typenames[j] = user_type_names[i_loc];

        }

        /* station comments */
        num_stn_comments = Mcargnum (handle, "COM.MENTS");
        if (num_stn_comments > 0)
        {
            stn->n_comments = num_stn_comments;
            stn->comments = (char **)malloc (sizeof (char **) * 
                                                            stn->n_comments);
            if (stn->comments == (char **)NULL) return (STATION *)NULL;
        }

        for (j=0; j<num_stn_comments; j++)
        {
            Mcargstr (handle, "COM.MENTS", j+1, (char *)NULL,
                                                        (const char **)&ctmp);
            stn->comments[j] = strdup (ctmp);
        }

        /* station priority */
        ok = Mcargint (handle, "PRI.ORITY", 1, MINPRIORITY, MINPRIORITY, 
                       MAXPRIORITY, &(stn->priority), (const char **)NULL);

        /* WSFO zone number stations resides */
        ok = Mcargint (handle, "ZON.E", 1, 0, 999, -999, &(stn->WFO_zone), 
                       (const char **)NULL);

        /* station is in a zone of this WSFO */
        ok = Mcargstr (handle, "WSFO", 1, (char *)NULL, (const char **)&ctmp);
        if (ctmp) stn->WSFO_office = strdup (ctmp);

        Mcargfree (handle);

        return stn;
}

/*
 * Add the typename to a type list
 *
 * Returns
 *   -1         memory allocation error
 *   >= 0       index in the names array that the typename is located
 */
static int
AddTypename (char *t_name, char ***names, int *num)
{
    int         i;
    char        **nms;
    int         n;

    nms = *names;
    n   = *num;

    for (i = 0 ; i < n ; i++)
    {
        if (strcmp (t_name, nms[i]) == 0) return i;
    }

    n++;
    nms = realloc (nms, sizeof (char *) * n);
    if (nms == (char **)0) return -1;

    nms[n-1] = strdup (t_name);

    *names = nms;
    *num   = n;

    return (n - 1);

}

/*
*$ Name:
*$      NewStation - allocate and initialize a new station structure
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      STATION *
*$      NewStation(void)
*$
*$ Input:
*$      none
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - error (failed to parse buffer or memory allocation error)
*$       <>0    - pointer to freshly allocated station
*$       
*$
*$ Remarks:
*$
*$      Station structure should be freed by calling the function
*$      FreeStation ().
*$
*$ Categories: 
*$      system 
*$      utility 
*/
STATION *
NewStation (void)
{

    STATION     *stn;

    stn = (STATION *) malloc (sizeof (STATION));
    if (stn == (STATION *)NULL) return (STATION *)NULL;

    memset (stn, 0, sizeof (STATION));
    stn->lat    = MCMISSING4;
    stn->lon    = MCMISSING4;
    stn->ele    = MCMISSING4;
    stn->uplat  = MCMISSING4;
    stn->uplon  = MCMISSING4;
    stn->upele  = MCMISSING4;
    stn->priority = 10;

    stn->valid.bdate = BOTIME;
    stn->valid.edate = EOTIME;

    return stn;
}

/*
*$ Name:
*$      FreeStation - frees a station and all allocated elements
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      void
*$      FreeStation(STATION *stn)
*$
*$ Input:
*$      stn     - pointer to station to free
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - error (failed to parse buffer or memory allocation error)
*$       <>0    - pointer to freshly allocated station
*$       
*$
*$ Remarks:
*$
*$      Frees memory for a station created by calling NewStation(). All
*$      dynamically allocated fields of the structure are also freed (station
*$      ID, datatype names, etc).
*$
*$ Categories: 
*$      system 
*$      utility 
*/
void
FreeStation (STATION *stn)
{

    int i;

    /* free all allocated strings */

    if (stn->id)                free (stn->id);
    if (stn->description)       free (stn->description);
    if (stn->state)             free (stn->state);
    if (stn->country)           free (stn->country);
    if (stn->county)            free (stn->county);
    if (stn->WSFO_office)       free (stn->WSFO_office);
    if (stn->continent)         free (stn->continent);

    /* free the comment strings */
    for (i = 0 ; i < stn->n_comments ; i++)
    {
        if (stn->comments[i])   free (stn->comments[i]);
    }
    if (stn->comments)          free (stn->comments);

    /* now free the station structure */
    free (stn);
}

#ifdef IDINFO
Fint
idinfo_ (const char *id, Fint *idn, Fint *f5or6, Fint *ewflg, Fint *llwflg,
         Fint *lat, Fint *lon, Fint *state, Fint *country, Fint *ele,
         FsLen id_len)
{
    STATION_QRY query;
    STATION     *stn;
    int         num_stn;
    int         handle;

    memset ((void *)&query, 0, sizeof (STATION_QRY));
    query.minlat = MCMISSING4;
    query.maxlat = MCMISSING4;
    query.minlon = MCMISSING4;
    query.maxlon = MCMISSING4;


    if (id_len == 0 && idn == 0) return 0;
    if (id_len > 0 && id)
    {
        query.id = fsalloc (id, id_len);
        *idn = 0;
    }
    else
    {
        query.idn = *idn;
        memset (id, ' ', 8);
    }

    handle = McSTNOpenQuery (query);
    if (handle < 0) return 0;

    num_stn = McSTNGetNumStations (handle);
    if (num_stn < 1) return 0;

    stn = McSTNGetNextStation (handle);
    if (stn == (STATION *)0) return 0;

    *lat = 0;
    *lon = 0;
    *ele = 0;
    memset (state, ' ', sizeof (Fint));
    memset (country, ' ', sizeof (Fint));


    if (stn->country) strcpy ((char *)country, stn->country);
    if (stn->state)   strcpy ((char *)state, stn->state);
    
    if (*llwflg)
    {
        Freal f_tmp;
        f_tmp = flalo_ ((Fint *)&stn->lat);
        *lat = f_tmp * 10000;
        f_tmp = flalo_ ((Fint *)&stn->lon);
        *lon = f_tmp * 10000;
    }
    else 
    {
        *lat = stn->lat;
        *lon = stn->lon;
    }

    if (*ewflg == -1)
    {
        *lat *= -1;
        *lon *= -1;
    }

    *ele = stn->ele;

    McSTNCloseQuery (handle);

    return 1;
}
#endif

/*
*$ Name:
*$      mcstnopenquery - Opens a query to the station database
*$
*$ Interface:
*$      include 'stations.inc'
*$
*$      integer function
*$      mcstnopenquery()
*$
*$ Input:
*$      none
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       >-1    - handle to successful query
*$       -1     - maximum number of queries reached
*$       -2     - query failed
*$
*$ Remarks:
*$      This function opens a query to the station database. Before calling
*$      this function, the query common block must be filled with information 
*$      describing the station(s) you are looking for. Upon successful 
*$      completion, a handle to the query is returned. The query common block
*$      is defined in the Fortran include file stations.inc.
*$
*$      Call mcstnclosequery to close this query after all stations have been
*$      retrieved. This frees memory allocated for the query and releases this
*$      handle so it can be used again.
*$
*$      No more than 255 queries can be active simultaneously.
*$
*$ Categories:
*$      system
*$      utility
*$
*$ Filename:
*$      stnqry.c
*/
Fint
mcstnopenquery_ (void)
{

    int         ok;
    STATION_QRY query;
    
    ok = McSTNSetQueryDefaults (&query);
    if (ok != 0) return -1;

    query.id            = (char **)malloc (sizeof (char *));
    if (query.id == (char **)NULL)
    {
        return -10;
    }
    query.id[0]         = fsalloc (fstnqry_.q_id, F_IDLEN);
    if (query.id[0] == (char *)NULL)
    {
        return -10;
    }
    query.n_id = 1;

    if (!query.id[0] || query.id[0][0] == '\0')
    {
        free (query.id[0]);
        free (query.id);
        query.id = (char **)NULL;
        query.n_id = 0;
    }

    query.userid        = (char **)malloc (sizeof (char *));
    query.userid[0]     = fsalloc (fstnqry_.q_userid, F_IDLEN);
    if (query.userid[0] == (char *)NULL)
    {
        return -10;
    }
    query.n_userid = 1;

    if (!query.userid[0] || query.userid[0][0] == '\0')
    {
        free (query.userid[0]);
        free (query.userid);
        query.userid = (char **)NULL;
        query.n_userid = 0;
    }

    if (fstnqry_.q_idn)
    {
        query.idn       = (int *)malloc (sizeof (int));
        query.idn[0]    = fstnqry_.q_idn;
        query.n_idn     = 1;
    }

    query.match         = fsalloc (fstnqry_.q_match, F_NAMELEN);
    if (query.match == (char *)NULL)
    {
        return -10;
    }
    if (query.match && query.match[0] == '\0')
    {
        free (query.match);
        query.match = (char *)NULL;
    }

    query.minlat        = fstnqry_.q_minlat;
    query.maxlat        = fstnqry_.q_maxlat;
    query.minlon        = fstnqry_.q_minlon;
    query.maxlon        = fstnqry_.q_maxlon;
    query.valid.bdate   = fstnqry_.q_mindate;
    query.valid.edate   = fstnqry_.q_maxdate;
    query.state         = fsalloc (fstnqry_.q_state, F_STATELEN);
    if (query.state == (char *)NULL)
    {
        return -10;
    }
    if (query.state && query.state[0] == '\0')
    {
        free (query.state);
        query.state = (char *)NULL;
    }

    query.country       = fsalloc (fstnqry_.q_country, F_COUNTRYLEN);
    if (query.country == (char *)NULL)
    {
        return -10;
    }
    if (query.country && query.country[0] == '\0')
    {
        free (query.country);
        query.country = (char *)NULL;
    }

    query.county        = fsalloc (fstnqry_.q_county, F_COUNTYLEN);
    if (query.county == (char *)NULL)
    {
        return -10;
    }
    if (query.county && query.county[0] == '\0')
    {
        free (query.county);
        query.county = (char *)NULL;
    }

    query.priority      = fstnqry_.q_priority;
    query.n_datatypes   = fstnqry_.q_n_datatypes;

    if (query.n_datatypes > 0)
    {
        query.typenames = Mcfstoarr ((char *)fstnqry_.q_typenames,
                                     query.n_datatypes,
                                     F_TYPELEN);
        if (query.typenames == (char **)NULL)
        {
            return -11;
        }
    }

    ok = McSTNOpenQuery (&query);

    /* free all the C allocated strings */
    if (query.id)               free (query.id);
    if (query.match)            free (query.match);
    if (query.state)            free (query.state);
    if (query.country)          free (query.country);
    if (query.county)           free (query.county);
    if (query.typenames)        Mcfreearr (query.typenames, query.n_datatypes);

    /* return the results */
    return ((Fint)ok);
}

/*
*$ Name:
*$      mcstngetnextstation - Return the next station matching a query
*$
*$ Interface:
*$      include 'stations.inc'
*$
*$      integer function
*$      mcstngetnextstation(int handle)
*$
*$ Input:
*$      handle  - handle to the query opened with mcstnopenquery.
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - sucess; next station returned
*$      -1      - invalid handle
*$      -2      - no more stations to be returned
*$      -3      - failed converting typenames or comments to Fortran (memory 
*$                allocation error)
*$
*$ Remarks:
*$      The query must first be opened by calling mcstnopenquery.
*$
*$      This function returns the station by filling the common block FSTN.
*$      After successfully calling this function the variables of this common
*$      contain all the information for the station.
*$
*$      Continue calling this function to retrieve all stations that match the
*$      query. mcstngetnumstations will give you the number of stations matching
*$      a particular query.
*$
*$ Categories: 
*$      system 
*$      utility 
*/
Fint
mcstngetnextstation_ (Fint *f_handle)
{

    int         handle;
    int         ok;
    STATION     *stn;

    handle = *f_handle;

    /* verify we received a valid handle */
    if (handle < 0 || handle >= MAXQUERIES)
    {
        return -1;
    }

    /* Get the information for the next station */
    stn = McSTNGetNextStation (handle);
    if (stn == (STATION *)NULL)
    {
        return -2;
    }

    /* initialize the return strings to blanks */

    memset (fstn_.s_id,         ' ', F_IDLEN);
    memset (fstn_.s_userid,     ' ', F_IDLEN);
    memset (fstn_.s_desc,       ' ', F_NAMELEN);
    memset (fstn_.s_state,      ' ', F_STATELEN);
    memset (fstn_.s_country,    ' ', F_COUNTRYLEN);
    memset (fstn_.s_county,     ' ', F_COUNTYLEN);
    memset (fstn_.s_wsfo_office,' ', F_WSFOLEN);
    memset (fstn_.s_continent,  ' ', F_CONTLEN);
    memset (fstn_.s_typenames,  ' ', F_MAXTYPES * F_TYPELEN);
    memset (fstn_.s_comments,   ' ', F_MAXCOMMENTS * F_COMMENTLEN);

    /* copy the C strings to the Fortran strings */

    if (stn->id)                Mcstrtofs (fstn_.s_id, stn->id, F_IDLEN);
    if (stn->userid)            Mcstrtofs (fstn_.s_userid, stn->userid, 
                                           F_IDLEN);
    if (stn->description)       Mcstrtofs (fstn_.s_desc, stn->description, 
                                           F_NAMELEN);
    if (stn->country)           Mcstrtofs (fstn_.s_country,stn->country, 
                                           F_COUNTRYLEN);
    if (stn->county)            Mcstrtofs (fstn_.s_county, stn->county, 
                                           F_COUNTYLEN);
    if (stn->state)             Mcstrtofs (fstn_.s_state, stn->state, 
                                           F_STATELEN);
    if (stn->WSFO_office)       Mcstrtofs (fstn_.s_wsfo_office, 
                                           stn->WSFO_office, F_WSFOLEN);
    if (stn->continent)         Mcstrtofs (fstn_.s_continent, stn->continent, 
                                           F_CONTLEN);
    if (stn->n_datatypes > 0) 
    {
        ok = Mcarrtofs ((const char * const *) stn->typenames,
                        stn->n_datatypes,
                        F_MAXTYPES,
                        F_TYPELEN,
                        (char *)fstn_.s_typenames);

        if (ok == -9999) return -3;
    }
    fstn_.s_n_datatypes = stn->n_datatypes;

    if (stn->n_comments > 0) 
    {
        ok = Mcarrtofs ((const char * const *) stn->comments,
                        stn->n_comments,
                        F_MAXCOMMENTS,
                        F_COMMENTLEN,
                        (char *)fstn_.s_comments);

        if (ok == -9999) return -3;
    }
    fstn_.s_n_comments = stn->n_comments;

    /* copy the binary data */

    fstn_.s_idn         = stn->idn;
    fstn_.s_argos       = stn->ARGOS;
    fstn_.s_lat         = stn->lat;
    fstn_.s_lon         = stn->lon;
    fstn_.s_ele         = stn->ele;
    fstn_.s_uplat       = stn->uplat;
    fstn_.s_uplon       = stn->uplon;
    fstn_.s_upele       = stn->upele;
    fstn_.s_bdate       = stn->valid.bdate;
    fstn_.s_edate       = stn->valid.edate;
    fstn_.s_wfo_zone    = stn->WFO_zone;
    fstn_.s_gmt_offset  = stn->GMT_offset;
    fstn_.s_classification      = stn->classification;
    fstn_.s_priority    = stn->priority;

    return 0;
}

/*
*$ Name:
*$      LoadCoreHeaders - load the headers from the core file
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      static int
*$      LoadCoreHeaders(void)
*$
*$ Input:
*$      none
*$
*$ Input and Output:
*$      none
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - success
*$      -1      - memory allocation error
*$      -2      - error reading header
*$      -3      - error reading sizes header
*$      -4      - error reading data offsets header
*$      -5      - error reading index header
*$      -6      - error reading datatypes
*$
*$ Remarks:
*$
*$ Categories: 
*$      system 
*$      utility 
*/
static int
LoadCoreHeaders ()
{

    int         ok;
    int         last_byte;
    int         i;
    char        *ctmp = 0;

    HEADER       = malloc (sizeof (STNHEADER));
    OFFSETS      = malloc (sizeof (STNDATAOFFSET));
    SIZES        = malloc (sizeof (STNSIZES));
    INDEX        = malloc (sizeof (STNINDEXHEADER));
    if (HEADER       == (STNHEADER *)NULL ||
        OFFSETS      == (STNDATAOFFSET *)NULL ||
        SIZES        == (STNSIZES *)NULL ||
        INDEX        == (STNINDEXHEADER *)NULL) return -1;

#   ifdef DEBUG_STATION
        Mcdprintf ("LoadCoreHeaders:\n");
#   endif
    /* read the header block */
    ok = Mcread (core_file, (off_t) 0, sizeof (STNHEADER), (void *)HEADER);
    if (ok < 0) return -2;
    M0swbyt4 (HEADER, sizeof (STNHEADER) / 4);

    /* read the sizes block */
    ok = Mcread (core_file, (off_t) HEADER->sizes_blk, sizeof (STNSIZES), (void 
*)SIZES);
    if (ok < 0) return -3;
    M0swbyt4 (SIZES, sizeof (STNSIZES) / 4);

    /* read the data offsets block */
    ok = Mcread (core_file, (off_t) HEADER->data_offset_blk, 
                 sizeof (STNDATAOFFSET), (void *)OFFSETS);
    if (ok < 0) return -4;
    M0swbyt4 (OFFSETS, sizeof (STNDATAOFFSET) / 4);

    /* read the index header block */
    ok = Mcread (core_file, (off_t) HEADER->index_info_blk, sizeof 
(STNINDEXHEADER),
                 (void *)INDEX);
    if (ok < 0) return -5;
    M0swbyt4 (INDEX, sizeof (STNINDEXHEADER) / 4);

#   ifdef DEBUG_STATION
        Mcdprintf ("LoadCoreHeaders: reading %d datatypes\n", HEADER->n_types);
#   endif

    /* allocate temporary storage */
    ctmp = calloc (SIZES->typename * sizeof (char) + 1, sizeof (char));

    /* starting location to read from */
    last_byte = HEADER->types_blk;

    /* read the datatypes and save a copy of them */
    num_types = 0;
    for (i = 0 ; i < HEADER->n_types ; i++)
    {

        ok = Mcread (core_file, (off_t) last_byte, (size_t) SIZES->typename, 
(void *)ctmp);
        if (ok < 0) return -6;

        AddTypename (ctmp, &type_names, &num_types);
        last_byte += SIZES->typename;
    }

    /* free temporary storage */
    if (ctmp) free (ctmp);

    return 0;
}

/*
*$ Name:
*$      LoadTypesIndex - load the datatypes index from the core file
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      static int
*$      LoadTypesIndex(STNTYPESINDEX **t_index)
*$
*$ Input:
*$      none
*$
*$ Input and Output:
*$      t_index - pointer to the datatypes index
*$
*$ Output:
*$      none
*$
*$ Return values:
*$       0      - success
*$      -1      - memory allocation error
*$      -2      - index header missing
*$      -3      - no datatypes in file, thus no index
*$
*$ Remarks:
*$
*$      t_index is dynamically allocated by this function.
*$
*$ Categories: 
*$      system 
*$      utility 
*/
static int
LoadTypesIndex (STNTYPESINDEX **t_index)
{

    STNTYPESINDEX       *typesidx;
    int                 *num_list;
    int                 **stn_list;
    int                 n_types;
    unsigned            n_bytes;
    int                 last_byte;
    int                 i;

    typesidx = malloc (sizeof (STNTYPESINDEX));
    if (typesidx == (STNTYPESINDEX *)NULL) return -1;

    if (HEADER->n_types < 1) return -3;

    n_types = HEADER->n_types;

#   if DEBUG_STATION
        Mcdprintf ("LoadTypesIndex: n_types = %d\n", n_types);
#   endif

    if (INDEX->type_index_offset == MCMISSING) return -2;

#   ifdef DEBUG_STATION
        Mcdprintf ("LoadTypesIndex: mallocing num_list\n");
#   endif
    num_list = (int *)malloc (n_types * sizeof (int));
    if (num_list == (int *)NULL) return -1;
    
    Mcread (core_file, (off_t) INDEX->type_index_offset, (size_t) (n_types * 
sizeof (int)),
            (void *)num_list);
    M0swbyt4 (num_list, n_types);

#   ifdef DEBUG_STATION
        Mcdprintf ("LoadTypesIndex: mallocing stn_list pointers\n");
#   endif
    stn_list = (int **)malloc (n_types * sizeof (int *));
    if (stn_list == (int **)NULL) return -1;

    last_byte = INDEX->type_index_offset + n_types * sizeof (int);
    for (i = 0 ; i < n_types ; i++)
    {
#       ifdef DEBUG_STATION
            Mcdprintf ("LoadTypesIndex: %d stations for type %d\n", 
num_list[i], i);
#       endif
        /* if we have stations for this type, load the index for that type */
        if (num_list[i] > 0)
        {
            n_bytes = num_list[i] * sizeof (int);
#           ifdef DEBUG_STATION
                Mcdprintf ("LoadTypesIndex: mallocing stn_list[%d] pointers\n", 
i);
#           endif
            stn_list[i] = (int *)malloc (n_bytes);
            if (stn_list[i] == (int *)NULL) return -1;
            Mcread (core_file, (off_t) last_byte, (size_t) n_bytes, (void 
*)stn_list[i]); 
            M0swbyt4 (stn_list[i], n_bytes / 4);
            last_byte += n_bytes;
        }
    }

    typesidx->n_list = num_list;
    typesidx->stn_list = stn_list;

    *t_index = typesidx;

    return 0;
}

/*
*$ Name:
*$      McSTNQueryAddID - Adds this character ID to list of ID's in query
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      int
*$      McSTNQueryAddID(STATION_QRY *query, char *id)
*$
*$ Input:
*$      id     - ID of the station to add to the query id list
*$
*$ Input and Output:
*$      query  - query structure
*$
*$ Output:
*$      none
*$
*$ Return values:
*$      0      - OK
*$      -1     - memory allocation error
*$
*$ Remarks:
*$      Memory for the list of pointers to the strings is reallocated each
*$      time a new id is added. Also, new memory for id string is allocated.
*$      It is up to the caller to free this memory.
*$
*$ Categories:
*$      system
*$      utility
*$
*$ Filename:
*$      stnqry.c
*/
int
McSTNQueryAddID (STATION_QRY *query, char *id)
{

    int         n_id;

    if (id == (char *)NULL) return -2;

    n_id = query->n_id;
    if (n_id == 0) query->id = 0;

    query->id = (char **)realloc (query->id, (n_id + 1) * sizeof (char *));
    if (query->id == (char **)NULL) return -1;

    query->id[n_id] = (char *)strdup (id);
    if (query->id[n_id] == (char *)NULL) return -1;

    query->n_id++;

    return 0;
}

/*
*$ Name:
*$      McSTNQueryAddIDN - Adds this block number to list of IDN's in query
*$
*$ Interface:
*$      #include "mcidas.h"
*$      #include "stations.h"
*$
*$      int
*$      McSTNQueryAddIDN(STATION_QRY *query, int idn)
*$
*$ Input:
*$      idn     - IDN (Block number)  of the station to add to the query id list
*$
*$ Input and Output:
*$      query  - query structure 
*$
*$ Output:
*$      none
*$
*$ Return values:
*$      0      - OK
*$      -1     - memory allocation error
*$
*$ Remarks:
*$      Memory for the list of wmo block number is reallocated each
*$      time a new block number is added.
*$      It is up to the caller to free this memory.
*$
*$ Categories:
*$      system
*$      utility
*$
*$ Filename:
*$      stnqry.c
*/
int
McSTNQueryAddIDN (STATION_QRY *query, int idn)
{

    int         n_idn;

    n_idn = query->n_idn;
    if (n_idn == 0) query->idn = 0;

    query->idn = (int *)realloc (query->idn, (n_idn + 1) * sizeof (int));
    if (query->idn == (int *)NULL) return -1;

    query->idn[n_idn] = idn;
    query->n_idn++;

    return 0;
}

void
print_station (STATION *s)
{
#ifdef DEBUG_STATION

#endif
}

void
print_query (STATION_QRY *q)
{
#ifdef DEBUG_STATION

    int i;

    Mcprintf ("STATION_QRY\n");
    Mcprintf ("-----------\n");
    if (q->n_id)
    {
        Mcprintf ("  id: ");
        for (i = 0 ; i < q->n_id ; i++)
        {
            if (q->id) Mcprintf ("\"%s\",", q->id[i]);
        }
    }
    Mcprintf ("\n");
    if (q->n_idn)
    {
        Mcprintf ("  idn:");
        for (i = 0 ; i < q->n_idn ; i++)
        {
            Mcprintf (" %d", q->idn[i]);
        }
        Mcprintf ("\n");
    }
    Mcprintf ("  state: ");
    if (q->state) Mcprintf ("\"%s\"", q->state);
    Mcprintf ("\n");
    Mcprintf ("  country: ");
    if (q->country) Mcprintf ("\"%s\"", q->country);
    Mcprintf ("\n");
    Mcprintf ("  county: ");
    if (q->county) Mcprintf ("\"%s\"", q->county);
    Mcprintf ("\n");
    Mcprintf ("  match: ");
    if (q->match) Mcprintf ("\"%s\"", q->match);
    Mcprintf ("\n");
    Mcprintf ("  minlat: %d\n", q->minlat);
    Mcprintf ("  maxlat: %d\n", q->maxlat);
    Mcprintf ("  minlon: %d\n", q->minlon);
    Mcprintf ("  maxlon: %d\n", q->maxlon);
    Mcprintf ("  valid.bdate: %d\n", q->valid.bdate);
    Mcprintf ("  valid.edate: %d\n", q->valid.edate);
    Mcprintf ("  number of datatypes: %d\n", q->n_datatypes);
    for (i = 0 ; i < q->n_datatypes ; i++)
    {
        Mcprintf ("      %s\n", q->typenames[i]);
    }
#endif
}
--------------------------- stnqry.c -----------------------------------------

-------------------------- stations.h ----------------------------------------
/*
 * Copyright(c) 2000, Space Science and Engineering Center, UW-Madison
 * Refer to "McIDAS Software Acquisition and Distribution Policies"
 * in the file  mcidas/data/license.txt
 */

/**** $Id: stations.h,v 1.6 2000/03/28 21:06:06 chadj Tst $ ****/

#ifndef TRUE
#define TRUE    1
#define FALSE   0
#endif

#define         BOTIME          1970001         /* beginning of time */
#define         EOTIME          2100001         /* end of time */
#define         MINPRIORITY     0               /* minimum priority */
#define         MAXPRIORITY     0               /* maximum priority */

typedef struct _DATE {
        int             bdate;          /* beginning date */
        int             edate;          /* ending date */
} DATE;

/* The definition of a station */
typedef struct _STATION {
        char            *id;            /* character ID of station */
        char            *userid;        /* user defined character ID */
        int             idn;            /* WMO block number of station */
        char            *description;   /* verbal description (name) station */
        int             ARGOS;          /* ARGOS ID of station */
        int             lat;            /* latitude of station (hhmmss) */
        int             lon;            /* longitude of station (hhmmss) */
        int             ele;            /* elevation of station (m) */
        int             uplat;          /* 2nd latitude of station (hhmmss) */
        int             uplon;          /* 2nd longitude of station (hhmmss) */
        int             upele;          /* 2nd elevation of station (m) */
        DATE            valid;          /* valid dates for this station */
        char            *state;         /* PO of station for this station */
        char            *country;       /* PO of country for this station */
        char            *county;        /* name of county of station */
        int             WFO_zone;       /* WFO_zone number of station */
        char            *WSFO_office;   /* ID of WSFO office of station */
        char            *continent;     /* name of continent station resides */
        int             GMT_offset;     /* offset from GMT for this station */
        int             n_datatypes;    /* number datatypes of this station */
        char            **typenames;    /* name of datatypes of this station */
        int             n_comments;     /* number comments of this station */
        char            **comments;     /* comments of this station */
        unsigned long   classification; /* classification of this station */
        int             priority;       /* plotting priority of this station */

} STATION;

/* The definition of a query */
typedef struct _STATION_QRY {

        char            **id;           /* character ID */
        int             n_id;           /* number of character IDs */
        char            **userid;       /* user defined character ID */
        int             n_userid;       /* nubmer of user IDs */
        int             *idn;           /* WMO block number */
        int             n_idn;          /* number of WMO block numbers */
        char            *match;         /* string to match description */
        int             minlat;         /* minimum lattitude (hhmmss) */
        int             maxlat;         /* maximum lattitude (hhmmss) */
        int             minlon;         /* minimum longitude (hhhmmss) */
        int             maxlon;         /* maximum longitude (hhhmmss) */
        DATE            valid;          /* valid dates */
        char            *state;         /* PO of state */
        char            *country;       /* PO of country */
        char            *county;        /* name of county */
        int             n_datatypes;    /* number of data types */
        char            **typenames;    /* list of data type names */
        int             priority;       /* priority number */

} STATION_QRY;

/* 
 * Define our Fortran structs. These are the structures filled by the
 * Fortran jacket routines which are written in C. The common blocks for these
 * structures are defined in stations.inc. The structures themselves are
 * instantiated as a static global in stnqry.c. To get at the variables in these
 * structures a Fortran program must include stations.inc and then access the
 * fields in each of the structs below as variables in the program. Fields
 * from the F_STATION struct are prepended with "s_", and fields from the 
 * F_STATION_QRY struct are prepended with "q_". (station vs. query fields)
 *
 * Because Fortran requires us to specify fixed lengths for characters, we need
 * to set some constants
*/
#define F_IDLEN         12
#define F_COMLEN        80
#define F_TYPELEN       16
#define F_NAMELEN       80
#define F_MAXTYPES      64
#define F_MAXCOMMENTS   24
#define F_COMMENTLEN    80
#define F_STATELEN      4
#define F_COUNTRYLEN    4
#define F_COUNTYLEN     36
#define F_WSFOLEN       12
#define F_CONTLEN       24

typedef struct _F_STATION {
        char            s_id[F_IDLEN];
        char            s_userid[F_IDLEN];
        Fint            s_idn;
        char            s_desc[F_NAMELEN];
        Fint            s_argos;
        Fint            s_lat;
        Fint            s_lon;
        Fint            s_ele;
        Fint            s_uplat;
        Fint            s_uplon;
        Fint            s_upele;
        Fint            s_bdate;
        Fint            s_edate;
        char            s_state[F_STATELEN];
        char            s_country[F_COUNTRYLEN];
        char            s_county[F_COUNTYLEN];
        Fint            s_wfo_zone;
        char            s_wsfo_office[F_WSFOLEN];
        char            s_continent[F_CONTLEN];
        Fint            s_gmt_offset;
        Fint            s_n_datatypes;
        char            s_typenames[F_MAXTYPES][F_TYPELEN];
        Fint            s_n_comments;
        char            s_comments[F_MAXCOMMENTS][F_COMMENTLEN];
        Fint            s_classification;
        Fint            s_priority;
} F_STATION;

typedef struct _F_STATION_QRY {
        char            q_id[F_IDLEN];
        char            q_userid[F_IDLEN];
        Fint            q_idn;
        char            q_match[F_NAMELEN];
        Fint            q_minlat;
        Fint            q_maxlat;
        Fint            q_minlon;
        Fint            q_maxlon;
        Fint            q_mindate;
        Fint            q_maxdate;
        char            q_state[F_STATELEN];
        char            q_country[F_COUNTRYLEN];
        char            q_county[F_COUNTYLEN];
        Fint            q_n_datatypes;
        char            q_typenames[F_MAXTYPES][F_TYPELEN];
        Fint            q_priority;
} F_STATION_QRY;

typedef struct _STNTYPESINDEX {
        int             *n_list;
        int             **stn_list;
} STNTYPESINDEX;

/* structures into the binary file */
/* <<<<< UPC mod 20000710 - change long to Fint4 as binary file values
   are all 4-byte quantities >>>>> */

typedef Fint4           OFFSET;

typedef struct _STNHEADER {
        int             version;
        int             n_stations;
        int             n_types;
        OFFSET          types_blk;
        OFFSET          sizes_blk;
        OFFSET          data_offset_blk;
        OFFSET          index_info_blk;
} STNHEADER;

typedef struct _STNTYPES {
        int             n_types;
        char            **names;
} STNTYPESBLK;

typedef struct _STNSIZES {
        int             id;
        int             idn;
        int             argos;
        int             lat;
        int             lon;
        int             ele;
        int             uplat;
        int             uplon;
        int             upele;
        int             state;
        int             country;
        int             county;
        int             typename;
        int             description;
        int             date;
        int             comment;
} STNSIZES;

typedef struct _STNDATAOFFSET {
        OFFSET          id;
        OFFSET          idn;
        OFFSET          argos;
        OFFSET          lat;
        OFFSET          lon;
        OFFSET          ele;
        OFFSET          uplat;
        OFFSET          uplon;
        OFFSET          upele;
        OFFSET          state;
        OFFSET          country;
        OFFSET          county;
        OFFSET          description;
        OFFSET          valid_date;
        OFFSET          types;
        OFFSET          n_types;
        OFFSET          comments;
        OFFSET          n_comments;
} STNDATAOFFSET;

typedef struct _STNINDEXHEADER {
        OFFSET          type_index_offset;
} STNINDEXHEADER;

/* user callable prototypes */
int                     M0STNSetCoreFile (const char *file);
int                     M0STNSetSiteFile (const char *file);
int                     M0STNSetUserFile (const char *file);
int                     McSTNGetDatatypes (char ***names, int *num);
int                     McSTNOpenQuery (STATION_QRY *q_info);
int                     McSTNGetNumStations (int handle);
STATION *               McSTNGetNextStation (int handle);
int                     McSTNCloseQuery (int handle);
int                     McSTNSetQueryDefaults (STATION_QRY *query);
int                     McSTNQueryAddID (STATION_QRY *query, char *id);
int                     McSTNQueryAddIDN (STATION_QRY *query, int idn);
Fint                    mcstnopenquery_ (void);
Fint                    mcstnsetquerydefaults_ (void);
Fint                    mcstnclosequery_ (Fint *handle);
Fint                    mcstngetnextstation_ (Fint *handle);
Fint                    mcstngetnumstations_ (Fint *handle);
Fint                    idinfo_ (const char *id, Fint *idn, Fint *f5or6, 
                                 Fint *ewflg, Fint *llwflg, Fint *lat, 
                                 Fint *lon, Fint *state, Fint *country, 
                                 Fint *ele, FsLen id_len);
-------------------------- stations.h ----------------------------------------

Tom