From pds@fsl.noaa.gov  Fri Oct  1 22:19:49 1999
Received: from gatekeeper.fsl.noaa.gov (gatekeeper.fsl.noaa.gov [137.75.1.181])
	by brain.fsl.noaa.gov (8.8.5/8.8.5) with ESMTP id WAA02237
	for <albers@brain.fsl.noaa.gov>; Fri, 1 Oct 1999 22:19:49 GMT
Received: from dilbert.fsl.noaa.gov (pds@dilbert.fsl.noaa.gov [137.75.132.102])
	by gatekeeper.fsl.noaa.gov (8.9.1/8.9.1) with ESMTP id WAA26974
	for <albers@mail.fsl.noaa.gov>; Fri, 1 Oct 1999 22:19:49 GMT
Received: (from pds@localhost)
	by dilbert.fsl.noaa.gov (8.9.1/8.9.1) id WAA12301
	for albers; Fri, 1 Oct 1999 22:19:48 GMT
Date: Fri, 1 Oct 1999 22:19:48 GMT
From: PDS Librarian <pds@fsl.noaa.gov>
Message-Id: <199910012219.WAA12301@dilbert.fsl.noaa.gov>
Content-Type: text
Status: OR

/*------------------------------------------------------------------------------
**  NOAA/ERL
**  Forecast Systems Laboratory
**  Facility Division
**  Remote Sensor Systems Branch
**
**  This software and its documentation are in the public domain and are 
**  furnished "as is".  The United States government, its instrumentalities,
**  officers, employees, and agents make no warranty, express or implied, as to
**  the usefullness of the software and documentation for any purpose.  They 
**  assume no responsibility (1) for the use of the software and documentation;
**  or (2) to provide technical support to users.
**
**  getRdamData.c
**
**  This module contains the routines to search and read NIMBUS pool files
**  containing translator and data notifications, and RDAM data.
**
**  WARNING: This is OPAQUE Facility Division (FD) Software. All OPAQUE FD 
**  Software is subject to change without notice.
**
**  01/23/97    Dell Long    v1.1    Original version
**----------------------------------------------------------------------------*/
static char RcsId[] =  "$Id: getRdamData.c,v 1.1 1997/03/13 22:40:22 pds Exp $";

#include <wsr88dI2netCDF.h>
#include <notifyPoolMisc.h>
#include <InitDam.h>
#include <InitRdam.h>

/*----------------------------------------------------------------------------
**  getTriggerRadialData()
**  Purpose:  Reads the DAM/RDAM structure pointed to by the arguments.
**  Arguments:
**      poolPath - Path of the pool file.
**      poolPosition - Position of DAM data in the pool file.
**      poolLength - Length of the DAM data.
**      dam - Pointer to the DAM data read from the pool file.
** 
**  Returns:  EXIT_SUCCESS, EXIT_FAILURE
**--------------------------------------------------------------------------*/
int getTriggerRadialData(char *poolPath, long poolPosition, long poolLength, 
                DamDataStruct **dam)
    {
    char *record;
    char timeStr[9];
    int dbHandle, status;
    long damSize;
    time_t frameTime;
    struct tm timeStruct;
    NotifyArgs *poolInfo;
    RdamDataStruct *rdam;

    poolInfo = (NotifyArgs *) calloc(1, sizeof(NotifyArgs));
    if (poolInfo == (NotifyArgs *)NULL)
        {
        PANIC;
        }

    /*--------------------------------------------------------------------------
    ** Open and read the given database.
    **------------------------------------------------------------------------*/
    dbHandle = open(poolPath, O_RDONLY);
    if (dbHandle < 0)
        {
        PdsAdvise (Targ, PdsError, "ERROR: Could not open %s on %s!\n\n", 
                   poolPath, strerror(errno));
        return OS_FUNCTION_ERROR;
        }


    /*--------------------------------------------------------------------------
    ** Record pool information into the poolInfo (NotifyArgs) structure.
    **------------------------------------------------------------------------*/
    poolInfo->Var.Data.Pool.Path = poolPath;
    poolInfo->Var.Data.Pool.Pos = poolPosition;
    poolInfo->Var.Data.Pool.Length = poolLength;


    /*--------------------------------------------------------------------------
    ** Allocate a buffer to hold an appropriately sized record.
    **------------------------------------------------------------------------*/
    record = (char *) calloc(1, poolInfo->Var.Data.Pool.Length);
    if (record == (char *)NULL)
        {
        PANIC;
        }


    /*--------------------------------------------------------------------------
    ** Get the message content.
    **------------------------------------------------------------------------*/
    if (getPoolData(dbHandle, poolInfo, record) == EXIT_FAILURE)
        {
        free(record);
        PdsAdvise (Targ, PdsError, "Could not read pool data");
        return EXIT_FAILURE;
        }

    free(poolInfo);
    close(dbHandle);

    /*--------------------------------------------------------------------------
    ** XDR decode the RDAM data
    **------------------------------------------------------------------------*/
    status = SerializeDamDataStruct(*dam, damSize, SER_DECODE, &record, 
                                    (long *) &poolLength);
    if (status != EXIT_SUCCESS)
        {
        free(record);
        PdsAdvise(Targ, PdsError, "Could not decode data");
        return EXIT_FAILURE;
        }

    rdam = (RdamDataStruct *)(*dam)->classInstance;

    /*--------------------------------------------------------------------------
    ** Make sure that this is indeed the last ray of an elevation or volume
    ** scan message by checking the message type and (if it _is_ a ray) the
    ** ray type.
    **------------------------------------------------------------------------*/
    if (rdam->data.hdr.msg_type == WSR88D_DRD)
        {
        if (rdam->data.msg.drd->ray_status != END_ELEVATION 
            &&rdam->data.msg.drd->ray_status != BAD_END_ELEVATION
            && rdam->data.msg.drd->ray_status != END_VOLUME_SCAN
            && rdam->data.msg.drd->ray_status != BAD_END_VOLUME_SCAN)
            {        
            free(record);
            PdsAdvise(Targ, PdsError, "Triggering RDAM message is radial data, "
                      "but not the last ray of a sweep");
            return EXIT_FAILURE;
            }
        }
    else
        {
        free(record);
        PdsAdvise(Targ, PdsError, "Triggering RDAM message is not radial data");
        return EXIT_FAILURE;
        }

    free(record);

    return EXIT_SUCCESS;
    } /* getTriggerRadialData() */

/*----------------------------------------------------------------------------
**  findXltNtyDBs()
**  Purpose:  Finds translator notifications matching the search criteria 
**            given by the arguments.
**  Arguments:
**      xltNtyList - List of matching translator notifications.
**      xltNtyPath - Path of where to find translator notification databases.
**      xltNtyKey - Regular expression of matching translator key.
**      startTime - Start Time of the sweep in which we are interested.
**      elevation - Elevation number of the sweep in which we are interested.
**      numValidXltNty - Number of matching translator notifications.
** 
**  Returns:  EXIT_SUCCESS, EXIT_FAILURE
**--------------------------------------------------------------------------*/
int findXltNtyDBs(NotifyArgs ***xltNtyList, char *xltNtyPath, char *xltNtyKey,
                  time_t startTime, int elevation, short *numValidXltNty)
    {
    int i, status;
    char **xltNtyDbList;
    char *record;
    int numDbFiles;
    PdsDataKey **ntyKeyList, *tempKey;
    int dbIndex, keyIndex;
    u_long dbHandle;
    u_long maxRecLen, oldMaxRecLen = 0;
    u_long maxDbKeys;
    time_t curElevStartTime;
    time_t tempTime = 0;
    int numNtyKeyMatches = 0;
    NotifyArgs *curNty;
    DescripTypes type;
    int *curElevation = NULL;

    xltNtyDbList = (char **) calloc(2, sizeof(char **));
    if (xltNtyDbList == (char **)NULL)
        PANIC;

    record = (char *) calloc(1, 1);
    if (record == NULL)
        PANIC;

    *numValidXltNty = 0;

    if ((numDbFiles = findNotifyDBs(xltNtyPath, startTime, &xltNtyDbList)) == 0)
        {
        PdsAdvise(Targ, PdsError, "No pool files were found");
        free(xltNtyDbList);
        free(record);

        return EXIT_FAILURE;
        }

    /*--------------------------------------------------------------------------
    ** Search for and record valid translator notifications.
    **------------------------------------------------------------------------*/
    if (Verbose == TRUE)
        {
        PdsAdvise(Targ, PdsUp, "Going into loop to search for valid "
                  "translator notifications.\n");
        }

    /*--------------------------------------------------------------------------
    ** Prime ntyKeyList for future realloc's.
    **------------------------------------------------------------------------*/
    ntyKeyList = (PdsDataKey **) calloc(1, sizeof(PdsDataKey *));
    if (ntyKeyList == (PdsDataKey **) NULL)
        PANIC;

    /*--------------------------------------------------------------------------
    ** Loop to examine the translator notification database list. (In 
    ** reality this is no longer a list).
    **------------------------------------------------------------------------*/
    for (dbIndex = 0; xltNtyDbList[dbIndex] != (char *)NULL; dbIndex++)
        {
        /*----------------------------------------------------------------------
        ** open a database for search
        **--------------------------------------------------------------------*/
        if (PdsOpenDtb(xltNtyDbList[dbIndex], &dbHandle, Readonly) != 0)
            {
            PdsAdvise(Targ, PdsError, "ERROR while opening %s -- %s", 
                      xltNtyDbList[dbIndex], strerror(errno));

            for (dbIndex = 0; xltNtyDbList[dbIndex] != (char *)NULL; dbIndex++)
                {
                free(xltNtyDbList[dbIndex]);
                }
            free(xltNtyDbList);
            free(record);
            free(ntyKeyList);

            return EXIT_FAILURE;
            }
        else
            {
            if (Verbose)
                PdsAdvise(Targ, PdsUp, "Examining translator notifications in %s",
                          xltNtyDbList[dbIndex]);
            }

         /*----------------------------------------------------------------------
        ** Get the max data record length in this database
        **--------------------------------------------------------------------*/
        if (MaxDataLen(dbHandle, &maxRecLen) != EXIT_SUCCESS)
            {
            PdsAdvise(Targ, PdsError, "Can't get maximum record length");

            for (dbIndex = 0; xltNtyDbList[dbIndex] != (char *)NULL; dbIndex++)
                free(xltNtyDbList[dbIndex]);

            free(xltNtyDbList);

            free(record);
            free(ntyKeyList);

            return EXIT_FAILURE;
            }

        /*----------------------------------------------------------------------
        ** Allocate a buffer to hold the max record
        **----------------------------------------------------------------*/
        if (oldMaxRecLen != maxRecLen)
            {
            record = (char *) realloc(record, (size_t)maxRecLen + 1);
            if (record == (char *)NULL)
                {
                PANIC;
                }

            oldMaxRecLen = maxRecLen;
            }


        /*----------------------------------------------------------------------
        ** Get the number of keys in this database.
        **--------------------------------------------------------------------*/
        if (NumDtbKeys(dbHandle, &maxDbKeys) != EXIT_SUCCESS)
            {
            PdsAdvise(Targ, PdsError, "Could not get the number of data keys");

            for (dbIndex = 0; xltNtyDbList[dbIndex] != (char *)NULL; dbIndex++)
                {
                free(xltNtyDbList[dbIndex]);
                }
            free(xltNtyDbList);

            free(record);
            free(ntyKeyList);

            return EXIT_FAILURE;
            }

        ntyKeyList = (PdsDataKey **) realloc(ntyKeyList, 
                                             maxDbKeys * sizeof(PdsDataKey *));
        if (ntyKeyList == (PdsDataKey **) NULL)
            {
            PANIC;
            }

        /*----------------------------------------------------------------------
        ** Loop through the keys finding ones that are of interest (match the 
        ** target). No sense reading & decoding data we aren't interested in.
        **--------------------------------------------------------------------*/
        if (Verbose == TRUE)
            PdsAdvise(Targ, PdsUp, "Looping through the keys and finding ones "
                      "that are of interest.\n");

        for (i = 0; i < maxDbKeys; i++)
            {
            /*----------------------------------------------------------------------
            ** Get the next key
            **--------------------------------------------------------------------*/
            if (GetKey(dbHandle, i, &tempKey) != 0) 
                break;

            if (matchPdsDataKey(xltNtyKey, tempKey) == 0)
                ntyKeyList[numNtyKeyMatches++] = tempKey;
#ifdef DEBUG
            else
                {
                PdsAdvise(Targ, PdsUp, "no match: %s", 
                          ((char *) tempKey) + sizeof(PdsDataKey));
                PdsAdvise(Targ, PdsUp, "Match attempts: %d      Matches: "
                          "%d", i + 1, numNtyKeyMatches);
                }
#endif
            }

        if (Verbose == TRUE)
            {
            PdsAdvise(Targ, PdsUp, "Found %d matches.", numNtyKeyMatches);
            PdsAdvise(Targ, PdsUp, "Loop through the keys that are "
                      "interesting based on their matching the target.\n");
            }
        for (keyIndex = 0; keyIndex < numNtyKeyMatches; keyIndex++)
            {
            /*------------------------------------------------------------------
            ** Get the message content
            **----------------------------------------------------------------*/
            status = GetData(dbHandle, ntyKeyList[keyIndex], record);
            if (status != EXIT_SUCCESS)
                {
                PdsAdvise(Targ, PdsError, 
                          "ERROR: Could not read data in function GetData.");

                for (dbIndex = 0; xltNtyDbList[dbIndex] != (char *)NULL; 
                     dbIndex++)
                    {
                    free(xltNtyDbList[dbIndex]);
                    }
                free(xltNtyDbList);

                for (i = 0; i < numNtyKeyMatches; i++)
                    free(ntyKeyList[i]);

                free(ntyKeyList);
                free(record);

                return EXIT_FAILURE;
                }

            curNty = (NotifyArgs *) calloc(1, sizeof(NotifyArgs));
            if (curNty == (NotifyArgs *)NULL)
                {
                PANIC;
                }

            /*------------------------------------------------------------------
            ** XDR decode the notification message.
            **----------------------------------------------------------------*/
            (void)DecodeNotify(record, curNty);

#ifdef DEBUG
            PdsAdvise(Targ, PdsUp, "Examining notification %s...", 
                      curNty->Var.Xlt.xltKey);
#endif

            /*------------------------------------------------------------------
            ** Confirm that this is a translator notification. If not then try 
            ** the next one.
            **------------------------------------------------------------*/
            if (curNty->Notify.Type != XLT_NOTIFY)
                {
                PdsAdvise(Targ, PdsError, "Config error. File %s contains "
                          "non-translator notifications.\n", 
                          xltNtyDbList[dbIndex]);

                free(curNty);
                continue;
                }

            /*------------------------------------------------------------------
            ** Confirm that the elevation start time is correct. If not try next.
            **----------------------------------------------------------------*/
            curElevStartTime = GetDataTime("Elevation Start Time", 
                                                &curNty->Var.Xlt.dataTimes);

            if (curElevStartTime != startTime)
                {
                if (tempTime != curElevStartTime)
                    tempTime = curElevStartTime;

                free(curNty);
                continue;
                }

            /*------------------------------------------------------------------
            ** Confirm that the ray is in the correct elevation.
            ** If it isn't then try the next one.
            **----------------------------------------------------------------*/
            type = Int;
            status = GetProductDescription("Elevation Number", &type,
                                           (char **) &curElevation, 
                                           &curNty->Var.Xlt.prodDesc);
            if (status == EXIT_SUCCESS)
                {
                if (*curElevation != elevation)
                    {
                    free(curNty);
                    continue;
                    }
                }
            else
                {
#ifdef DEBUG
                PdsAdvise(Targ, PdsError, "Product description %s not found in "
                          "XltNotify.", "Elevation Number");
#endif
                free(curNty);
                continue;
                }

            /*------------------------------------------------------------------
            ** This notification meets all acceptance criteria.
            **----------------------------------------------------------------*/
            (*numValidXltNty)++;

#ifdef DEBUG
            PdsAdvise(Targ, PdsUp, "Adding translator notification %02d -- %s "
                      "to list...", *numValidXltNty, curNty->Var.Xlt.xltKey);
#endif
            *xltNtyList = (NotifyArgs **) realloc(*xltNtyList, *numValidXltNty *
                                                  sizeof(NotifyArgs *));
            if (*xltNtyList == (NotifyArgs **) NULL)
                {
                PANIC;
                }

            (*xltNtyList)[*numValidXltNty - 1] = curNty;
            }

        /*----------------------------------------------------------------------
        ** Clean up for next database.
        **--------------------------------------------------------------------*/
        numNtyKeyMatches = 0;
            
        PdsCloseDtb(dbHandle);
        }/* end of loop through database files */

    if (*numValidXltNty == 0)
        {
        for (dbIndex = 0; xltNtyDbList[dbIndex] != (char *)NULL; dbIndex++)
            {
            free(xltNtyDbList[dbIndex]);
            }
        free(xltNtyDbList);

        free(record);

        for (i = 0; i < numNtyKeyMatches; i++)
            {
            free(ntyKeyList[i]);
            }
        free(ntyKeyList);

        return EXIT_SUCCESS;
        }

    /*--------------------------------------------------------------------------
    ** Sort the xltNtyList to speed up matching against data arrival.
    ** notifications.
    ** (realloc necessary because qsort needs a slot to shuffle stuff in)
    **------------------------------------------------------------------------*/
    *xltNtyList = (NotifyArgs **) realloc(*xltNtyList,((*numValidXltNty + 1) *
                                                         sizeof(NotifyArgs *)));
    if (*xltNtyList == (NotifyArgs **) NULL)
        {
        PANIC;
        }

    /*--------------------------------------------------------------------------
    ** Sort by translated key.
    **------------------------------------------------------------------------*/
    qsort((char *) *xltNtyList, *numValidXltNty, sizeof(NotifyArgs *),
          compareXltNotifications);

    /*--------------------------------------------------------------------------
    ** Free xltNtyDbList, record, and ntyKeyList.
    **------------------------------------------------------------------------*/
    for (dbIndex = 0; xltNtyDbList[dbIndex] != (char *)NULL; dbIndex++)
        {
        free(xltNtyDbList[dbIndex]);
        }
    free(xltNtyDbList);

    for (i = 0; i < numNtyKeyMatches; i++)
        {
        free(ntyKeyList[i]);
        }
    free(ntyKeyList);

    free(record);

    return EXIT_SUCCESS;
    } /* findXltNtyDBs() */

/*----------------------------------------------------------------------------
**  findDataNtyDBs()
**  Purpose:  Finds data arrival notifications whose keys exactly match the 
**            one of the valid translator notification keys.
**  Arguments:
**      startTime - Start time of the sweep in which we are interested.
**      xltNtyList - List of valid translator notifications to match against.
**      dataNtyList - List of matching data arrival notifications
**      dataNtyKey - Regular expression of a candidate data arrival key.
**      numValidXltNty - Number of translator notifications in xltNtyList.
**      numValidDataNty - Number of data arrival notifications in dataNtyList. 
**  Returns:  EXIT_SUCCESS, EXIT_FAILURE
**--------------------------------------------------------------------------*/
int findDataNtyDBs(time_t startTime, NotifyArgs **xltNtyList, 
                   NotifyArgs ***dataNtyList, char *dataNtyPath, char *dataNtyKey,
                   short numValidXltNty, short *numValidDataNty)
    {
    char *record;
    char **dataNtyDbList;
    int status, n, i;
    int dbIndex, keyIndex;
    int numNtyKeyMatches = 0;
    u_long dbHandle;
    u_long maxRecLen, oldMaxRecLen = 0;
    u_long maxDbKeys;
    PdsDataKey *tempKey;
    PdsDataKey **ntyKeyList;
    NotifyArgs *curNty;


    /*--------------------------------------------------------------------------
    ** Make sure that the number of valid data arrival notifications 
    ** (numValidDataNty) has been re-set to zero.
    **------------------------------------------------------------------------*/
    *numValidDataNty = 0;

    /*--------------------------------------------------------------------------
    ** Prime data notification database list (dataNtyDbList) and record.
    **------------------------------------------------------------------------*/
    dataNtyDbList = (char **) calloc(1, 1);
    if (dataNtyDbList == (char **)NULL)
        {
        PANIC;
        }

    record = (char *) calloc(1, 1);
    if (record == NULL)
        {
        PANIC;
        }


    /*--------------------------------------------------------------------------
    ** Find data notification databases.
    **------------------------------------------------------------------------*/
    status = findNotifyDBs(dataNtyPath, startTime, &dataNtyDbList);
    if (status == 0)
        {
        PdsAdvise(Targ, PdsError, "No pool files found in Data Arrival "
                  "notification path");

        free(dataNtyDbList);
        free(record);
        return EXIT_FAILURE;
        }


    /*--------------------------------------------------------------------------
    ** Search for and record valid data notifications.
    **------------------------------------------------------------------------*/
    if (Verbose == TRUE)
        {
        PdsAdvise(Targ, PdsUp, "Search for and record valid data "
                  "notifications.\n");
        }

    for (dbIndex = 0; dataNtyDbList[dbIndex] != (char *)NULL; dbIndex++)
        {
        /*----------------------------------------------------------------------
        ** Open a database for search.
        **--------------------------------------------------------------------*/
        status = PdsOpenDtb(dataNtyDbList[dbIndex], &dbHandle, Readonly);
        if (status != 0)
            {
            PdsAdvise(Targ, PdsError, "ERROR while opening %s -- %s\n", 
                      dataNtyDbList[dbIndex], strerror(errno));

            for (dbIndex = 0; dataNtyDbList[dbIndex] != (char *)NULL; dbIndex++)
                {
                free(dataNtyDbList[dbIndex]);
                }
            free(dataNtyDbList);

            free(record);
            return EXIT_FAILURE;
            }
#ifdef DEBUG
        else
            {
            PdsAdvise(Targ, PdsUp, "Examining data arrival notifications "
                      "in %s", dataNtyDbList[dbIndex]);
            }
#endif

        /*----------------------------------------------------------------------
        ** Get the max data record length in this database.
        **--------------------------------------------------------------------*/
        status = MaxDataLen(dbHandle, &maxRecLen);
        if (status != EXIT_SUCCESS)
            {
            PdsAdvise(Targ, PdsError, "ERROR: Can't get maximum data length "
                      "in function findDataNtyDBs");

            for (n = 0; dataNtyDbList[n] != (char *)NULL; n++)
                {
                free(dataNtyDbList[n]);
                }
            free(dataNtyDbList);

            free(record);
            return EXIT_FAILURE;
            }

        /*----------------------------------------------------------------------
        ** Allocate a buffer to hold the max record.
        **--------------------------------------------------------------------*/
        if (oldMaxRecLen != maxRecLen)
            {
            record = (char *) realloc(record, (size_t)maxRecLen + 1);
            if (record == (char *)NULL)
                {
                PANIC;
                }

            oldMaxRecLen = maxRecLen;
            }


        /*----------------------------------------------------------------------
        ** Get the number of keys in this database.
        **--------------------------------------------------------------------*/
        status = NumDtbKeys(dbHandle, &maxDbKeys);
        if (status != EXIT_SUCCESS)
            {
            PdsAdvise(Targ, PdsError, "ERROR: Cound not get the number of "
                      "data base keys in function findDataNtyDBs");

            for (n = 0; dataNtyDbList[n] != (char *)NULL; n++)
                {
                free(dataNtyDbList[n]);
                }
            free(dataNtyDbList);

            free(record);
            return EXIT_FAILURE;
            }

        ntyKeyList = (PdsDataKey **)calloc(maxDbKeys, sizeof(PdsDataKey *));
        if (ntyKeyList == (PdsDataKey **) NULL)
            {
            PANIC;
            }


        /*----------------------------------------------------------------------
        ** Loop through the keys finding ones that are of interest.
        ** No sense reading & decoding data we aren't interested in.
        **--------------------------------------------------------------------*/
        if (Verbose == TRUE)
            {
            PdsAdvise(Targ, PdsUp, "Loop through the keys finding ones that "
                      "are of interest.\n");
            }


        for (i = 0; i < maxDbKeys; i++)
            {
            /*----------------------------------------------------------------------
            ** Get the next key
            **--------------------------------------------------------------------*/
            if (GetKey(dbHandle, i, &tempKey) != 0) 
                break;

            if (matchPdsDataKey(dataNtyKey, tempKey) == 0)
                ntyKeyList[numNtyKeyMatches++] = tempKey;
            else
                {
                PdsAdvise(Targ, PdsUp, "Match attempts: %d      Matches: "
                          "%d", i + 1, numNtyKeyMatches);
                }
            }

        /*----------------------------------------------------------------------
        ** Loop through keys that are interesting based on their matching the 
        ** target. Read and decode their associated data. If the frame start 
        ** time does not match the one given on the command line or if the 
        ** scan line is not within the specified range then eliminate the key 
        ** from the key list -- i.e. we are not actually interested in it.
        **--------------------------------------------------------------------*/
        if (Verbose == TRUE)
            {
            PdsAdvise(Targ, PdsUp, "Loop through keys that are interesting "
                      "based on their matching the target.\n");
            }

        for (keyIndex = 0; keyIndex < numNtyKeyMatches; keyIndex++)
            {
            /*------------------------------------------------------------------
            ** Get the message content.
            **----------------------------------------------------------------*/
            status = GetData(dbHandle, ntyKeyList[keyIndex], record);
            if (status != EXIT_SUCCESS)
                {
                PdsAdvise(Targ, PdsError, "ERROR while reading reading data "
                          "in function GetData");

                for (n = 0; dataNtyDbList[n] != (char *)NULL; n++)
                    {
                    free(dataNtyDbList[n]);
                    }
                free(dataNtyDbList);

                for (n = 0; n < numNtyKeyMatches; ++n)
                    {
                    free(ntyKeyList[n]);
                    }
                free(ntyKeyList);

                free(record);
                return EXIT_FAILURE;
                }

            curNty = (NotifyArgs *) calloc(1, sizeof(NotifyArgs));
            if (curNty == (NotifyArgs *)NULL)
                {
                PANIC;
                }

            /*------------------------------------------------------------------
            ** XDR decode the data arrival notification message.
            **----------------------------------------------------------------*/
            (void)DecodeNotify(record, curNty);

            /*------------------------------------------------------------------
            ** Confirm that this is a data arrival notification. If not try 
            ** next.
            **----------------------------------------------------------------*/
            if (curNty->Notify.Type != DATA_NOTIFY)
                {
                PdsAdvise(Targ, PdsError, "Config error. File %s contains "
                          "non-data notifications.", 
                          dataNtyDbList[dbIndex]);

                free(curNty);
                continue;
                }

            /*------------------------------------------------------------------
            ** Confirm that the contents _exactly_ matches one of the 
            ** translated keys...Translator notification keys must be 
            ** sorted.
            **----------------------------------------------------------------*/
            status = -1;

#ifdef DEBUG
            PdsAdvise(Targ, PdsUp, "Checking: %s", 
                      ((NotifyArgs *) curNty)->Var.Data.Tgt.key);
#endif

            if (bsearch(curNty, xltNtyList, numValidXltNty, 
                        sizeof(NotifyArgs *), compareData2XltNotifies) == NULL)
                {
                free(curNty);
                continue;
                }

            /*------------------------------------------------------------------
            ** This notification meets all acceptance criteria.
            **----------------------------------------------------------------*/
            (*numValidDataNty)++;

#ifdef DEBUG
            PdsAdvise(Targ, PdsUp, "Adding notification %02d -- %s to list...",
                      *numValidDataNty, curNty->Var.Data.Tgt.key);
#endif

            *dataNtyList = (NotifyArgs **) realloc(*dataNtyList, 
                                                   *numValidDataNty *
                                                   sizeof(NotifyArgs *));
            if (*dataNtyList == (NotifyArgs **)NULL)
                {
                PANIC;
                }

            (*dataNtyList)[*numValidDataNty - 1] = curNty;
            } /* end of for loop using numNtyKeyMatches */

        /*----------------------------------------------------------------------
        ** Clean up for next database.
        **--------------------------------------------------------------------*/
        free(ntyKeyList);
            
        numNtyKeyMatches = 0;
        
        PdsCloseDtb(dbHandle);

        } /* end of for loop using dbIndex */


    /*--------------------------------------------------------------------------
    ** Free dataNtyDbList and record arrays.
    **------------------------------------------------------------------------*/
    for (dbIndex = 0; dataNtyDbList[dbIndex] != (char *)NULL; dbIndex++)
        {
        free(dataNtyDbList[dbIndex]);
        }
    free(dataNtyDbList);

    free(record);

    if (*numValidDataNty == 0)
        {
        free(*dataNtyList);
        }

    return EXIT_SUCCESS;
    } /* findDataNtyDBs() */