/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF.  The full HDF copyright notice, including       *
 * terms governing use, modification, and redistribution, is contained in    *
 * the COPYING file, which can be found at the root of the source code       *
 * distribution tree, or in https://support.hdfgroup.org/ftp/HDF/releases/.  *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/****************************************************************************
 * tattdatainfo.c - tests the functions SDgetattdatainfo, SDgetoldattdatainfo,
 *		    and SDgetanndatainfo.
 * Structure of the file:
 *    test_attdatainfo - test driver
 *	test_attrs     - tests getting SDS attribute's data info
 *	test_annots    - tests getting SDS annotation's data info
 *	test_dfsdattrs - tests getting data info of old attributes added by
 *			 DFSD API
 *
 ****************************************************************************/

#include <stdlib.h>
#include <string.h>

#include "mfhdf.h"
#include "hdftest.h"

#ifdef H4_HAVE_SYS_TYPES_H
#include <sys/types.h> /* Windows off_t */
#endif

static intn test_attrs();
static intn test_dfannots();
static intn test_dfsdattrs();

/* Reads character string data from HDF file without the use of HDF4 library */
intn readnoHDF_char(const char *filename, const int32 offset, const int32 length, const char *orig_buf);

#define ATTR_FILE "attdatainfo.hdf" /* data file */
#define X_LENGTH  10
#define Y_LENGTH  10
#define RANK      2
#define ISFIRST   (int)1
#define NOTFIRST  (int)0

/****************************************************************************
   Name: test_attrs() - tests getting attribute's data info

   Description:
        This routine creates and writes data to non-special SDSs and verifies
        data and data information with SDgetattdatainfo.  The tests include
        the following SDSs:
        - a 2-dim 5x8 element SDS, float32, with no data
        - a 1-dim 10-element SDS, int32, with 10 values
        - a 2-dim 5x8 element SDS, float32, with 5x8 values
        - a 1-dim 20-element SDS, char, with 20 values
        SDgetattdatainfo will retrieve the number of blocks in the data sets
        and the offsets and lengths of the blocks.  Then the data will be read
        back from the file at the previously retrieved offsets/lengths, without
        the use of the HDF4 library, and will be verified against the original
        data buffers.

 ****************************************************************************/
#define SDS1_NAME      "First SDS"
#define SDS2_NAME      "Second SDS"
#define RANK1          1
#define RANK2          2
#define LENGTH1_X      10
#define LENGTH2_X      5
#define LENGTH2_Y      8
#define LENGTH3_X      21
#define FILE_ATTR_NAME "File_contents"
#define SDS_ATTR_NAME1 "Valid_range 1"
#define SDS_ATTR_NAME2 "Valid_range 2"
#define DIM_ATTR_NAME  "Dim_metric"

static intn
test_attrs()
{
    int32   sd_id, sds_id, dim_id, dim_idx, att_idx;
    int32   dimsizes[2], starts[2], edges[2];
    int32   data1[LENGTH1_X];
    float   data2[LENGTH2_X][LENGTH2_Y];
    char    sds_name[20];
    int32   n_values, nattrs;
    int32   offset = 0, length = 0;
    char8   file_values[] = "Storm_track_data";
    float32 sds_values[2] = {2., 10.};
    char8   dim_values[]  = "Seconds";
    intn    status;
    int     ii, jj;
    intn    num_errs = 0; /* number of errors so far */

    /* Create the file and initialize the SD interface */
    sd_id = SDstart(ATTR_FILE, DFACC_CREATE);
    CHECK(sd_id, FAIL, "test_attrs: SDstart");

    /* Set an attribute that describes the file contents. */
    n_values = 16;
    status   = SDsetattr(sd_id, FILE_ATTR_NAME, DFNT_CHAR8, n_values, (void *)file_values);
    CHECK(status, FAIL, "SDsetattr")

    /***************************************************************
     Create and write non-special SDSs
     ***************************************************************/

    /* Create a 2x2 data set called "EmptyDataset" */
    dimsizes[0] = LENGTH2_X;
    dimsizes[1] = LENGTH2_Y;

    /*
     * Create a 1-dim 10-element SDS, type int32, then write 10 values
     * to it
     */
    dimsizes[0] = LENGTH1_X;
    sds_id      = SDcreate(sd_id, SDS1_NAME, DFNT_INT32, RANK1, dimsizes);
    CHECK(sds_id, FAIL, "SDcreate");

    for (ii = 0; ii < LENGTH1_X; ii++)
        data1[ii] = 1000 * ii;

    starts[0] = 0;
    edges[0]  = LENGTH1_X;
    status    = SDwritedata(sds_id, starts, NULL, edges, (void *)data1);
    CHECK(status, FAIL, "SDwritedata");

    status = SDendaccess(sds_id);
    CHECK(status, FAIL, "SDendaccess");

    /* Assign two attributes to the first SDS. */
    n_values = 2;
    status   = SDsetattr(sds_id, SDS_ATTR_NAME1, DFNT_FLOAT32, n_values, (void *)sds_values);
    CHECK(status, FAIL, "SDsetattr")
    n_values = 7;
    status   = SDsetattr(sds_id, SDS_ATTR_NAME2, DFNT_CHAR8, n_values, (void *)dim_values);
    CHECK(status, FAIL, "SDsetattr")

    /*
     * Create a 2-dim 5x8 element SDS, type float32, then write 5x8 values
     * to it
     */
    dimsizes[0] = LENGTH2_X;
    dimsizes[1] = LENGTH2_Y;
    sds_id      = SDcreate(sd_id, SDS2_NAME, DFNT_FLOAT32, RANK2, dimsizes);
    CHECK(sds_id, FAIL, "SDcreate");

    for (ii = 0; ii < LENGTH2_X; ii++)
        for (jj = 0; jj < LENGTH2_Y; jj++)
            data2[ii][jj] = 500.50 * (ii + jj);

    starts[0] = 0;
    starts[1] = 0;
    edges[0]  = LENGTH2_X;
    edges[1]  = LENGTH2_Y;
    status    = SDwritedata(sds_id, starts, NULL, edges, (void *)data2);
    CHECK(status, FAIL, "SDwritedata");

    /* Get the the second dimension identifier of the SDS. */
    dim_idx = 1;
    dim_id  = SDgetdimid(sds_id, dim_idx);
    CHECK(dim_id, FAIL, "SDgetdimid")

    /* Set an attribute of the dimension that specifies the dimension metric. */
    n_values = 7;
    status   = SDsetattr(dim_id, DIM_ATTR_NAME, DFNT_CHAR8, n_values, (void *)dim_values);
    CHECK(status, FAIL, "SDsetattr")

    status = SDendaccess(sds_id);
    CHECK(status, FAIL, "SDendaccess");

    status = SDend(sd_id);
    CHECK(status, FAIL, "SDend");

    /***********************************************************************
     Read data info for later accessing data without the use of HDF4 library
     ***********************************************************************/

    /* Re-open the file */
    sd_id = SDstart(ATTR_FILE, DFACC_RDWR);
    CHECK(sd_id, FAIL, "test_attrs: SDstart");

    /* Open the second data set, verify that number of data block is 1, then
        retrieve and record the offset/length */
    sds_id = SDselect(sd_id, 0);
    CHECK(sds_id, FAIL, "test_attrs: SDselect SDS index 1");

    /* Get SDS' rank */
    status = SDgetinfo(sds_id, sds_name, NULL, NULL, NULL, &nattrs);
    CHECK(status, FAIL, "test_attrs: SDgetinfo SDS index 0");
    VERIFY(nattrs, 2, "test_attrs: SDgetinfo SDS index 0");

    for (att_idx = 0; att_idx < nattrs; att_idx++) {
        status = SDgetattdatainfo(sds_id, att_idx, &offset, &length);
        CHECK(status, FAIL, "test_attrs: SDgetattdatainfo");
    }

    status = SDendaccess(sds_id);
    CHECK(status, FAIL, "SDendaccess");

    status = SDend(sd_id);
    CHECK(status, FAIL, "SDend");

    /******************************************************************
     Read data using previously obtained data info without HDF4 library
     ******************************************************************/

    /* Return the number of errors that's been kept track of so far */
    return num_errs;
} /* test_attrs */

/***************************************************************************
   Test getting data info of annotations from DFAN
   There are several utility functions to help generating and verifying the
   data.  The actual test function is test_dfannots() that follows those.
***************************************************************************/
#define DFAN_NDG_FILE "tdfanndg.hdf"
#define DFAN_SDG_FILE "tdfansdg.hdf" /* only for SDG annotation tests */
#define MAXLEN_LAB    50
#define MAXLEN_DESC   200
#define ROWS          10
#define COLS          10
#define REPS          2

/* Utility functions: */
/* to generate data set's data */
static void gen2Dfloat(int height, int width, float *data);
/* to verify data of labels and descriptions */
static intn check_lab_desc(char *fname, uint16 tag, uint16 ref, char *label, char *desc);

/****************************************************************
**
**  gen2Dfloat:  generate 2-D data array
**
****************************************************************/
static void
gen2Dfloat(int height, int width, float *data)
{
    int    i, j;
    float *pdata;

    /* store one value per row, increasing by one for each row */
    pdata = data;
    for (i = 0; i < height; i++)
        for (j = 0; j < width; j++)
            *pdata++ = (float)(i + 1);
}

/****************************************************************
**
**  check_lab_desc:  read and compare label and description
**                   with expected ones
**
****************************************************************/
static intn
check_lab_desc(char *fname, uint16 tag, uint16 ref, char *label, char *desc)
{
    int32 inlablen, indesclen, ret;
    char  inlabel[MAXLEN_LAB], *indesc;
    intn  num_errs = 0;

    inlablen = DFANgetlablen(fname, tag, ref);
    CHECK(inlablen, FAIL, "check_lab_desc: DFANgetlablen");

    ret = DFANgetlabel(fname, tag, ref, inlabel, MAXLEN_LAB);
    CHECK(ret, FAIL, "check_lab_desc: DFANgetlabel");
    VERIFY_CHAR(inlabel, label, "check_lab_desc: DFANgetlabel");

    indesclen = DFANgetdesclen(fname, tag, ref);
    CHECK(indesclen, FAIL, "check_lab_desc: DFANgetdesclen");
    if (indesclen >= 0) {
        indesc = (char *)malloc(indesclen + 1);
        ret    = DFANgetdesc(fname, tag, ref, indesc, MAXLEN_DESC);
        CHECK(ret, FAIL, "check_lab_desc: DFANgetdesc");

        indesc[indesclen] = '\0';
        VERIFY_CHAR(indesc, desc, "check_lab_desc: DFANgetdesc");
        free(indesc);
    }
    return (num_errs);
}

/****************************************************************
**
**  add_sdfile_annotations: Adds file labels and descriptions with
**	DFAN API, then reads them back and verifies that they are
**      written correctly.  This function also creates the file.
**
****************************************************************/
intn
add_sdfile_annotations()
{
    int32 file_id;
    char  labels[2][MAXLEN_LAB], descs[2][MAXLEN_DESC], tempstr[MAXLEN_DESC];
    intn  ret;
    intn  num_errs = 0;

    /* set up file labels and descriptions */

    strcpy(labels[0], "File Label #1");
    strcpy(labels[1], "File Label #2");
    strcpy(descs[0], "File Descr #1: This is a file label, added\n");
    strcat(descs[0], "       by the DFAN interface...**END SDS 1 DESCR**\n");
    strcpy(descs[1], "File Descr #2: This is another file label added\n");
    strcat(descs[1], "       by the DFAN API as well.**END SDS 2 DESCR**\n");

    /********  Write file labels and descriptions *********/

    file_id = Hopen(DFAN_NDG_FILE, DFACC_CREATE, 0);
    if (file_id == FAIL)
        printf("\nUnable to open file %s for writing.\n\n", DFAN_NDG_FILE);

    ret = DFANaddfid(file_id, labels[0]);
    CHECK(ret, FAIL, "DFANaddfid");

    ret = DFANaddfid(file_id, labels[1]);
    CHECK(ret, FAIL, "DFANaddfid");

    ret = DFANaddfds(file_id, descs[0], (int32)strlen(descs[0]));
    CHECK(ret, FAIL, "DFANaddfds");

    ret = DFANaddfds(file_id, descs[1], (int32)strlen(descs[1]));
    CHECK(ret, FAIL, "DFANaddfds");

    if (FAIL == Hclose(file_id))
        printf("\nUnable to close file %s after writing.\n\n", DFAN_NDG_FILE);

    /********  Read file labels *********/

    file_id = Hopen(DFAN_NDG_FILE, DFACC_READ, 0);
    if (file_id == FAIL)
        printf("\n\nUnable to open file %s for reading.\n\n", DFAN_NDG_FILE);

    ret = DFANgetfidlen(file_id, ISFIRST);
    CHECK(ret, FAIL, "DFANgetfidlen");
    VERIFY(ret, strlen(labels[0]), "DFANgetfidlen first file label");

    ret = DFANgetfid(file_id, tempstr, MAXLEN_LAB, ISFIRST);
    CHECK(ret, FAIL, "DFANgetfid");
    VERIFY_CHAR(labels[0], tempstr, "DFANgetfid first file label");

    ret = DFANgetfidlen(file_id, NOTFIRST);
    CHECK(ret, FAIL, "DFANgetfidlen");
    VERIFY(ret, strlen(labels[1]), "DFANgetfidlen second file label");

    ret = DFANgetfid(file_id, tempstr, MAXLEN_LAB, NOTFIRST);
    CHECK(ret, FAIL, "DFANgetfid");
    VERIFY_CHAR(labels[1], tempstr, "DFANgetfid second file label");

    /********  Read file descriptions *********/

    ret = DFANgetfdslen(file_id, ISFIRST);
    CHECK(ret, FAIL, "DFANgetfdslen");
    VERIFY(ret, strlen(descs[0]), "DFANgetfdslen first file description");

    ret = DFANgetfds(file_id, tempstr, MAXLEN_DESC, ISFIRST);
    CHECK(ret, FAIL, "DFANgetfds");
    VERIFY_CHAR(tempstr, descs[0], "DFANgetfds first file description");

    ret = DFANgetfdslen(file_id, NOTFIRST);
    CHECK(ret, FAIL, "DFANgetfdslen");
    VERIFY(ret, strlen(descs[1]), "DFANgetfdslen second file description");

    ret = DFANgetfds(file_id, tempstr, MAXLEN_DESC, NOTFIRST);
    CHECK(ret, FAIL, "DFANgetfds");
    VERIFY_CHAR(tempstr, descs[1], "DFANgetfds second file description");

    if (FAIL == Hclose(file_id))
        printf("\n\nUnable to close file %s after reading.\n\n", DFAN_NDG_FILE);

    return (num_errs);
} /* add_sdfile_annotations */

/****************************************************************
**
**  add_sdsNDG_annotations: Adds data labels and descriptions with
**	DFAN API, then reads them back and verifies that they are
**	written correctly.  The data object in this function is
**	DFTAG_NDG.
**
****************************************************************/
intn
add_sdsNDG_annotations()
{
    char   labels[2][MAXLEN_LAB], descs[2][MAXLEN_DESC];
    uint16 refnum;
    int32  ret;
    intn   rank;
    int    jj;
    int32  dimsizes[2];
    float *data;
    intn   num_errs = 0;

    /* set up object labels and descriptions */

    strcpy(labels[0], "Object label #1: sds");
    strcpy(labels[1], "Object label #2: sds");
    strcpy(descs[0], "Object Descr #1: 1 2 3 4 5 6 7 8 9 10 11 12 \n");
    strcat(descs[0], "       13 14 15 16 17 18 19 20 **END SDS 1 DESCR**\n");
    strcpy(descs[1], "Object Descr #2: a b c d e f g h i j k l m n o p \n");
    strcat(descs[1], "       q r s t u v w x y z ??? **END SDS 2 DESCR**\n");

    /***** generate float array *****/

    data        = (float *)malloc(ROWS * COLS * sizeof(float));
    dimsizes[0] = ROWS;
    dimsizes[1] = COLS;

    /* Generate data for data sets */
    gen2Dfloat(ROWS, COLS, data);

    /* Set rank and dimension sizes for subsequent SDSs */
    DFSDsetdims(2, dimsizes);

    /********  Write labels and descriptions *********/

    /* Write REPS data sets and add a label and a description to each data set */
    for (jj = 0; jj < REPS; jj++) {
        /* write out scientific data set */
        ret = DFSDadddata(DFAN_NDG_FILE, 2, dimsizes, (void *)data);
        CHECK(ret, FAIL, "add_sdsNDG_annotations: DFSDadddata");
        refnum = DFSDlastref();

        /* Add label and description to this data set */
        ret = DFANputlabel(DFAN_NDG_FILE, DFTAG_NDG, refnum, labels[jj]);
        CHECK(ret, FAIL, "add_sdsNDG_annotations: DFANputlabel");

        ret = DFANputdesc(DFAN_NDG_FILE, DFTAG_NDG, refnum, descs[jj], (int32)strlen(descs[jj]));
        CHECK(ret, FAIL, "add_sdsNDG_annotations: DFANputdesc");
    }

    /********  Read labels and descriptions *********/
    for (jj = 0; jj < REPS; jj++) {
        ret = DFSDgetdims(DFAN_NDG_FILE, &rank, dimsizes, 3);
        CHECK(ret, FAIL, "add_sdsNDG_annotations: DFSDgetdims");
        refnum = DFSDlastref();

        /* Verify data of labels and descriptions */
        num_errs = check_lab_desc(DFAN_NDG_FILE, DFTAG_NDG, refnum, labels[jj], descs[jj]);
    }
    free(data);

    return (num_errs);
} /* add_sdsNDG_annotations */

/****************************************************************
**
**  add_sdsSDG_annotations: Adds data labels and descriptions with
**	DFAN API, then reads them back and verifies that they are
**	written correctly.  The data object in this function is
**	DFTAG_SDG.
**
****************************************************************/
intn
add_sdsSDG_annotations()
{
    char   labsds[MAXLEN_LAB], descsds[MAXLEN_DESC], descris[MAXLEN_DESC];
    uint16 refnum;
    intn   rank;
    int    j;
    int32  dimsizes[2];
    float *data;

    /* set up object labels and descriptions */

    strcpy(labsds, "sdsSDG label #1: sds");
    strcpy(descsds, "sdsSDG Descr #1: 1  2  3  4  5  6  7  8  9 10 11 12 \n");
    strcat(descsds, "             13 14 15 16 17 18 19 20 **END SDS DESCR**\n");
    strcpy(descris, "sdsSDG Descr #2: A B C D E F G H I J K L \n");
    strcat(descris, "                M N O **END IMAGE DESCR **\n");

    /***** generate float array and image *****/

    data = (float *)malloc(ROWS * COLS * sizeof(float));

    dimsizes[0] = ROWS;
    dimsizes[1] = COLS;

    gen2Dfloat(ROWS, COLS, data);

    DFSDsetdims(2, dimsizes);

    /********  Write labels and descriptions *********/
    for (j = 0; j < REPS; j++) {
        /* write out scientific data set */
        DFSDadddata(DFAN_SDG_FILE, 2, dimsizes, (void *)data);

        /* write out annotations for 2 out of every 3 */
        refnum = DFSDlastref();

        DFANputlabel(DFAN_SDG_FILE, DFTAG_SDG, refnum, labsds);
        DFANputdesc(DFAN_SDG_FILE, DFTAG_SDG, refnum, descsds, (int32)strlen(descsds));
    }

    /********  Read labels and descriptions *********/

    for (j = 0; j < REPS; j++) {
        DFSDgetdims(DFAN_SDG_FILE, &rank, dimsizes, 3);
        refnum = DFSDlastref();

        if ((j % 3) != 0) /* read in annotations for 2 out of every 3 */
            check_lab_desc(DFAN_SDG_FILE, DFTAG_SDG, refnum, labsds, descsds);
    }
    free(data);

    return 0;
}

/****************************************************************
**
**  get_ann_datainfo: Gets data info of annotations and verifies
**	against the provided offsets/lengths.
**
****************************************************************/
intn
get_ann_datainfo(int32 id, ann_type annot_type, int32 *chk_offsets, int32 *chk_lengths)
{
    int32 *offsetarray = NULL, *lengtharray = NULL;
    int32  num_annots;
    intn   ii, num_errs = 0;

    num_annots = SDgetanndatainfo(id, annot_type, 0, NULL, NULL);
    CHECK(num_annots, FAIL, "get_ann_datainfo: SDgetanndatainfo annot_type with NULL buffers");

    if (num_annots > 0) {
        offsetarray = (int32 *)malloc(num_annots * sizeof(int32));
        if (offsetarray == NULL)
            exit(-1);
        lengtharray = (int32 *)malloc(num_annots * sizeof(int32));
        if (lengtharray == NULL)
            exit(-1);

        num_annots = SDgetanndatainfo(id, annot_type, num_annots, offsetarray, lengtharray);
        CHECK(num_annots, FAIL, "get_ann_datainfo: SDgetanndatainfo");

        /* Verify offsets and lengths of annotations */
        for (ii = 0; ii < num_annots; ii++) {
            VERIFY(offsetarray[ii], chk_offsets[ii], "get_ann_datainfo: SDgetanndatainfo");
            VERIFY(lengtharray[ii], chk_lengths[ii], "get_ann_datainfo: SDgetanndatainfo");
        }

        free(offsetarray);
        free(lengtharray);
    }
    return (num_errs);
}

/***************************************************************************
   Name: test_dfannots() - tests getting data info of annotations from DFAN

   Description:
        This routine uses several utility routines to
        - create various file's and data's annotations
        - retrieve and verify the offsets/lengths of these annotations

***************************************************************************/
static int
test_dfannots(void)
{
    int32 sd_id, sds_id, sds_index;
    intn  status;
    int32 n_datasets, n_file_attr;
    int32 chk_offsets[10], chk_lengths[10];
    intn  num_errs = 0;

    /* Add file annotations */
    status = add_sdfile_annotations();
    if (status > 0)
        fprintf(stderr, "test_dfannots: errors while adding file annotations\n");

    /* Add SDS annotations via NDG */
    status = add_sdsNDG_annotations();
    if (status > 0)
        fprintf(stderr, "test_dfannots: errors while adding NDG annotations\n");

    /* Add SDS annotations via SDG in a separate file */
    status = add_sdsSDG_annotations();
    if (status > 0)
        fprintf(stderr, "test_dfannots: errors while adding SDG annotations\n");

    /**********************************************************************
     * Using SD API to get offset/length of data from file annotations    *
     * and data set annotations in file DFAN_NDG_FILE to test NDG annots  *
     **********************************************************************/

    /* Open the file and initialize the SD interface. */
    sd_id = SDstart(DFAN_NDG_FILE, DFACC_READ);
    CHECK(sd_id, FAIL, "test_dfannots: SDstart");

    /* BMR: need better checking algorithm here! */

    /* Get data info of file labels and verify them against chk_offsets and
        chk_lengths */
    chk_offsets[0] = 307;
    chk_offsets[1] = 294; /* verified with UNIX command */
    chk_lengths[0] = 13;
    chk_lengths[1] = 13; /* "od --format=a" */
    status         = get_ann_datainfo(sd_id, AN_FILE_LABEL, chk_offsets, chk_lengths);
    if (status > 0)
        fprintf(stderr, "test_dfannots: errors while verifying annotations\n");

    /* Get data info of file descs and verify them against chk_offsets and
        chk_lengths */
    chk_offsets[0] = 414;
    chk_offsets[1] = 320; /* verified with UNIX command */
    chk_lengths[0] = 99;
    chk_lengths[1] = 94; /* "od --format=a" */
    status         = get_ann_datainfo(sd_id, AN_FILE_DESC, chk_offsets, chk_lengths);
    if (status > 0)
        fprintf(stderr, "test_dfannots: errors while verifying annotations\n");

    /* Obtain information about the file. */
    status = SDfileinfo(sd_id, &n_datasets, &n_file_attr);
    CHECK(status, FAIL, "test_dfannots: SDfileinfo");

    /* Get annotation information of each SDS in the file. */
    for (sds_index = 0; sds_index < n_datasets; sds_index++) {
        sds_id = SDselect(sd_id, sds_index);
        CHECK(sds_id, FAIL, "test_dfannots: SDselect");

        /* Only data sets have annotations */
        if (!SDiscoordvar(sds_id)) {
            /* Get data info of object descs and verify them against
               chk_offsets and chk_lengths */
            if (sds_index == 2) /* first data set, not dimension var */
            {
                chk_offsets[0] = 987; /* verified with UNIX */
                chk_lengths[0] = 96;  /* command "od --format=a" */
            }
            else if (sds_index == 5) /* second data set, not dimension var */
            {
                chk_offsets[0] = 1729; /* verified with UNIX */
                chk_lengths[0] = 101;  /* command "od --format=a" */
            }

            status = get_ann_datainfo(sds_id, AN_DATA_DESC, chk_offsets, chk_lengths);
            if (status > 0)
                fprintf(stderr, "test_dfannots: errors while verifying annotations\n");

            /* Get data info of object labels and verify them against
               chk_offsets and chk_lengths */
            if (sds_index == 2) /* first data set */
            {
                chk_offsets[0] = 963; /* verified with UNIX */
                chk_lengths[0] = 20;  /* command "od --format=a" */
            }
            else if (sds_index == 5) /* second data set */
            {
                chk_offsets[0] = 1705; /* verified with UNIX */
                chk_lengths[0] = 20;   /* command "od --format=a" */
            }
            status = get_ann_datainfo(sds_id, AN_DATA_LABEL, chk_offsets, chk_lengths);
            if (status > 0)
                fprintf(stderr, "test_dfannots: errors while verifying annotations\n");
        } /* SDS is not coordinate var */

        /* Terminate access to the selected data set. */
        status = SDendaccess(sds_id);
        CHECK(status, FAIL, "test_dfannots: SDendaccess");
    } /* for each data set */

    /* Terminate access to the SD interface and close the file. */
    status = SDend(sd_id);
    CHECK(status, FAIL, "test_dfannots: SDend");

    /**********************************************************************
     * Using SD API to get offset/length of data from file annotations    *
     * and data set annotations in file DFAN_NDG_FILE to test NDG annots  *
     * -Not doing now because there are no such data in NASA files and we *
     * are running out of time.                                           *
     **********************************************************************/
    /* Return the number of errors that's been kept track of so far */
    return num_errs;
}

/***************************************************************************
   Name: test_dfsdattrs() - tests getting data info of attributes from DFSD

   Description:
        This routine creates and writes a dataset and several attributes with
        the DFSD API to test the API function SDgetoldattdatainfo.
        In the DFSD API, an SDS attribute is stored using tag/ref approach,
        unlike the newer attributes which were introduced in Apr, 1993 and are
        stored in Vdatas.  The dimension attributes are stored following the
        SDS attribute.  All attributes are separated by null characters.

        In this test, SDgetoldattdatainfo will retrieve the offsets and lengths
        of several attributes.  Then the data will be read back from the file
        at the retrieved offsets/lengths, without the use of the HDF4 library,
        and will be verified against the original data buffers.

***************************************************************************/
#define OLDATTFILE "tdfsdatts.hdf"
#define XX         6
#define YY         6

/* Compares a string against the original buffer, returns 0 if equals, or -1 */
intn compare(const char *outstring, const char *instring);

static intn
test_dfsdattrs()
{
    int         i, j, ret;
    intn        rank;
    int32       dims[2];
    float32     f32[XX][YY], tf32[XX][YY];
    intn        info_count = 0;
    int32       offset = 0, length = 0;
    int32       fid = -1, sdsid = -1, dimid = -1;
    intn        status    = 0;
    intn        num_errs  = 0; /* number of errors so far */
    const char *datalabel = "Datalabel", *dataunit = "Dataunit", *datafmt = "Datafmt", *coordsys = "coordsys";
    char        in_datalabel[256], in_dataunit[256], in_datafmt[256], in_coordsys[256];

    const char *dimlabels[2], *dimunits[2], *dimfmts[2];
    char        in_dimlabels[2][256], in_dimunits[2][256], in_dimfmts[2][256];
    /*
    float32  scplnf32[XX] = {(float32) 0.0, (float32) 100.0, (float32) 0.1,
                        (float32) 101.0, (float32) 0.2, (float32) 102.0};
    float32  scrowf32[YY] = {(float32) 0.0, (float32) 10.0, (float32) 20.0,
                        (float32) 1.0, (float32) 11.0, (float32) 21.0};
    uncomment these when the calls to DFSDsetdimscale are uncommented.
    */

    rank    = 2;
    dims[0] = XX;
    dims[1] = YY;

    dimlabels[0] = "";
    dimunits[0]  = "c_dim1_unit";
    dimfmts[0]   = "c_dim1_fmt";

    dimlabels[1] = "c_dim2_label";
    dimunits[1]  = "c_dim2_unit";
    dimfmts[1]   = "c_dim2_fmt";

    for (i = 0; i < XX; i++) {
        for (j = 0; j < YY; j++) {
            f32[i][j] = (float32)((i * XX) + j); /* range: 0 ~ 4-billion */
        }
    }

    ret = DFSDsetdims(rank, dims);
    CHECK(ret, FAIL, "DFSDsetdims");
    /* individual files */

    ret = DFSDsetNT(DFNT_NFLOAT32);
    CHECK(ret, FAIL, "DFSDsetNT");
    ret = DFSDsetdims(rank, dims);
    CHECK(ret, FAIL, "DFSDsetdims");

    /* Set attributes to the dataset and its dimensions */
    ret = DFSDsetdatastrs(datalabel, dataunit, datafmt, coordsys);
    CHECK(ret, FAIL, "DFSDsetdatastrs");
    ret = DFSDsetdimstrs(1, dimlabels[0], dimunits[0], dimfmts[0]);
    CHECK(ret, FAIL, "DFSDsetdimstrs");
    ret = DFSDsetdimstrs(2, dimlabels[1], dimunits[1], dimfmts[1]);
    CHECK(ret, FAIL, "DFSDsetdimstrs");

    /* Note: Setting attribute to a dimension with DFSD API doesn't make the
        dimension become a coordinate variable, only setting dimension scale
        does.  Thus, when no setting scale is done, the dumper will not show
        the dimension variables.  However, the dimension attributes are there
        and SDgetoldattdatainfo will still find them.  For a demo, the two
        calls to DFSDsetdimscale can be uncommented, then dumper will show
        the dimension variables and their attributes. -BMR, Mar 13, 2011 */

    /* Set dimension scales */
    /*
    ret = DFSDsetdimscale(1, dims[0], (void *) scplnf32);
    CHECK(ret, FAIL, "DFSDsetdimscale");

    ret = DFSDsetdimscale(2, dims[1], (void *) scrowf32);
    CHECK(ret, FAIL, "DFSDsetdimscale");
    */

    /* Write/Read data to/from SDS */
    ret = DFSDputdata(OLDATTFILE, rank, dims, (void *)f32);
    CHECK(ret, FAIL, "DFSDputdata");
    ret = DFSDgetdata(OLDATTFILE, rank, dims, (void *)tf32);
    CHECK(ret, FAIL, "DFSDgetdata");

    /* Read attributes from the dataset and its dimensions */
    ret = DFSDgetdatastrs(in_datalabel, in_dataunit, in_datafmt, in_coordsys);
    CHECK(ret, FAIL, "DFSDgetdatastrs");
    ret = DFSDgetdimstrs(1, in_dimlabels[0], in_dimunits[0], in_dimfmts[0]);
    CHECK(ret, FAIL, "DFSDgetdimstrs");
    ret = DFSDgetdimstrs(2, in_dimlabels[1], in_dimunits[1], in_dimfmts[1]);
    CHECK(ret, FAIL, "DFSDgetdimstrs");

    /* Verify a few */
    ret = compare(in_datalabel, datalabel); /* SDS' label */
    CHECK(ret, FAIL, "compare");
    ret = compare(in_dimunits[0], dimunits[0]); /* first dim's units */
    CHECK(ret, FAIL, "compare");
    ret = compare(in_dimlabels[1], dimlabels[1]); /* first dim's label */
    CHECK(ret, FAIL, "compare");

    /* Using SD API to get the offsets and lengths of various attributes, then
       read them from the file without the use of HDF4 library and verify them
       against the original buffers */

    /* Open the file with SD API */
    fid = SDstart(OLDATTFILE, DFACC_RDWR);
    CHECK(fid, FAIL, "SDstart");

    /* Get the first dataset (and only one) */
    sdsid = SDselect(fid, 0);
    CHECK(sdsid, FAIL, "SDselect");

    /* Test SDgetoldattdatainfo with dataset's attribute string _HDF_LongName */
    info_count = SDgetoldattdatainfo(0, sdsid, _HDF_LongName, &offset, &length);
    CHECK(info_count, FAIL, "SDgetoldattdatainfo");
    status = readnoHDF_char(OLDATTFILE, offset, length, datalabel);
    CHECK(status, FAIL, "readnoHDF_char");

    /* Test with attribute string _HDF_LongName of dataset's first dimension */
    dimid = SDgetdimid(sdsid, 0);
    CHECK(dimid, FAIL, "SDgetdimid");
    info_count = SDgetoldattdatainfo(dimid, sdsid, _HDF_LongName, &offset, &length);
    CHECK(info_count, FAIL, "SDgetoldattdatainfo");
    /* This dimension doesn't have an _HDF_LongName attribute, so length must be 0,
        and readnoHDF_char will not be needed to verify the attribute values */
    VERIFY(length, 0, "SDgetoldattdatainfo");

    /* Test with attribute string _HDF_Format of dataset's second dimension */
    dimid = SDgetdimid(sdsid, 1);
    CHECK(dimid, FAIL, "SDgetdimid");
    info_count = SDgetoldattdatainfo(dimid, sdsid, _HDF_Format, &offset, &length);
    CHECK(info_count, FAIL, "SDgetoldattdatainfo");
    status = readnoHDF_char(OLDATTFILE, offset, length, dimfmts[1]);
    CHECK(status, FAIL, "readnoHDF_char");

    /* Test with dataset's attribute string _HDF_CoordSys */
    info_count = SDgetoldattdatainfo(0, sdsid, _HDF_CoordSys, &offset, &length);
    CHECK(info_count, FAIL, "SDgetoldattdatainfo");
    status = readnoHDF_char(OLDATTFILE, offset, length, coordsys);
    CHECK(status, FAIL, "readnoHDF_char");
    /* Note: coordsys' length has 1 more than the number of actual chars.  We'll
       need to study that to document better. */

    /* Terminate access to the dataset and close the file */
    status = SDendaccess(sdsid);
    CHECK(status, FAIL, "SDendaccess");
    status = SDend(fid);
    CHECK(status, FAIL, "SDend");

    return (num_errs);
}

intn
compare(const char *outstring, const char *instring)
{
    intn status = 0;
    if (strcmp(outstring, instring) != 0) {
        fprintf(stderr, ">>> Test failed for %s\n", outstring);
        fprintf(stderr, "    Input string =  %s\n", instring);
        status = -1;
    }
    return (status);
}

/*******************************************************************
  Name: readnoHDF_char - utility routine to read and verify character
                        data without HDF4 library

  Description:
        readnoHDF_char opens the file and reads in data at the specified
        offset.  The read data is compared against the original data passed
        by caller.  If any mismatch occurs, an error message will be
        displayed but the process will continue.

  Parameters:
        char *filename	IN: name of the file
        int32 offset	IN: where to start read data
        int32 length	IN: how long to read the data
        char *orig_buf	IN: original data buffer to compare against

  Return value:
        SUCCEED/FAIL

********************************************************************/
intn
readnoHDF_char(const char *filename, const int32 offset, const int32 length, const char *orig_buf)
{
    FILE  *fd;          /* file descriptor */
    size_t readlen = 0; /* number of bytes actually read */
    char  *readcbuf;
    intn   ret_value = SUCCEED;

    /* Open the file for reading without SD API */
    fd = fopen(filename, "r");
    if (fd == NULL) {
        fprintf(stderr, "readnoHDF_char: unable to open file %s", filename);
        exit(1);
    }

    /* Forward to the position of the first block of data */
    if (fseek(fd, (off_t)offset, SEEK_SET) == -1) {
        fprintf(stderr, "readnoHDF_char: unable to seek offset %d\n", (int)offset);
        exit(1);
    }

    /* Allocate buffers for SDS' data */
    readcbuf = (char *)malloc(length * sizeof(char));
    CHECK_ALLOC(readcbuf, "readcbuf", "readnoHDF_char");

    /* Read in this block of data */
    readlen = fread((void *)readcbuf, 1, length, fd);
    if (readlen > 0) {
        /* Compare data read without HDF4 lib against the original buffer */
        if (strncmp(readcbuf, orig_buf, readlen) != 0)
            fprintf(stderr,
                    "Failure: non-HDF reading got different values than written values\n   >>> written = "
                    "%s\n   >>> read = %s\n",
                    orig_buf, readcbuf);
    }
    free(readcbuf);

    /* Close the file */
    if (fclose(fd) == -1) {
        fprintf(stderr, "readnoHDF_char: unable to close file %s", filename);
        exit(1);
    }
    return ret_value;
}

/* Test driver for testing the public functions SDgetattdatainfo(),
   SDgetoldattdatainfo(), and SDgetanndatainfo() */
extern int
test_att_ann_datainfo()
{
    int num_errs = 0;

    /* Output message about test being performed */
    TESTING("getting location info of attr and annot data (tattdatainfo.c)");

    /* Test getting data info of attributes for SDSs */
    num_errs = num_errs + test_attrs();

    /* Test getting data info of annotations added by DFAN API */
    num_errs = num_errs + test_dfannots();

    /* Test getting data info of annotations added by AN API */
    /* num_errs = num_errs + test_mfannots(); not needed right now
     */

    /* Test getting data info of old attributes added by DFSD API */
    num_errs += test_dfsdattrs();

    if (num_errs == 0)
        PASSED();
    else
        H4_FAILED();

    /* Return the number of errors that's been kept track of so far */
    return num_errs;
}
