/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * 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.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "hdp.h"
#include <math.h>

void
dumpvd_usage(intn argc, char *argv[])
{
    (void)argc;

    printf("Usage:\n");
    printf("%s dumpvd [-a|-i <indices>|-r <refs>|-n <names>|-c <classes>|-f <f1, f2,..>] [-dhv] [-o "
           "<filename>] [-bx] <filelist>\n",
           argv[0]);
    printf("\t-a\tDump all VDs in the file (default)\n");
    printf("\t-i <indices>\tDump the VDs at positions listed in <indices>\n");
    printf("\t-r <refs>\tDump the VDs with reference number listed in <refs>\n");
    printf("\t-n <names>\tDump the VDs with name listed in <names>\n");
    printf("\t-c <classes>\tDump the VDs with class listed in <classes>\n");
    printf("\t-f <f1, f2,..> \tDump based on fields in vdata header\n");
    printf("\t-d\tDump data only, no tag/ref, formatted to input to hp2hdf\n");
    printf("\t-h\tDump header only, no annotation for elements nor data\n");
    printf("\t-v\tDump everything including all annotations (default)\n");
    printf("\t-o <filename>\tOutput to file <filename>\n");
    printf("\t-b\tBinary format of output\n");
    printf("\t-x\tAscii text format of output (default)\n");
    printf("\t<filelist>\tList of hdf file names, separated by spaces\n");
} /* end dumpvd_usage() */

intn
parse_dumpvd_opts(dump_info_t *dumpvd_opts, intn *curr_arg, intn argc, char *argv[],
                  char *flds_chosen[MAXCHOICES], int *dumpallfields)
{
    int32 i, lastItem;
    char *tempPtr, *ptr;

    /* traverse the command and process each option */
/* Allows '/' for options on Windows */
#ifdef H4_HAVE_WIN32_API
    while ((*curr_arg < argc) && ((argv[*curr_arg][0] == '-') || (argv[*curr_arg][0] == '/')))
#else
    while ((*curr_arg < argc) && (argv[*curr_arg][0] == '-'))
#endif
    {
        switch (argv[*curr_arg][1]) {
            case 'a': /* dump all, default */
                dumpvd_opts->filter = DALL;

                /* indicate that no specific vdata requested, will dump all */
                dumpvd_opts->num_chosen = NO_SPECIFIC;
                (*curr_arg)++;
                break;

            case 'i':                          /* dump by index */
                dumpvd_opts->filter |= DINDEX; /* set bit DINDEX */
                (*curr_arg)++;

                /* parse and store the given indices in structure by_index */
                parse_number_opts(argv, curr_arg, &dumpvd_opts->by_index);
                (*curr_arg)++;
                break;

            case 'r':                           /* dump by reference */
                dumpvd_opts->filter |= DREFNUM; /* set bit DREFNUM */
                (*curr_arg)++;

                /* parse and store the given ref numbers in structure
   by_ref */
                parse_number_opts(argv, curr_arg, &dumpvd_opts->by_ref);
                (*curr_arg)++;
                break;

            case 'n':                         /* dump by names */
                dumpvd_opts->filter |= DNAME; /* set bit DNAME */
                (*curr_arg)++;

                /* parse and store the given names in structure by_name */
                parse_string_opts(argv, curr_arg, &dumpvd_opts->by_name);
                (*curr_arg)++;
                break;

            case 'c':                          /* dump by class */
                dumpvd_opts->filter |= DCLASS; /* set bit DCLASS */
                (*curr_arg)++;

                /* parse and store the given classes in structure by_class */
                parse_string_opts(argv, curr_arg, &dumpvd_opts->by_class);
                (*curr_arg)++;
                break;

            case 'f':                              /* dump a subset of the fields */
                if (dumpvd_opts->filter == DALL)   /* not necessary to set this */
                    dumpvd_opts->filter = DFIELDS; /* leave it here anyway */
                *dumpallfields = 0;                /*???*/
                (*curr_arg)++;

                lastItem = 0;
                ptr      = argv[*curr_arg];
                for (i = 0; !lastItem; i++) {
                    tempPtr = strchr(ptr, ',');
                    if (tempPtr == NULL)
                        lastItem = 1;
                    else
                        *tempPtr = '\0';
                    flds_chosen[i] = (char *)malloc(sizeof(char) * (strlen(ptr) + 1));
                    CHECK_ALLOC(flds_chosen[i], "flds_chosen[i]", "parse_dumpvd_opts");

                    strcpy(flds_chosen[i], ptr);
                    ptr = tempPtr + 1;
                }
                flds_chosen[i] = NULL;
                (*curr_arg)++;
                break;

            case 'd': /* dump data only */
                dumpvd_opts->contents = DDATA;
                (*curr_arg)++;
                break;

            case 'h': /* no annotations nor data */
                dumpvd_opts->contents = DHEADER;
                (*curr_arg)++;
                break;

            case 'v': /* dump all info */
                dumpvd_opts->contents = DVERBOSE;
                (*curr_arg)++;
                break;

            case 'o': /* specify output file */
                dumpvd_opts->dump_to_file = TRUE;

                /* Get file name */
                strcpy(dumpvd_opts->file_name, argv[++(*curr_arg)]);

                (*curr_arg)++;
                break;

            case 'b': /* dump data in binary */
                dumpvd_opts->file_format = DBINARY;
                (*curr_arg)++;
                break;

            case 'x': /* dump data in ascii, also default */
                dumpvd_opts->file_format = DASCII;
                (*curr_arg)++;
                break;

            default: /* invalid dumpvd option */
                printf("Warning: Invalid dumpvd option %s\n", argv[*curr_arg]);
                return (FAIL);
        } /* end switch */
    }     /* end while */

    /* add the number of vdatas requested by index, by ref#, and by name
       to have a total number of requested vdatas */
    dumpvd_opts->num_chosen = dumpvd_opts->by_index.num_items + dumpvd_opts->by_ref.num_items +
                              dumpvd_opts->by_name.num_items + dumpvd_opts->by_class.num_items;

    return (SUCCEED);
} /* end parse_dumpvd_opts */

/* VSref_index returns the index of a vdata given the vdata's ref#
   or returns FAIL, if the ref# is not found */
int32
VSref_index(int32 file_id, int32 vd_ref)
{
    int32 find_ref  = -1;
    int   index     = 0; /* index is zero based? */
    int32 ret_value = FAIL;

    while ((find_ref = VSgetid(file_id, find_ref)) != FAIL) {
        if (find_ref == vd_ref) {
            ret_value = index;
            goto done; /* found, done */
        }
        index++;
    }

done:
    return ret_value;
} /* VSref_index */

/* VSstr_index returns the index of a vdata given the vdata's name or
   class, if the name is not found, and returns FAIL, otherwise.  */
int32
VSstr_index(int32 file_id, char *filter_str, /* searched vd's name or class */
            int    is_name,                  /* TRUE if searching for name, FALSE if class */
            int32 *find_ref,                 /* current ref#, will return next found */
            int32 *index)                    /* index of the vdata w/ref# *find_ref */
{
    int32 vdata_id = FAIL;
    char  vdata_name[MAXNAMELEN];
    int32 ret_value = SUCCEED;

    /* starting from the ref# *find_ref, search for the vdata having a
       name or class the same as the given string filter_str; when no
       more vdata to search, return FAIL */
    while ((*find_ref = VSgetid(file_id, *find_ref)) != FAIL) {
        /* printf("VSstr_index: *find_ref = %d\n", *find_ref);
         */
        vdata_id = VSattach(file_id, *find_ref, "r");
        if (FAIL == vdata_id)
            ERROR_GOTO_2("in %s: VSattach failed for vdata with ref#=%d", "VSstr_index", (int)*find_ref);

        /* if the string searched is a vdata's name */
        if (is_name) {
            if (FAIL == VSgetname(vdata_id, vdata_name))
                ERROR_GOTO_2("in %s: VSgetname failed for vdata with ref#=%d", "VSstr_index", (int)*find_ref);
        }
        /* or the string searched is a vdata's class */
        else {
            if (FAIL == VSgetclass(vdata_id, vdata_name))
                ERROR_GOTO_2("in %s: VSgetclass failed for vdata with ref#=%d", "VSstr_index",
                             (int)*find_ref);
        }

        if (FAIL == VSdetach(vdata_id))
            ERROR_GOTO_2("in %s: VSdetach failed for vdata with ref#=%d", "VSstr_index", (int)*find_ref);

        /* if the vd's name or vd's class is the given string, return the
           index of the vdata found */
        if (strcmp(vdata_name, filter_str) == 0) {
            /* store the current index to return first */
            ret_value = (*index);

            /* then increment index for next vdata - same class vdatas*/
            (*index)++;
            goto done; /* succeeded */
        }
        else
            ret_value = FAIL;

        /* Note: in either case, increment the index for the next vdata */
        (*index)++;
    } /* end while getting vdatas */

    /* when VSgetid returned FAIL in while above, search should stop */
    ret_value = FAIL;

done:
    return ret_value;
} /* VSstr_index() */

/* compose the list of indices of the requested vdatas although some
   vdatas are requested by ref# or name.
   The routine returns:
        - the number of vdatas to be processed, or
        - NO_SPECIFIC if all vdatas are to be processed, or
        - 0 if none.
   If there are any errors, the parameter index_error will return TRUE */
int32
choose_vd(dump_info_t *dumpvd_opts, int32 **vd_chosen, int32 file_id, int *index_error)
{
    int32    i, index, find_ref, number, num_vd_chosen = dumpvd_opts->num_chosen, vd_count = 0;
    filter_t filter    = dumpvd_opts->filter; /* temporary name */
    intn     ret_value = 0;

    /* if no specific vdatas are requested, return vdata count as
       NO_SPECIFIC (-1) */
    if (filter == DALL)
        HGOTO_DONE(NO_SPECIFIC);

    /* if specific vdatas were requested, allocate space for the array
       of indices */
    if (num_vd_chosen > 0)
        alloc_index_list(vd_chosen, num_vd_chosen);

    /* else, no chosen vdatas but filter is not DALL, it shouldn't be this
       combination, return vdata count as NO_SPECIFIC to dump all */
    else
        HGOTO_DONE(NO_SPECIFIC);

    /* if there are some vdatas requested by index, store the indices in
       the array provided by the caller */
    if (filter & DINDEX)
        for (i = 0; i < dumpvd_opts->by_index.num_items; i++) {
            (*vd_chosen)[vd_count] = dumpvd_opts->by_index.num_list[i];
            vd_count++;
        }

    /* if there are some vdatas requested by ref#, store the indices in
       the array provided by the caller */
    if (filter & DREFNUM)
        for (i = 0; i < dumpvd_opts->by_ref.num_items; i++) {
            index = VSref_index(file_id, dumpvd_opts->by_ref.num_list[i]);
            if (index == FAIL) {
                printf("Vdata with reference number %d: not found\n", (int)dumpvd_opts->by_ref.num_list[i]);
                *index_error = TRUE; /* index error */
            }
            else {
                (*vd_chosen)[vd_count] = index;
                vd_count++;
            }
        } /* for */

    /* if there are some vdatas requested by name, store the indices in
       the array provided by the caller */
    if (filter & DNAME)
        for (i = 0; i < dumpvd_opts->by_name.num_items; i++) {
            find_ref = -1;
            number   = 0;
            index    = VSstr_index(file_id, dumpvd_opts->by_name.str_list[i], 1, &find_ref, &number);
            if (index == FAIL) {
                printf("Vdata with name '%s': not found\n", dumpvd_opts->by_name.str_list[i]);
                *index_error = TRUE;
            }
            else {
                (*vd_chosen)[vd_count] = index;
                vd_count++;
            }
        } /* for */

    /* if there are some vdatas requested by class, store the indices in
       the array provided by the caller */
    if (filter & DCLASS)
        for (i = 0; i < dumpvd_opts->by_class.num_items; i++) {
            int32 found = FALSE;

            find_ref = -1;
            number   = 0;
            while ((index = VSstr_index(file_id, dumpvd_opts->by_class.str_list[i], 0, &find_ref, &number)) !=
                   FAIL) {
                if (vd_count < num_vd_chosen)
                    (*vd_chosen)[vd_count] = index;
                else {
                    *vd_chosen = (int32 *)realloc(*vd_chosen, sizeof(int32) * (num_vd_chosen + 1));
                    if (*vd_chosen == NULL) {
                        fprintf(stderr, "Failure in choose_vd: Memory re-allocation error\n");
                        exit(1);
                    } /* end if */
                    (*vd_chosen)[vd_count] = index;
                    num_vd_chosen++;
                }
                vd_count++;
                found = TRUE;
            }
            if (!found) {
                printf("Vdata with class %s: not found\n", dumpvd_opts->by_class.str_list[i]);
                *index_error = TRUE; /* index error */
            }
        } /* for */

    if (filter == DFIELDS) /* Don't have to worry about which chosen fields yet. */
    {
    }

    ret_value = vd_count; /* actual number of vdatas to be processed; might
     be different from dumpvd_opts->num_chosen because of the non-unique
     class name */
done:
    return ret_value;
} /* choose_vd */

void
printHeader(FILE *fp, char *fldstring, char *fields, vd_info_t *curr_vd)
{
    (void)fldstring;

    fprintf(fp, "Vdata: %d\n", (int)curr_vd->index);
    if (curr_vd->tag == FAIL) /* print vdata tag */
        fprintf(fp, "   tag = <Undefined>; ");
    else
        fprintf(fp, "   tag = %d; ", (int)curr_vd->tag);

    /* print reference number without checking because it's from
       VSgetid and has been checked by the calling routine */
    fprintf(fp, "reference = %d;\n", (int)curr_vd->ref);

    if (curr_vd->nvf == FAIL) /* print number of records in the vdata */
        fprintf(fp, "   number of records = <Undefined>; ");
    else
        fprintf(fp, "   number of records = %d;", (int)curr_vd->nvf);

    if (curr_vd->interlace == FAIL) /* print interlace mode */
        fprintf(fp, "   interlace = <Undefined>;\n");
    else if (curr_vd->interlace == 0)
        fprintf(fp, " interlace = FULL_INTERLACE (0);\n");
    else if (curr_vd->interlace == 1)
        fprintf(fp, "   interlace = NO_INTERLACE;\n");
    else
        fprintf(fp, "   interlace = <Unknown interlace mode (%d)>;\n", (int)curr_vd->interlace);

    /* print the list of field names of the vdata if it's available */
    /* The list of field names can be very long and would look very
       messy when being displayed if it were to be dumped out at once.
       print_fields displays a list in a nice way even if the list is
       long.  The second parameter specifies how the field name list
       begins; it's needed because dumpvg also uses this routine and
       has different indentation format than dumpvd */
    print_fields(fields, "   fields = ", fp);

    fprintf(fp, "   record size (in bytes) = %d;\n", (int)curr_vd->vsize);

    if (curr_vd->name[0] == '\0') /* print vdata name */
        fprintf(fp, "   name = <Undefined>; ");
    else
        fprintf(fp, "   name = %s;", curr_vd->name);

    /* print class name - Note that vdclass can be NULL */
    if (curr_vd->clss[0] == '\0')
        fprintf(fp, " class = <Undefined>;\n");
    else
        fprintf(fp, " class = %s;\n", curr_vd->clss);

} /* end of printHeader */

intn
getFieldIndices(char *fields, char *flds_chosen[MAXCHOICES], int32 *flds_indices)
{
    int32 lastItem = 0;          /* whether the last field in the list 'fields' is reached */
    int32 fld_name_idx;          /* index for the list of field names */
    int32 idx     = 0;           /* index for the array of field indices */
    char *tempPtr = NULL;        /* temp ptr to mark the end of a field name in the list */
    char *ptr     = NULL;        /* used to forward to next field name in the field name list */
    char  fldstring[MAXNAMELEN]; /* holds a field name extracted from field name list */
    int32 i;
    intn  flds_match = 0;
    intn  ret_value  = SUCCEED;
    char  tempflds[VSFIELDMAX * FIELDNAMELENMAX];

    /* make copy of the field name list retrieved by VSinquire to use
       in processing the field names */
    strcpy(tempflds, fields);

    ptr = tempflds; /* used to forward the field names */

    i = (-1); /* dummy? */
    HDmemfill(flds_indices, &i, sizeof(int32), MAXCHOICES);

    /* Extract each field name from the list of fields of the
       current record. */
    for (fld_name_idx = 0; !lastItem; fld_name_idx++) {
        /* look for a comma in the fields */
        tempPtr = strchr(ptr, ',');

        /* if no comma is found, that means the last field name is reached */
        if (tempPtr == NULL)
            lastItem = 1; /* set flag */

        /* otherwise, add null to end the extracted field name */
        else
            *tempPtr = '\0';

        /* extract that field into fldstring */
        strcpy(fldstring, ptr);

        /* forward the pointer to the next field name in the list */
        ptr = tempPtr + 1;

        /* Compare the extracted field name with each of the names
           of the fields having been chosen. */
        for (i = 0; flds_chosen[i] != NULL; i++) {
            if (!strcmp(flds_chosen[i], fldstring)) {
                flds_indices[idx] = fld_name_idx;
                idx++;
                flds_match = 1;
            }
        } /* for (i...) */
    }     /* for (fld_name_idx...) */

    /* return the flag indicating whether any given fields exist */
    ret_value = flds_match;

    return ret_value;
} /* end of getFieldIndices */

intn
dumpvd_ascii(dump_info_t *dumpvd_opts, int32 file_id, const char *file_name, FILE *fp, int32 num_vd_chosen,
             char *flds_chosen[MAXCHOICES], int32 *vd_chosen, int dumpallfields)
{
    int32         flds_indices[MAXCHOICES];
    int32         i;
    int32         vd_chosen_idx; /* index for vd_chosen array */
    int32         nvf;
    int32         interlace;
    int32         vsize;
    int32         vdata_ref = -1;
    int32         vdata_tag;
    char          vdclass[VSNAMELENMAX + 1];
    char          vdname[VSNAMELENMAX + 1];
    char          fldstring[MAXNAMELEN];
    intn          dumpall = 0;
    file_format_t ff      = DASCII;
    vd_info_t     curr_vd;
    int32         vd_id      = FAIL;
    int32         an_handle  = FAIL;
    int32         num_fields = 0;
    intn          status = SUCCEED, ret_value = SUCCEED;
    char          fields[VSFIELDMAX * FIELDNAMELENMAX];

    if (dumpvd_opts->contents != DDATA) {
        fprintf(fp, "File name: %s \n\n", file_name);

        /* print file annotations */
        status = print_file_annotations(file_id, file_name);
        if (status == FAIL)
            ERROR_GOTO_2("in %s: Failure in printing file annotations for file %s", "dumpvd_ascii",
                         file_name);
    }
    vd_chosen_idx = 0; /* start at the beginning of vd_chosen */

    /* Determine if all VDs are to be dumped out. */
    if (num_vd_chosen <= 0) /* If so, set the corresponding flag. */
        dumpall = 1;
    else
        /* Otherwise, sort the indices of the chosen VDs in increasing
           order so that they will be dumped out in such order */
        sort(vd_chosen, num_vd_chosen);

    /* Examine each VD, which is identified by its ref# returned by VSgetid */
    for (i = 0;
         (vdata_ref = VSgetid(file_id, vdata_ref)) != FAIL && (dumpall != 0 || vd_chosen_idx < num_vd_chosen);
         i++) {
        intn data_only;      /* indicates whether to print data only */
        intn flds_match = 0; /* indicates whether any requested fields exist,
                or if no field requested, set to 1 means dump all fields */
        char sep[2];         /* the character that is used to separate 2 fields */

        /* Only dump the info of the chosen VDs or all of the VDs if none
           has been selected. */
        if ((!dumpall) && (i != vd_chosen[vd_chosen_idx]))
            continue; /* skip */

        vd_chosen_idx++; /* One vdata has been located; so go to the next one in
                    the array. */
        vd_id = VSattach(file_id, vdata_ref, "r");
        if (vd_id == FAIL) /* continue to the next vdata */
            ERROR_CONT_2("in %s: VSattach failed for vdata_ref#=%d", "dumpvd_ascii", (int)vdata_ref);

        /* Note: each of the parameters returned by a query routine below
           must be checked before being used */

        /* Retrieves general information about the vdata.  Note that NULL is
           passed in for vdata's record size because it is not needed and
           attempting to retrieve it causes failure when the vdata doesn't
           have any fields defined.  (bug #626) - BMR Mar 12, 02 */
        if (FAIL == VSinquire(vd_id, &nvf, &interlace, fields, NULL, vdname))
            ERROR_CONT_END("in %s: VSinquire failed for vdata with ref#=%d", "dumpvd_ascii", (int)vdata_ref,
                           vd_id);

        /* Get the number of fields in the vdata, continue to the next vdata
           if this failed.  If succeeded, check for exceeding max allowed */
        num_fields = VFnfields(vd_id);
        if (num_fields == FAIL)
            ERROR_CONT_END("in %s: VFnfields failed for vdata with ref#=%d", "dumpvd_ascii", (int)vdata_ref,
                           vd_id);

        if (num_fields >= VSFIELDMAX)
            ERROR_CONT_END("in %s: Number of fields exceeded the max allowed for vdata with ref#=%d, i.e., "
                           "possible data corruption",
                           "dumpvd_ascii", (int)vdata_ref, vd_id);

        /* Get the HDF size of the specified fields of the vdata; VShdfsize
           returns 0 if there are no fields previously defined */
        vsize = VShdfsize(vd_id, fields);
        if (vsize == FAIL)
            ERROR_CONT_END("in %s: VShdfsize failed for vdata with ref#=%d", "dumpvd_ascii", (int)vdata_ref,
                           vd_id);

        if (FAIL == (vdata_tag = VSQuerytag(vd_id)))
            ERROR_CONT_END("in %s: VSQuerytag failed for vdata with ref#=%d", "dumpvd_ascii", (int)vdata_ref,
                           vd_id);

        if (FAIL == VSgetclass(vd_id, vdclass))
            ERROR_CONT_END("in %s: VSgetclass failed for vdata with ref#=%d", "dumpvd_ascii", (int)vdata_ref,
                           vd_id);

        /* If one or more fields were specified by the user, then find out
           what they were, determine their corresponding indices in
           "fields", and store these indices in the array "flds_indices" so
           that they can be used to determine whether a field should be
           dumped later on. */
        if (flds_chosen[0] != NULL)
            flds_match = getFieldIndices(fields, flds_chosen, flds_indices);

        /* If no fields were chosen, all fields are to be dumped out, and
           so all fields match. */
        else /* if (flds_chosen[0] == NULL) */
            flds_match = 1;

        if (flds_match) {
            switch (dumpvd_opts->contents) {
                case DVERBOSE: /* dump all information */
                case DHEADER:  /* header only, no attributes, annotations
                                  or data */
                    /* store the vdata info into the vd_info_t struct for convenience */
                    curr_vd.index     = i;         /* vdata index */
                    curr_vd.nvf       = nvf;       /* number of records in the vdata */
                    curr_vd.interlace = interlace; /* interlace mode of the vdata */
                    curr_vd.vsize     = vsize;     /* record size of the vdata */
                    curr_vd.ref       = vdata_ref; /* vdata ref# */
                    curr_vd.tag       = vdata_tag; /* vdata tag */
                    strcpy(curr_vd.clss, vdclass); /* vdata class */
                    strcpy(curr_vd.name, vdname);  /* vdata name */
                    printHeader(fp, fldstring, fields, &curr_vd);

                    /* proceed to printing data if not printing header only */
                    if (dumpvd_opts->contents != DHEADER) {
                        /* dump vdata attributes */
                        status = dumpattr(vd_id, _HDF_VDATA, 1, ff, fp);
                        if (FAIL == status)
                            ERROR_BREAK_3("in %s: %s failed for attributes of vdata with ref#=%d",
                                          "dumpvd_ascii", "dumpattr", (int)vdata_ref, FAIL);

                        /* dump all the annotations for this vdata */
                        status = print_data_annots(file_id, file_name, vdata_tag, vdata_ref);
                        if (FAIL == status)
                            ERROR_BREAK_3("in %s: %s failed for attributes of vdata with ref#=%d",
                                          "dumpvd_ascii", "print_data_annots", (int)vdata_ref, FAIL);

                        /* BMR - 6/30/98 to fix bug #236: if no fields are defined or
                           no data is written, break out and don't fall through */
                        if (fields[0] == '\0' || nvf == 0) {
                            fprintf(fp, "   No data written\n\n");
                            break;
                        }
                    }
                    else       /* only header, no attributes, annotations or data */
                        break; /* break out and don't fall through */

                    /* note that we fall through if not printing header only */
                case DDATA: /* data only */
                    if (dumpvd_opts->contents == DDATA) {
                        data_only = 1;
                        strcpy(sep, "");
                    }
                    else {
                        data_only = 0;
                        strcpy(sep, ";");
                    }

                    /* Only the chosen or all fields will be dumped out. */
                    status = dumpvd(vd_id, ff, data_only, fp, sep, flds_indices, dumpallfields);
                    if (FAIL == status)
                        ERROR_BREAK_2("in %s: dumpvd failed for vdata with ref#=%d", "dumpvd_ascii",
                                      (int)vdata_ref, FAIL);
                    break; /* out of DDATA */

                default:
                    printf("dumping vdata in file %s, unknown option %d\n", file_name, dumpvd_opts->contents);
            } /* switch */
        }

        if (FAIL == VSdetach(vd_id))
            fprintf(stderr, "in %s: VSdetach failed on vdata with ref#=%d", "dumpvd_ascii", (int)vdata_ref);
        /* just simply goes to the next vdata */

        vd_id = FAIL; /* reset */

    } /* for each vdata */

done:
    if (ret_value == FAIL) { /* Failure cleanup */
        if (an_handle != FAIL)
            ANend(an_handle);

        if (vd_id != FAIL)
            VSdetach(vd_id);
    }

    return ret_value;
} /* dumpvd_ascii() */

intn
dumpvd_binary(dump_info_t *dumpvd_opts, int32 file_id, const char *file_name, FILE *fp, int32 num_vd_chosen,
              char *flds_chosen[MAXCHOICES], int32 *vd_chosen, int dumpallfields)
{
    int32         flds_indices[MAXCHOICES];
    int32         i, vd_chosen_idx;
    int32         nvf;
    int32         interlace;
    int32         vdata_ref = -1;
    char          vdname[VSNAMELENMAX + 1];
    intn          dumpall = 0;
    file_format_t ff      = DBINARY;
    int32         vd_id   = FAIL;
    intn          status;
    intn          ret_value = SUCCEED;
    char          fields[VSFIELDMAX * FIELDNAMELENMAX];

    (void)dumpvd_opts;
    (void)file_name;

    vd_chosen_idx = 0; /* "vd_chosen_idx" is used to index the array of "vd_chosen". */

    /* Determine if all VDs are to be dumped out. */
    if (num_vd_chosen <= 0) /* If so, set the corresponding flag. */
        dumpall = 1;
    else {
        /* Otherwise, sort the indices of the chosen VDs in increasing
                       order so that they will be dumped out in such order. */
        sort(vd_chosen, num_vd_chosen);
    }

    /* Examine each VD. */
    for (i = 0;
         (vdata_ref = VSgetid(file_id, vdata_ref)) != -1 && (dumpall != 0 || vd_chosen_idx < num_vd_chosen);
         i++) {
        int  data_only, flds_match = 0;
        char sep[2]; /* character used to separate fields */

        /* Only dump the info of the chosen VDs or all of the VDs if none
           has been selected. */
        if ((!dumpall) && (i != vd_chosen[vd_chosen_idx]))
            continue; /* skip */

        vd_chosen_idx++; /* One vdata has been located; so go to the next one in
                    the array.  ???*/

        /* Select the next vdata for processing */
        vd_id = VSattach(file_id, vdata_ref, "r");
        if (vd_id == FAIL) /* continue to the next vdata */
            ERROR_CONT_2("in %s: VSattach failed for vdata with ref#=%d", "dumpvd_binary", (int)vdata_ref);

        status = VSinquire(vd_id, &nvf, &interlace, fields, NULL, vdname);
        if (FAIL == status) /* end access to vd_id and cont. to next vdata */
            ERROR_CONT_END("in %s: VSinquire failed for vdata with ref#=%d", "dumpvd_binary", (int)vdata_ref,
                           vd_id);

        /* BMR - 7/1/98 realized while fixing bug #236.
           Skip binary printing if the vdata is empty */
        if (fields[0] == '\0' || nvf == 0)
            fprintf(stderr, "in %s: Vdata with ref#=%d is empty.\n", "dumpvd_binary", (int)vdata_ref);

        else /* vdata is not empty */
        {
            /* removed calls to VSQuerytag and VSgetclass - not useful in
               dumping by binary - BMR 8/23/00) */

            /* If one or more fields were specified by the user, then find out
               what they were, determine their corresponding indices in
               "fields", and store these indices in the array "flds_indices" so
               that they can be used to determine whether a field should be
               dumped later on. */
            if (flds_chosen[0] != NULL)
                flds_match = getFieldIndices(fields, flds_chosen, flds_indices);

            /* If no fields were chosen, all fields are to be dumped out, and
               so all fields match. */
            else /* if (flds_chosen[0] == NULL) */
                flds_match = 1;

            if (flds_match) {
                /* BMR: removed the if statement to determine if data_only
                   should be set; set data_only in either case */
                data_only = 1;
                strcpy(sep, "");

                /* Only the chosen or all fields will be dumped out. */
                if (FAIL == dumpvd(vd_id, ff, data_only, fp, sep, flds_indices, dumpallfields))
                    ERROR_CONT_END("in %s: Failure in dumping data for vdata with ref#=%d", "dumpvd_binary",
                                   (int)vdata_ref, vd_id);
            }
        } /* end of if (fields[0] == '\0' || nvf == 0 ) */

        if (FAIL == VSdetach(vd_id))
            fprintf(stderr, "in %s: VSdetach failed on vdata with ref#=%d", "dumpvd_binary", (int)vdata_ref);

        vd_id = FAIL; /* reset */

    } /* for each vdata */

    return (ret_value);
} /* dumpvd_binary */

/* closeVD combines the processes of Vend, Hclose, freeing the list
   of numbers, and resetting all ids, after validating the ids first.
   When either Vend or Hclose fails, closeVD prints an informative
   message then resetting the ids as normal since these failures are
   highly unlikely and since the files are opened as read-only, it's
   safe to go on. */
void
closeVD(int32      *file_id,   /* will be returned as a FAIL */
        int32     **vd_chosen, /* will be returned as a NULL */
        const char *curr_file_name)
{
    if (*file_id != FAIL) {
        if (FAIL == Vend(*file_id))
            fprintf(stderr, "Failure in closeVD: Vend failed for file %s\n", curr_file_name);
        if (FAIL == Hclose(*file_id))
            fprintf(stderr, "Failure in closeVD: Hclose failed for file %s\n", curr_file_name);
        *file_id = FAIL; /* reset */
    }

    free(*vd_chosen);
    *vd_chosen = NULL;

} /* end of closeVD */

intn
dvd(dump_info_t *dumpvd_opts, intn curr_arg, intn argc, char *argv[], char *flds_chosen[MAXCHOICES],
    int dumpallfields)
{
    int32         file_id = FAIL;
    char          file_name[MAXFNLEN];
    int32        *vd_chosen = NULL;
    FILE         *fp        = NULL;
    int32         num_vd_chosen;
    intn          index_error = 0;
    file_format_t ff;
    intn          status;
    intn          ret_value = SUCCEED;

    /* check for missing input file name */
    if (curr_arg >= argc) /* goto done with FAIL */
        ERROR_GOTO_0("Missing input file name.  Please try again.\n");

    while (curr_arg < argc) { /* Loop until all specified files have been processed */

        intn isHDF = TRUE; /* FALSE, if current file is not HDF file */

        /* get file name */
        strcpy(file_name, argv[curr_arg]);

        /* record for later use */
        strcpy(dumpvd_opts->ifile_name, file_name);
        curr_arg++;

        closeVD(&file_id, &vd_chosen, file_name);

        /* Print an informative message and skip this file if it is not
           an HDF file */
        isHDF = Hishdf(file_name);
        if (isHDF == FALSE) {
            /* if there are no more files to be processed, print error
               message, then returns with FAIL */
            if (curr_arg == argc) {
                ERROR_GOTO_1("in dvd: %s is not an HDF file", file_name);
            }
            else /* print message, then continue processing the next file */
            {
                ERROR_CONT_1("in dvd: %s is not an HDF file", file_name);
            }
        }

        /* open current hdf file with error check, if fail, go to next file */
        file_id = Hopen(file_name, DFACC_READ, 0);
        if (file_id == FAIL) {
            /* if there are no more files to be processed, print error
               message, then returns with FAIL */
            if (curr_arg == argc) {
                ERROR_GOTO_1("in dvd: Failure in opening file %s", file_name);
            }
            /* otherwise, print message, then continue processing the next file */
            else
                ERROR_CONT_1("in dvd: Failure in opening file %s", file_name);
        }

        /* initiate VG interface; if fail, probably something fatal, returns
           with FAIL */
        if (FAIL == Vstart(file_id))
            ERROR_GOTO_1("in dvd: Vstart failed for file %s\n", file_name);

        /* Find out which VDs have been chosen. */
        num_vd_chosen = choose_vd(dumpvd_opts, &vd_chosen, file_id, &index_error);

        /* if there are no valid indices, move on to the next file */
        if (index_error && num_vd_chosen == 0)
            continue; /* to the next file, closeVG before opening next file
                         takes care of Vend, Hclose, and free vg_chosen */

        ff = dumpvd_opts->file_format;
        fp = stdout; /* default file pointer to the standard output */
        switch (ff) {
            case DASCII: /*    ASCII file   */

                /* set output file */
                if (dumpvd_opts->dump_to_file)
                    fp = fopen(dumpvd_opts->file_name, "w");

                status = dumpvd_ascii(dumpvd_opts, file_id, file_name, fp, num_vd_chosen, flds_chosen,
                                      vd_chosen, dumpallfields);
                if (FAIL == status)
                    ERROR_BREAK_0("in dvd: dumpvd_ascii returned failure", FAIL);
                break;
            case DBINARY: /*  binary file, not fully tested yet  */

                /* Get output file name.  */
                if (dumpvd_opts->dump_to_file)
                    fp = fopen(dumpvd_opts->file_name, "wb");

                status = dumpvd_binary(dumpvd_opts, file_id, file_name, fp, num_vd_chosen, flds_chosen,
                                       vd_chosen, dumpallfields);
                if (FAIL == status)
                    ERROR_BREAK_0("in dvd: dumpvd_binary returned failure", FAIL);

                break;
            default:
                printf("dumping vdata, unknown output file option \n");
                ret_value = FAIL;
        } /* switch for output file   */

        free(vd_chosen);
        vd_chosen = NULL;

        if (dumpvd_opts->dump_to_file)
            fclose(fp);

        if (FAIL == Vend(file_id))
            ERROR_CONT_1("in dvd: Vend failed on file %s\n", file_name);

        if (FAIL == Hclose(file_id))
            ERROR_CONT_1("in dvd: Hclose failed on file %s\n", file_name);

        file_id = FAIL; /* reset */

    } /* while processing files  */

done:
    if (ret_value == FAIL) { /* Failure cleanup */
        if (file_id != FAIL) {
            Vend(file_id);
            Hclose(file_id);
        }

        free(vd_chosen);
        vd_chosen = NULL;
    }

    return ret_value;
} /* dvd */

/* exported */
intn
do_dumpvd(intn curr_arg, intn argc, char *argv[], intn help)
{
    dump_info_t dumpvd_opts; /* dumpvd options */
    char       *flds_chosen[MAXCHOICES];
    int         dumpallfields;
    intn        status, ret_value = SUCCEED;
    int         i;

    flds_chosen[0] = NULL;
    dumpallfields  = 1;

    /* initialize the structure that holds user's options and inputs */
    init_dump_opts(&dumpvd_opts);

    if (help == TRUE) {
        dumpvd_usage(argc, argv);
        goto done;
    }

    /* incomplete command */
    if (curr_arg >= argc) {
        dumpvd_usage(argc, argv);
        ERROR_GOTO_0("in do_dumpvd: command is incomplete");
    } /* end if */

    /* parse the user's command and store the inputs in dumpvd_opts */
    status = parse_dumpvd_opts(&dumpvd_opts, &curr_arg, argc, argv, flds_chosen, &dumpallfields);
    if (status == FAIL) {
        dumpvd_usage(argc, argv);
        ret_value = FAIL; /* return status to caller */
        goto done;        /* skip dvd */
    }

    /* display data and information as specified in dumpvd_opts */
    status = dvd(&dumpvd_opts, curr_arg, argc, argv, flds_chosen, dumpallfields);
    if (status == FAIL)
        ERROR_GOTO_0("in do_dumpvd: dvd failed");

done:
    /* free the lists for given indices, ref#s, names, and classes if
       they had been allocated */
    free_num_list(dumpvd_opts.by_index.num_list);
    free_num_list(dumpvd_opts.by_ref.num_list);
    free_str_list(dumpvd_opts.by_name.str_list, dumpvd_opts.by_name.num_items);
    free_str_list(dumpvd_opts.by_class.str_list, dumpvd_opts.by_class.num_items);

    for (i = 0; flds_chosen[i] != NULL; i++)
        free(flds_chosen[i]);

    return ret_value;
} /* end do_dumpvd() */
