#include "../config.h"
/*After much deliberation, it was decided NOT to Internationalize this file.*/

/*

  This module provides access routines to take an HDF file and return an
  HTML description of its contents.

  The function hdfGrokFile() will take a whole file and return stuff that
  can be seen at the "top level" (i.e. all datasets, all raster images and
  all Vgroups which are not contained within other Vgroups).

  The function hdfGrokReference() will return a description of what can
  be "seen" from a given location in the file.  Typically, this will be how
  users can navigate their way through a forest of Vgroups.

*/

/*
#define CHOUCK
*/

#ifdef HAVE_HDF
#define HDF

#if defined(__sgi)
#define IRIS4
#endif
#if defined(_IBMR2)
#define IBM6000
#endif
#if defined(sun)
#define SUN
#ifndef Void
#define Void void
#endif
#endif
#if defined(cray)
#define UNICOS
#endif
#if defined(__alpha)
#define DEC_ALPHA
#endif
#if defined(__hpux)
#define HP9000
#endif
/* VMS defines itself */

/* netCDF error toggle */
extern int ncopts;

/* wanker */
extern char *use_this_url_instead;

#ifndef DISABLE_TRACE
extern int srcTrace;
#endif

#include "mfhdf.h"
#include "mosaic.h"
#include "libhtmlw/HTML.h" /* for ImageInfo */
#ifdef HAVE_DTM
#include "netdata.h"
#endif
#include <X11/Xlib.h>

/* make all in-lined images smaller than hdfImageSize X hdfImageSize */
#define hdfImageSize get_pref_int(eHDF_MAX_IMAGE_DIMENSION)

/* limits on datasets and attribute counts to send into brief mode */
#define MAX_DATASET_DISPLAY   get_pref_int(eHDF_MAX_DISPLAYED_DATASETS)
#define MAX_ATTRIBUTE_DISPLAY get_pref_int(eHDF_MAX_DISPLAYED_ATTRIBUTES)

/* if is HDF Power User then drop most of the supporting text */
#define POWER_USER            get_pref_boolean(eHDF_POWER_USER)

#define SHOW_URL              FALSE

static FILE * fp;
static int    brief;
static char * my_url;

#ifdef CHOUCK
/* forward decls */
void hdfStartImage
    PROTO(());
void hdfEndImage
    PROTO(());
ImageInfo * hdfFetchImage
    PROTO((intn *bg));
void hdfXsds
    PROTO((char *name, int32 rank));
void hdfXvgroup
    PROTO((char *name, char *class, int32 count));
#endif /* CHOUCK */

/* return a string description of the number type */
char *
#ifdef PROTOTYPE
get_type(int32 nt)
#else
get_type(nt)
int32 nt;
#endif
{

    switch(nt) {

    case DFNT_CHAR   : return("8-bit characters");

    case DFNT_INT8   : return("signed 8-bit integers");
    case DFNT_UINT8  : return("unsigned 8-bit integers");
    case DFNT_INT16  : return("signed 16-bit integers");
    case DFNT_UINT16 : return("unsigned 8-bit integers");
    case DFNT_INT32  : return("signed 32-bit integers");
    case DFNT_UINT32 : return("unsigned 32-bit integers");

    case DFNT_FLOAT32  : return("32-bit floating point numbers");
    case DFNT_FLOAT64  : return("64-bit floating point numbers");

    default : return("unknown number type");

    }

} /* get_type */


/* return a string dump of buffer */
/* FREES the incoming buffer !!!!!!!!!!!!!!!!!! */
char *
#ifdef PROTOTYPE
buffer_to_string(char * tbuff, int32 nt, int32 count)
#else
buffer_to_string(tbuff, nt, count)
char * tbuff;
int32 nt;
int32 count;
#endif
{
    intn i;
    char * buffer;

    if(nt == DFNT_CHAR) {
        tbuff[count] = '\0';
        return tbuff;
    }

    buffer = (char *) HDgetspace(80 * count);
    if(buffer == NULL) return NULL;

    buffer[0] = '\0';

    switch(nt) {
    case DFNT_INT8   :
    case DFNT_UINT8  :
        sprintf(buffer, "%d", ((int8 *)tbuff)[0]);
        for(i = 1; i < count; i++)
            sprintf(buffer, "%s, %d", buffer, ((int8 *)tbuff)[i]);
        break;
    case DFNT_INT16   :
    case DFNT_UINT16  :
        sprintf(buffer, "%d", ((int16 *)tbuff)[0]);
        for(i = 1; i < count; i++)
            sprintf(buffer, "%s, %d", buffer, ((int16 *)tbuff)[i]);
        break;
    case DFNT_INT32   :
    case DFNT_UINT32  :
        sprintf(buffer, "%d", ((int32 *)tbuff)[0]);
        for(i = 1; i < count; i++)
            sprintf(buffer, "%s, %d", buffer, ((int32 *)tbuff)[i]);
        break;
    case DFNT_FLOAT32 :
        sprintf(buffer, "%f", ((float32 *)tbuff)[0]);
        for(i = 1; i < count; i++)
            sprintf(buffer, "%s, %f", buffer, ((float32 *)tbuff)[i]);
        break;
    case DFNT_FLOAT64 :
        sprintf(buffer, "%f", ((float64 *)tbuff)[0]);
        for(i = 1; i < count; i++)
            sprintf(buffer, "%s, %f", buffer, ((float64 *)tbuff)[i]);
        break;
    }

    HDfreespace((void *)tbuff);
    return buffer;

} /* buffer_to_string */


/* return a string of the contents of an attribute */
char *
#ifdef PROTOTYPE
get_attribute(int32 id, int32 num, int32 nt, int32 count)
#else
get_attribute(id, num, nt, count)
int32 id;
int32 nt;
int32 count;
int32 num;
#endif
{

    char *tbuff;
    int32 dsize;
    int32 status;

    dsize = DFKNTsize(nt);

    if(dsize < 1) return NULL;

    tbuff = HDgetspace(dsize * (count + 1));
    if(tbuff == NULL) return NULL;

    status = SDreadattr(id, num, tbuff);
    if(status == FAIL) return NULL;

    return(buffer_to_string(tbuff, nt, count));

}


/*

  Print out the info for data sets in the file.  This code is based on
  the multi-file interface of HDF 3.3 so it is able to decode netCDF files
  as well a strict HDF files

*/
#ifdef PROTOTYPE
do_sds(char *fname)
#else
do_sds(fname)
char *fname;
#endif
{

    int32 fid;
    int32 sds;
    int32 dsets, nattr, nattrs, status;
    int32 i, j;
    char name[512];
    int32 nt, dimsizes[50], rank;
    intn  count;
    /* SWP */
    int k;
    char attr_name[MAX_NC_NAME];
    int32 ant;
    int32 alen;
    char attr_data[MAX_NC_NAME];
    char longname[MAX_NC_NAME];

    fid = SDstart(fname, DFACC_RDONLY);
    if(fid == FAIL) return;

    status = SDfileinfo(fid, &dsets, &nattr);
    if(status == FAIL) return;

    if(dsets + nattr < 1) return;

    fprintf(fp, "<H2>Datasets</H2>\n");

    /* see if enough to send us into brief mode */
    if(dsets > MAX_DATASET_DISPLAY)
        brief = TRUE;

    fprintf(fp, "There are %d dataset%s and %d global attribute%s in this file.<P>\n",
           dsets, (dsets == 1 ? "" : "s"),
           nattr, (nattr == 1 ? "" : "s"));

    if(dsets) {
        fprintf(fp, "Available datasets :\n");
        fprintf(fp, "<UL>\n");
        for(i = 0; i < dsets; i++) {
            sds = SDselect(fid, i);
            if(sds == FAIL) return;

            status = SDgetinfo(sds, name, &rank, dimsizes, &nt, &nattrs);
            if(status == FAIL) return;

	    /* SWP -- 09/06/95 */
            if (get_pref_boolean(eHDFLONGNAME)) {
		strcpy(longname,"No Label/Longname");
		for (k=0; k<nattrs; k++) {
			status = SDattrinfo(sds, k, attr_name, &ant, &alen);
			if (status == FAIL) {
			}
			else if (!strcmp(attr_name,"long_name")) {
				status = SDreadattr(sds, k, (VOIDP)(&attr_data[0]));
				if (status == FAIL) {
				}
				else {
					attr_data[alen]='\0';
					strcpy(longname,attr_data);
					break;
				}
			}
		}

		if(POWER_USER)
			fprintf(fp, "<LI> <A NAME=\"DataSet%d\"><B>%s(%s)</B></A> rank %d : [",
				i, name, longname, rank);
		else
			fprintf(fp, "<LI> Dataset <A NAME=\"DataSet%d\"><B>%s(%s)</B></A> has rank %d with dimensions [",
				i, name, longname, rank);
	    }
	    else {
		if(POWER_USER)
			fprintf(fp, "<LI> <A NAME=\"DataSet%d\"><B>%s</B></A> rank %d : [",
				i, name, rank);
		else
			fprintf(fp, "<LI> Dataset <A NAME=\"DataSet%d\"><B>%s</B></A> has rank %d with dimensions [",
				i, name, rank);
	    }

#ifdef CHOUCK
            /* put it in the picture */
            hdfXsds(name, rank);
#endif /* CHOUCK */

            for(j = 0; j < rank; j++)
                if(j == 0)
                    fprintf(fp, "%d", dimsizes[j]);
                else
                    fprintf(fp, ", %d", dimsizes[j]);

            fprintf(fp, "]");

            if(POWER_USER)
                fprintf(fp, "; %s.\n", get_type(nt));
            else
                fprintf(fp, ".  The dataset is composed of %s.\n", get_type(nt));

#ifdef HAVE_DTM
        if (mo_dtm_out_active_p () && ((rank == 2) || (rank == 3)))
          fprintf (fp, "(To broadcast this dataset over DTM, click <A HREF=\"#hdfdtm;tag=%d,ref=%d\">here</A>.)\n",
                   (int32) DFTAG_NDG, i);
#endif
            if(nattrs) {
                if(brief || nattrs > MAX_ATTRIBUTE_DISPLAY) {

                    fprintf(fp, "There %s %d <A HREF=\"#hdfref;tag=%d,ref=%d\">attribute%s</A>.\n",
                            (nattrs == 1 ? "is" : "are"), nattrs, (int32) DFTAG_NDG, i,
                            (nattrs == 1 ? "" : "s"));

                } else {

                    if(POWER_USER)
                        fprintf(fp, "Attributes :\n");
                    else
                        fprintf(fp, "It has the following attributes :\n");

                    fprintf(fp, "<UL>\n");
                    for(j = 0; j < nattrs; j++) {
                        char *valstr;
                        status = SDattrinfo(sds, j, name, &nt, &count);
                        if(status == FAIL) return;

                        valstr = get_attribute(sds, j, nt, count);
                        if(valstr == NULL) continue;

                        if(POWER_USER)
                            fprintf(fp, "<LI> <i>%s</i> : <B>%s</B>", name, valstr);
                        else
                            fprintf(fp, "<LI> Attribute <i>%s</i> has the value : <B>%s</B>", name, valstr);

                        HDfreespace((void *)valstr);

                    }
                    fprintf(fp, "</UL>\n");
                }
            }

            SDendaccess(sds);
        }
        fprintf(fp, "</UL>\n");
    }


    if(nattr) {
        fprintf(fp, "Global attributes :\n");
        fprintf(fp, "<UL>\n");
        for(i = 0; i < nattr; i++) {

            char *valstr;

            status = SDattrinfo(fid, i, name, &nt, &count);
            if(status == FAIL) return;

            valstr = get_attribute(fid, i, nt, count);
            if(valstr == NULL) continue;

            if(POWER_USER)
                fprintf(fp, "<LI> <i>%s</i> : <B>%s</B>", name, valstr);
            else
                fprintf(fp, "<LI> Attribute <i>%s</i> has the value : <B>%s</B>", name, valstr);

            HDfreespace((void *)valstr);

        }
        fprintf(fp, "</UL>\n");
    }

    SDend(fid);

} /* do_sds */


/*

  If there are a lot of attributes we need to do them in a separate window

  That's what this function is for

*/
do_attributes(char *fname, int index) {

    char name[MAX_NC_NAME];
    int32 nattrs, nt, dims[MAX_VAR_DIMS], rank, status;
    int32 fid, sds;
    intn j, count;

    fid = SDstart(fname, DFACC_RDONLY);
    if(fid == FAIL) return;

    sds = SDselect(fid, index);
    if(sds == FAIL) return;

    status = SDgetinfo(sds, name, &rank, dims, &nt, &nattrs);
    if(status == FAIL) return;

    if(nattrs) {

        fprintf(fp, "Dataset <B>%s</B> has the following attributes :\n", name);
        fprintf(fp, "<UL>\n");
        for(j = 0; j < nattrs; j++) {
            char *valstr;
            status = SDattrinfo(sds, j, name, &nt, &count);
            if(status == FAIL) return;

            valstr = get_attribute(sds, j, nt, count);
            if(valstr == NULL) continue;

            if(POWER_USER)
                fprintf(fp, "<LI> <i>%s</i> : <B>%s</B>", name, valstr);
            else
                fprintf(fp, "<LI> Attribute <i>%s</i> has the value : <B>%s</B>", name, valstr);

            HDfreespace((void *)valstr);

        }
        fprintf(fp, "</UL>\n");
    }

    SDend(fid);

} /* do_attributes */


/*

  Print out info about file ids and descriptions

*/
#ifdef PROTOTYPE
do_fanns(int32 fid)
#else
do_fanns(fid)
int32 fid;
#endif
{
    char *buffer;
    int32 status;
    int32 len;

    len = DFANgetfidlen(fid, 1);
    if(len > 0) {

        buffer = HDgetspace(len + 1);
        if(buffer == NULL) return;

        status = DFANgetfid(fid, buffer, len + 1, 1);
        if(status == FAIL) return;

        fprintf(fp, "This file has the following label: <B>%s</B><P>\n", buffer);

        HDfreespace((void *)buffer);

    }

    len = DFANgetfdslen(fid, 1);
    if(len > 0) {

        buffer = HDgetspace(len + 1);
        if(buffer == NULL) return;

        status = DFANgetfds(fid, buffer, len, 1);
        if(status == FAIL) return;

        buffer[len] = '\0';
        fprintf(fp, "Here is the file description: <BLOCKQUOTE> %s </BLOCKQUOTE>\n", buffer);

        HDfreespace((void *)buffer);

    }

} /* do_fanns */


/*

  Print out labels and descriptions for this item

*/
#ifdef PROTOTYPE
print_desc(char *fname, uint16 tag, uint16 ref, char *name)
#else
print_desc(fname, tag, ref, name)
char *fname;
uint16 tag, ref;
char *name;
#endif
{

    int32 len, status;
    char *buffer;

    len = DFANgetlablen(fname, tag, ref);
    if(len > 0) {

        buffer = HDgetspace(len + 1);
        if(buffer == NULL) return;

            status = DFANgetlabel(fname, tag, ref, buffer, len + 1);
        if(status == SUCCEED) {
            name[0] = tolower(name[0]);

            if(POWER_USER)
                fprintf(fp, "Label : <B>%s</B> <P> \n", name, buffer);
            else
                fprintf(fp, "This %s was given the label : <B>%s</B> <P> \n", name, buffer);
        }
        HDfreespace((void *)buffer);
    }

    len = DFANgetdesclen(fname, tag, ref);
    if(len > 0) {

        buffer = HDgetspace(len + 1);
        if(buffer == NULL) return;

        status = DFANgetdesc(fname, tag, ref, buffer, len + 1);
        if(status == SUCCEED) {
            buffer[len] = '\0';
            name[0] = toupper(name[0]);
            fprintf(fp, "%s description : <BLOCKQUOTE> %s </BLOCKQUOTE>\n", name, buffer);
        }
        HDfreespace((void *)buffer);
    }

} /* print_desc */


/*

  print out the info for RIGSs in the file

*/
#ifdef PROTOTYPE
do_rigs(char *fname)
#else
do_rigs(fname)
char *fname;
#endif
{
    int32 count, i, ref, len;
    int32 status, w, h;
    intn  ip;

    count = DFR8nimages(fname);

    if(count < 1) return;

    fprintf(fp, "<H2>Images</H2>\n");

    if(count == 1)
        fprintf(fp, "There is 1 image in this file :\n");
    else
        fprintf(fp, "There are %d images in this file :\n", count);

    fprintf(fp, "<UL>\n");

    for(i = 0; i < count; i++) {
        status = DFR8getdims(fname, &w, &h, &ip);
        if(status == FAIL) return;

        ref = DFR8lastref();
        if(ref == FAIL) return;

        if(POWER_USER) {
            fprintf(fp, "<LI> Image : <IMG SRC=\"#hdfref;tag=%d,ref=%d\"> [%d by %d]\n",
                    (int32) DFTAG_RIG, ref, w, h);

            if(w > hdfImageSize || h > hdfImageSize)
                fprintf(fp, "  (subsampled)");

            if(ip) fprintf(fp, " has a palette\n");

            fprintf(fp, ".  ");

        } else {
            fprintf(fp, "<LI> This image : <IMG SRC=\"#hdfref;tag=%d,ref=%d\"> has dimensions %d by %d\n",
                    (int32) DFTAG_RIG, ref, w, h);

            if(w > hdfImageSize || h > hdfImageSize)
                fprintf(fp, "  (the image has been subsampled for display)");
            fprintf(fp, ".  ");

            if(ip) fprintf(fp, "There is also a palette associated with this image.\n");
        }

#ifdef HAVE_DTM
        if (mo_dtm_out_active_p ())
          fprintf (fp, "(To broadcast this image over DTM, click <A HREF=\"#hdfdtm;tag=%d,ref=%d\">here</A>.)\n", (int32) DFTAG_RIG, ref);
#endif

        print_desc(fname, DFTAG_RIG, ref, strdup ("image"));

    }

    fprintf(fp, "</UL>\n");

} /* do_rigs */


/*

  print out the info for Palettes in the file

*/
#ifdef PROTOTYPE
do_pals(char *fname)
#else
do_pals(fname)
char *fname;
#endif
{
    int32 count, i, ref, len;
    int32 status, w, h, ip;
    char pal[768];

    count = DFPnpals(fname);

    if(count < 1) return;

    fprintf(fp, "<H2>Palettes</H2>\n");

    if(count == 1)
        fprintf(fp, "There is 1 palette in this file :\n");
    else
        fprintf(fp, "There are %d palettes in this file :\n", count);

    fprintf(fp, "<UL>\n");

    for(i = 0; i < count; i++) {
        status = DFPgetpal(fname, pal);
        if(status == FAIL) return;

        ref = DFPlastref();
        if(ref < 1) return;

        fprintf(fp, "<LI> Here's what the palette looks like : <IMG SRC=\"#hdfref;tag=%d,ref=%d\">\n", (int32) DFTAG_IP8, ref);

#ifdef HAVE_DTM
        if (mo_dtm_out_active_p ())
          fprintf (fp, "(To broadcast this palette over DTM, click <A HREF=\"#hdfdtm;tag=%d,ref=%d\">here</A>.)\n", (int32) DFTAG_IP8, ref);
#endif

        print_desc(fname, DFTAG_IP8, ref, strdup ("palette"));

    }

    fprintf(fp, "</UL>\n");

} /* do_pals */


/* ------------------------------ dump_vdata ------------------------------ */
#ifdef PROTOTYPE
dump_vdata(int32 fid, int32 ref)
#else
dump_vdata(fid, ref)
int32 fid;
int32 ref;
#endif
{
    int32  vd;
    char   name[VSNAMELENMAX + 1], class[VSNAMELENMAX + 1];
    char   fields[(FIELDNAMELENMAX + 1) * VSFIELDMAX];
    char   padded_fields[(FIELDNAMELENMAX + 2) * VSFIELDMAX];
    int32  intr, sz, cnt;
    int32  nfields, single;
    char * p, * q;

    vd = VSattach(fid, ref, "r");
    if(vd == FAIL) return;

    VSinquire(vd, &cnt, &intr, fields, &sz, name);
    VSgetclass(vd, class);

    nfields = VFnfields(vd);
    single = FALSE;
    if((cnt == 1) && (nfields == 1))
        single = TRUE;

    if(name[0]  == '\0') sprintf(name, "[no name value]");

    fprintf(fp, "<LI> Vdata <B>%s</B>", name);
    if(class[0])
        fprintf(fp, " of class <B>%s</B>", class);
    if(!single)
        fprintf(fp," contains %d record%s", cnt, (cnt == 1 ? "" : "s"));
    fprintf(fp,".  \n");

    for(p = fields, q = padded_fields; *p; p++, q++) {
        *q = *p;
        if(*q == ',') *(++q) = ' ';
    }
    *q = '\0';

    fprintf(fp, "This Vdata contains the field%s <B>%s</B>\n",
            (nfields == 1 ? "" : "s"), padded_fields);

    /* if there is a single field with one value print out its values */
    if(single) {

        uint8 *tbuff;

        /* Attempted bugfix -- added 1 -- marca, 10:52pm sep 23. */
        tbuff = (uint8 *)HDgetspace(VFfieldisize(vd, 0) + 1);
        if(tbuff == NULL)
            return;

        VSsetfields(vd, fields);
        VSread(vd, tbuff, 1, 0);
        tbuff = buffer_to_string(tbuff, VFfieldtype(vd, 0), VFfieldorder(vd, 0));

        if(tbuff) {
            fprintf(fp, ": %s", tbuff);
            HDfreespace((void *)tbuff);
        }

    }

    fprintf(fp, ".  \n");

    VSdetach(vd);

} /* dump_vdata */


/* ------------------------------- dump_vg -------------------------------- */
/*

  Print out some HTML for the given Vgroup.

  If X is TRUE then call the functions to register the Vgroup in the
  hacked up image

*/
#ifdef PROTOTYPE
dump_vg(int32 fid, int32 ref, int32 X)
#else
dump_vg(fid, ref, X)
int32 fid;
int32 ref;
int32 X;
#endif
{
    int32 vg, count;
    char name[VSNAMELENMAX + 1], class[VSNAMELENMAX + 1];

    vg = Vattach(fid, ref, "r");
    if(vg == FAIL) return;

    Vgetname(vg, name);
    Vgetclass(vg, class);
    count = Vntagrefs(vg);

    if(name[0]  == '\0') sprintf(name, "[no name value]");
    if(class[0] == '\0') sprintf(class, "[no class value]");

    if(count)
        fprintf(fp, "<LI> Vgroup <A HREF=\"#hdfref;tag=%d,ref=%d\">%s</A> of class <B>%s</B> has %d element%s.\n",
                DFTAG_VG, ref, name, class, count, (count == 1 ? "" : "s"));

    else
        fprintf(fp, "<LI> Vgroup %s of class <B>%s</B> is empty.\n",
                name, class);

#ifdef CHOUCK
    if(X)
        hdfXvgroup(name, class, count);
#endif /* CHOUCK */

    Vdetach(vg);

} /* dump_vg */


/*
  print out the info for Vgroups in the file

  If where is -1 print out info for the Vgroups we can see at the top level
  (i.e. Vgroups which are not contained within other Vgroups)

  If where is a positive number assume that is the ref of a Vgroup and report
  everything that is contained within that Vgroup.

  Because of poor code design, a lot of functionality will be duplicated when
  reporting on the contents of a Vgroup.  Oh well.  Will give someone something
  to do later....

  I guess we know why this used to be called spank.c
*/
#ifdef PROTOTYPE
do_vgs(int32 fid, char *fname, int32 where)
#else
do_vgs(fid, fname, where)
int32  fid;
char * fname;
int32  where;
#endif
{

    int32 curr_ref, i, count, cnt;
    int32 curr_vg, vg, vg1, vd;
    int32 tag, ref, myref, intr, sz;
    int any;
    char name[VSNAMELENMAX], class[VSNAMELENMAX];

    if(where == -1) {

        if(Hnumber(fid, DFTAG_VG) < 1) return;

        any = FALSE;

        /* looking for top level VGroups */
        curr_ref = -1;
        while((curr_ref = Vgetid(fid, curr_ref)) != FAIL) {
            int top_level = TRUE;

            /* look through all other VGroups */
            myref = -1;
            while(top_level && ((myref = Vgetid(fid, myref)) != FAIL)) {
                if(myref == curr_ref) continue;
                vg = Vattach(fid, myref, "r");
                if(vg == FAIL) continue;

                count = Vntagrefs(vg);
                for(i = 0; i < count; i++) {
                    Vgettagref(vg, i, &tag, &ref);
                    if(tag == (int32) DFTAG_VG && ref == curr_ref) top_level = FALSE;

                }

                Vdetach(vg);
            }

            if(top_level) {

                if(any == FALSE) {
                    fprintf(fp, "<H2>Vgroups</H2>\n");
                    fprintf(fp, "The following Vgroups are visible at this level of the file.\n");
                    fprintf(fp, "<UL>\n");
                    any = TRUE;
                }

                dump_vg(fid, curr_ref, TRUE);

            }

        }

        if(any) fprintf(fp, "</UL>\n");

    } else {
        /* looking for stuff inside a given VGroup */

        vg = Vattach(fid, where, "r");
        if(vg == FAIL) return;

        Vgetname(vg, name);
        Vgetclass(vg, class);
        count = Vntagrefs(vg);

        /* set up title and stuff */
        fprintf(fp, "<TITLE>Vgroup: %s</TITLE>\n", name);
        fprintf(fp, "<H1>Vgroup %s</H1>\n", name);

        fprintf(fp, "This Vgroup is named %s and is of class <B>%s</B>.\n", name, class);

        if(count == 0) {
            Vdetach(vg);
            return;
        }

        if(count == 1)
          fprintf(fp, "There is %d element in Vgroup %s : \n", count, name);
        else
          fprintf(fp, "There are %d elements in Vgroup %s : \n", count, name);

        fprintf(fp, "<UL>\n");

        for(i = 0; i < count; i++) {
            Vgettagref(vg, i, &tag, &ref);
            switch((uint16) tag) {

            case DFTAG_VG :
                dump_vg(fid, ref, FALSE);
                break;
            case DFTAG_VH :
                dump_vdata(fid, ref);
                break;
            case DFTAG_RIG :
            case DFTAG_RI8 :
                {
                    int32 count, i, len;
                    int32 status, w, h;
                    intn  ip;

                    DFR8readref(fname, (uint16) ref);

                    status = DFR8getdims(fname, &w, &h, &ip);
                    if(status == FAIL) return;

                    if(POWER_USER) {
                        fprintf(fp, "<LI> Image : <IMG SRC=\"#hdfref;tag=%d,ref=%d\"> [%d by %d]\n",
                                (int32) DFTAG_RIG, ref, w, h);

                        if(w > hdfImageSize || h > hdfImageSize)
                            fprintf(fp, "  (subsampled)");

                        if(ip) fprintf(fp, " has a palette\n");

                        fprintf(fp, ".  ");

                    } else {
                        fprintf(fp, "<LI> Nested Raster Image : <IMG SRC=\"#hdfref;tag=%d,ref=%d\"> has dimensions %d by %d\n",
                                (int32) DFTAG_RIG, ref, w, h);

                        if(w > hdfImageSize || h > hdfImageSize)
                            fprintf(fp, "  (the image has been subsampled for display)");
                        fprintf(fp, ".  ");

                        if(ip) fprintf(fp, "There is also a palette associated with this image.\n");
                    }

#ifdef HAVE_DTM
                    if (mo_dtm_out_active_p ())
                        fprintf (fp, "(To broadcast this image over DTM, click <A HREF=\"#hdfdtm;tag=%d,ref=%d\">here</A>.)\n", (int32) DFTAG_RIG, ref);
#endif

                    print_desc(fname, DFTAG_RIG, ref, strdup ("image"));

                }
            break;

            default:
                if(HDgettagsname((uint16)tag))
                    fprintf(fp, "<LI> HDF object : %s (Ref = %d)\n", HDgettagsname((uint16)tag), ref);
                else
                    fprintf(fp, "<LI> HDF object : Tag = %d; Ref = %d\n", tag, ref);
                break;
            }
        }

        fprintf(fp, "</UL>\n");
        Vdetach(vg);

    }

} /* do_vgs */


/*

  Process Vdatas that are visible from the top level
    (i.e. they are not inside any Vgroups)

*/
#ifdef PROTOTYPE
do_lone_vds(int32 fid)
#else
do_lone_vds(fid)
int32 fid;
#endif
{

    int32 count, vd;
    int32 i, *ids, status;

    count = VSlone(fid, NULL, 0);
    if(count < 1) return;

    ids = (int32 *) HDgetspace(sizeof(int32) * count);
    if(ids == NULL) return;

    status = VSlone(fid, ids, count);
    if(status != count) return;

    fprintf(fp, "<H2>Vdatas</H2>\n");
    fprintf(fp, "There are %d Vdatas visible at this level of the file.\n", count);
    fprintf(fp, "<UL>\n");

    for(i = 0; i < count; i++)
        dump_vdata(fid, ids[i]);

    fprintf(fp, "<UL>\n");
    HDfreespace((void *)ids);

} /* do_lone_vds */


char *
#ifdef PROTOTYPE
hdfGrokFile(char *fname, char *realname)
#else
hdfGrokFile(fname, realname)
char *fname;
char *realname;
#endif
{
    char *tmp = (char *)mo_tmpnam((char *)0);
    char *data;
    int32 len, fid;

    /* make sure we don't crash on invalid open */
    ncopts = 0;

    fp = fopen(tmp, "w");
    if(!fp) return(NULL);

    fprintf (fp, "<TITLE>Scientific Data Brows-o-rama</TITLE>\n");
    fprintf (fp, "<H1>Scientific Data Brows-o-rama</H1>\n");

    if(SHOW_URL)
        fprintf (fp, "This file was accessed as URL <code>%s</code> <p>\n", realname);

    if(my_url)
        free(my_url);

    my_url = strdup(realname);

    /* start doing the full description */
    brief = FALSE;

#ifdef CHOUCK
    fprintf(fp, "<P> File dump : <A HREF=\"#hdfref;fileImageHit\"> <IMG SRC=\"#hdfref;fileImage\" ISMAP></A><P>\n");

    hdfStartImage();
#endif

    fid = Hopen(fname, DFACC_RDONLY, 0);

    if(fid != FAIL) {

        Vstart(fid);

        /* Do file annotations */
        do_fanns(fid);

    }

    /* Do SDSs */
    do_sds(fname);

    if(fid != FAIL) {

        /* Do RIGs */
        do_rigs(fname);

        /* Do Pals */
        do_pals(fname);

        /* Do Vgroups */
        do_vgs(fid, fname, -1);

        /* Do lone Vdatas */
        do_lone_vds(fid);

        /* close the file */
        Vend(fid);
        Hclose(fid);

    }

#ifdef CHOUCK
    hdfEndImage();
#endif

    fclose(fp);

    fp = fopen(tmp, "r");
    if (fp != NULL) {

        /*
         * Find the length of the file the really cheesy way!
         */
        fseek(fp, 0L, 0);
        fseek(fp, 0L, 2);
        len = ftell(fp);
        fseek(fp, 0L, 0);
        data = HDgetspace((len + 1) * sizeof(unsigned char));
        if (data == NULL)
            return(NULL);

        len = fread(data, sizeof(char), len, fp);
        fclose(fp);
        remove(tmp);
        data[len] = '\0';
        return(data);

    }

    return NULL;

} /* hdfGrokFile */


char *
#ifdef PROTOTYPE
hdfGrokReference(char *fname, char *ref, char *realname)
#else
hdfGrokReference(fname, ref, realname)
char *fname;
char *ref;
char *realname;
#endif
{
    int32 t, r;
    int32 x, y;

    char *tmp = (char *)mo_tmpnam((char *) 0);
    char *data;
    int32 len, fid;

    fp = fopen(tmp, "w");
    if(!fp) return(NULL);

    fprintf (fp, "<TITLE>HDF Brows-o-rama Internal</TITLE>\n");
    fprintf (fp, "<H1>HDF Brows-o-rama Internal</H1>\n");

    if(SHOW_URL)
        fprintf (fp, "This file was accessed as URL <code>%s</code> <p>\n", realname);

    if(sscanf(ref, "tag=%d,ref=%d", &t, &r) == 2) {

        switch((uint16) t) {
        case DFTAG_VG  :
            fid = Hopen(fname, DFACC_RDONLY, 0);
            if(fid == FAIL) return(NULL);

            Vstart(fid);

            do_vgs(fid, fname, r);

            Vend(fid);
            Hclose(fid);
            break;
        case DFTAG_NDG :
            do_attributes(fname, r);
           break;
        default:
            fprintf (fp, "<H2>Sorry, Bad Tag</H2> We're sorry, but objects of type %s are not currently recognized.<P>\n",
                     HDgettagsname((uint16)t));
            break;
        }

    } else {
        if(sscanf(ref, "fileImageHit?%d,%d", &x, &y) == 2) {
            char *buf;
            fprintf(fp, "<H2>File Image Hit</H2>\n");
            fprintf(fp, "We're sorry, but we really have no clue what is at location %d %d\n", x, y);

            buf = malloc(100);
            sprintf(buf, "#DataSet%d", 2);

            use_this_url_instead = buf;

        } else {
            fprintf(fp, "<H2>Sorry, Bad Reference</H2>\n");
            fprintf(fp, "We're sorry, but reference <I>%s</I> is bad and we can't figure out what to do about it.<P>\n",
                    ref);
        }
    }

    fclose(fp);

    fp = fopen(tmp, "r");
    if (fp != NULL) {

        /*
         * Find the length of the file the really cheesy way!
         */
        fseek(fp, 0L, 0);
        fseek(fp, 0L, 2);
        len = ftell(fp);
        fseek(fp, 0L, 0);
        data = HDgetspace((len + 1) * sizeof(unsigned char));
        if (data == NULL)
            return(NULL);

        len = fread(data, sizeof(char), len, fp);
        fclose(fp);
        remove(tmp);
        data[len] = '\0';
        return(data);

    }

    return NULL;

} /* hdfGrokReference */


/*

ImageInfo *hdfGetImage(char *filename, char *reference, int subsample)
  returns an ImageInfo struct corresponding to the named reference,
  as in hdfGrokReference.  If subsample == TRUE then subsample the
  image to fit within a hdfImageSize X hdfImageSize box

Return the backgroud pixel's index in bg only if the file Image

  return NULL on failure

*/

ImageInfo *
#ifdef PROTOTYPE
hdfGetImage(char *filename, char *reference, intn subsample, intn *bg)
#else
hdfGetImage(filename, reference, subsample, bg)
char *filename;
char *reference;
intn subsample;
intn *bg;
#endif
{

    ImageInfo *Image;
    int32 tag, ref, i, j;
    int32 status, w, h;
    intn  isp;
    char pal[768];

    Image = NULL;

#ifdef CHOUCK
    if(!strcmp(reference, "fileImage")) {
        use_this_url_instead = NULL;
        return ((ImageInfo *) hdfFetchImage(bg));
    }
#endif

    if(sscanf(reference, "tag=%d,ref=%d", &tag, &ref) == 2) {

#ifndef DISABLE_TRACE
	if (srcTrace) {
		fprintf (stderr, "[hdfGrokImage] Got tag %d, ref %d\n", tag, ref);
	}
#endif

        if((uint16)tag == DFTAG_RIG || (uint16)tag == DFTAG_RI8) {

            status = DFR8readref(filename, (uint16) ref);
            if(status == FAIL) return NULL;

            status = DFR8getdims(filename, &w, &h, &isp);
            if(status == FAIL) return NULL;

            /* allocate the basic structure */
            Image = (ImageInfo *) HDgetspace(sizeof(ImageInfo));
            if(Image == NULL) return NULL;

            /* allocate space for the data */
            Image->image_data = (unsigned char *) HDgetspace(w * h * sizeof(unsigned char));
            if(Image->image_data == NULL) return NULL;

            /* allocate the palette space */
            Image->reds = (int *) HDgetspace(256 * sizeof(int));
            if(Image->reds == NULL) return NULL;
            Image->greens = (int *) HDgetspace(256 * sizeof(int));
            if(Image->greens == NULL) return NULL;
            Image->blues = (int *) HDgetspace(256 * sizeof(int));
            if(Image->blues == NULL) return NULL;

            /* fill in the static fields */
            Image->ismap = FALSE;
            Image->width = w;
            Image->height = h;
            Image->num_colors = 256;
            Image->image = NULL;

            /* read the image */
            status = DFR8getimage(filename, Image->image_data, w, h, pal);
            if(status == FAIL) return NULL;

            /* set the palette */
            if(isp) {
                /* move the palette over into the fields */
                for(i = 0; i < 256; i++) {
                    Image->reds  [i] = (int)pal[i * 3]     << 8;
                    Image->greens[i] = (int)pal[i * 3 + 1] << 8;
                    Image->blues [i] = (int)pal[i * 3 + 2] << 8;
                }
            } else {
                /* create a fake palette */
                for(i = 0; i < 256; i++) {
                    Image->reds  [i] = i << 8;
                    Image->greens[i] = i << 8;
                    Image->blues [i] = i << 8;
                }
            }

            if(subsample) {
                int max   = (h > w ? h : w);
                int skip;

#ifndef DISABLE_TRACE
		if (srcTrace) {
			fprintf (stderr, "[***] max '%d'\n", max);
			fprintf (stderr, "[***] hdfImageSize '%d'\n", hdfImageSize);
		}
#endif

                skip = max / hdfImageSize;

                if(skip) {

                    /* allocate space for the data */
                    int i, j;
                    int cnt = 0;
                    unsigned char * newSpace =
                        (unsigned char *) HDgetspace(max * max * sizeof(unsigned char));
                    if(newSpace == NULL) return NULL;

                    skip++;
                    for(j = 0; j < h; j += skip)
                        for(i = 0; i < w; i += skip)
                            newSpace[cnt++] = Image->image_data[i + j * w];

                    HDfreespace((void *)(Image->image_data));
                    Image->image_data = newSpace;
                    Image->height = h / skip;
                    Image->width  = w / skip;
                    if(w % skip) Image->width++;

                }

            }

            return Image;

        }

        if((uint16)tag == DFTAG_IP8) {

            status = DFPreadref(filename, (uint16) ref);
            if(status == FAIL) return NULL;

             /* allocate the basic structure */
            Image = (ImageInfo *) HDgetspace(sizeof(ImageInfo));
            if(Image == NULL) return NULL;

            /* allocate space for the data */
            h = 30;
            Image->image_data = (unsigned char *) HDgetspace(768 * h * sizeof(unsigned char));
            if(Image->image_data == NULL) return NULL;

            /* allocate the palette space */
            Image->reds = (int *) HDgetspace(256 * sizeof(int));
            if(Image->reds == NULL) return NULL;
            Image->greens = (int *) HDgetspace(256 * sizeof(int));
            if(Image->greens == NULL) return NULL;
            Image->blues = (int *) HDgetspace(256 * sizeof(int));
            if(Image->blues == NULL) return NULL;

            /* fill in the static fields */
            Image->ismap = FALSE;
            Image->width = 256;
            Image->height = h;
            Image->num_colors = 256;
            Image->image = NULL;

            /* read the image */
            status = DFPgetpal(filename, pal);
            if(status == FAIL) return NULL;

            /* move the palette over into the fields */
            for(i = 0; i < 256; i++) {
                Image->reds  [i] = (int)pal[i * 3]     << 8;
                Image->greens[i] = (int)pal[i * 3 + 1] << 8;
                Image->blues [i] = (int)pal[i * 3 + 2] << 8;
                for(j = 0; j < h; j++)
                    Image->image_data[i + j * 256] = i;
            }

        }

        return Image;

    }


} /* hdfGetImage */


/*

ImageInfo *hdfGrokImage(char *filename, char *reference)
  returns an ImageInfo struct corresponding to the named reference,
  as in hdfGrokReference.

  return NULL on failure

*/
ImageInfo *
#ifdef PROTOTYPE
hdfGrokImage(char *filename, char *reference, int *bg)
#else
hdfGrokImage(filename, reference, bg)
char *filename;
char *reference;
int  *bg;
#endif
{

    return (hdfGetImage(filename, reference, TRUE, bg));

} /* hdfGrokImage */


#ifdef HAVE_DTM
/* Send the referenced image over DTM.  This is bad architecture.
   But what else is new? */
void
#ifdef PROTOTYPE
hdfDtmThang(char *filename, char *reference)
#else
hdfDtmThang(filename, reference)
char *filename;
char *reference;
#endif
{
    ImageInfo *img;
    int32 tag, ref;
    intn  foo;

    /* Check for presence of DTM output port. */
    if (!mo_dtm_out_active_p ())
        return;

    if(sscanf(reference, "tag=%d,ref=%d", &tag, &ref) == 2) {

        if((uint16)tag == DFTAG_RIG) {
            /* Using a cached image is not an option since we quantized. */
            img = hdfGetImage(filename, reference, FALSE, &foo);
            if (img) {
                mo_dtm_send_image ((void *)img);
                /* We specify copy_internally in args to NetSendRaster8Group,
                   so we can free everything here now. */
                free (img->image_data);
                free (img->reds);
                free (img->greens);
                free (img->blues);
                free (img);
            }
            return;
        }

        if((uint16)tag == DFTAG_IP8) {

            img = hdfGrokImage(filename, reference, NULL);
            if(img) {
                mo_dtm_send_palette ((void *)img);
                /* We specify copy_internally in args to NetSendRaster8Group,
                   so we can free everything here now. */
                free (img->image_data);
                free (img->reds);
                free (img->greens);
                free (img->blues);
                free (img);

            }

        }

        if((uint16)tag == DFTAG_NDG) {
            int32 start[10], end[10];
            int32 fid, sds, nt, nattr, size, i, tmp;
            char name[512];
            Data *d;

            fid = SDstart(filename, DFACC_RDONLY);
            if(fid == FAIL) return;

            sds = SDselect(fid, (int32) ref);
            if(sds == FAIL) return;

            /* davet what the hell is this about ???? */
            if (!(d = DataNew()))
                return;
            d->entity = ENT_Internal;
            d->dot = DOT_Array;

            /* get all basic meta-data */
            sprintf(name, "(no name)");
            SDgetinfo(sds, name, &(d->rank), d->dim, &nt, &nattr);

            if((d->rank > 3) || (d->rank < 2)) return;

            switch(nt) {
            case DFNT_INT8:
            case DFNT_UINT8:
                d->dost = DOST_Char;
                break;
            case DFNT_INT16:
            case DFNT_UINT16:
                d->dost = DOST_Int16;
                break;
            case DFNT_INT32:
            case DFNT_UINT32:
                d->dost = DOST_Int32;
                break;
            case DFNT_FLOAT32:
                d->dost = DOST_Float;
                break;
            case DFNT_FLOAT64:
                d->dost = DOST_Double;
                break;
            }

            /* set up the region we want to read */
            for(i = 0; i < d->rank; i++) {
                start[i] = 0;
                end[i]  = d->dim[i];
            }

            /* figger out how much space */
            for(i = 0, size = 1; i < d->rank; i++)
                size *= d->dim[i];

            /* allocate storage to store the raw numbers */
            if (!(d->data = (VOIDP) HDgetspace(size * DFKNTsize(nt))))
                return;

            /* read that crazy data */
            SDreaddata(sds, start, NULL, end, d->data);

            /* set the name */
            if (d->label = (char *) MALLOC(strlen(name)+1)){
                strcpy(d->label,name);
            }

            SDendaccess(sds);
            SDend(fid);

            /* swap numbers because Collage was written by a bunch of idiots */
            for(i = 0; i < ( d->rank / 2 ); i++) {
                tmp = d->dim[i];
                d->dim[i] = d->dim[d->rank - i - 1];
                d->dim[d->rank - i - 1] = tmp;
            }

            mo_dtm_send_dataset(d);

            /* free it */
            HDfreespace((void *)(d->label));
            HDfreespace((void *)(d->data));
            HDfreespace((void *)d);

        }

    }

    return;

}
#endif

#endif /* HAVE_HDF */

#ifdef CHOUCK

#define ImSize 512

Display *dsp;
Pixmap scrPix;
GC scrGC;
ImageInfo *img;
XFontStruct *myFont;

extern mo_window *current_win;
Widget view;

int32 sds, images, vgroups, vdatas;

void
hdfStartImage()
{
    int ret;
    Cardinal argcnt;
    Arg arg[5];
    XmFontList font_list = (XmFontList)NULL;
    XmFontContext font_context;
    XmStringCharSet charset;

    /*
     * Keep track of the window Widget
     */
    view = current_win->scrolled_win;

    /*
     * Remember the display
     */
    dsp = XtDisplay(view);

    /*
     * Create the Graphics Context
     */
    scrGC = XCreateGC(dsp, XtWindow(view), 0, NULL);

    /*
     * Create a ImSizexImSize pixmap to draw in, and clear it to the background
     */
    scrPix = XCreatePixmap(dsp, XtWindow(view), ImSize, ImSize,
                           DefaultDepth(dsp, DefaultScreen(dsp)));
    XSetForeground(dsp, scrGC, WhitePixel(dsp, DefaultScreen(dsp)));
    XFillRectangle(dsp, scrPix, scrGC, 0, 0, ImSize, ImSize);

    /*
     * Prepare to draw our stuff in Black on White
     */
    XSetForeground(dsp, scrGC, BlackPixel(dsp, DefaultScreen(dsp)));
    XSetBackground(dsp, scrGC, WhitePixel(dsp, DefaultScreen(dsp)));

    sds = images = vgroups = vdatas = 0;

#ifdef FONTS_SUCK
    /*
     *  Get a FontStruct so everything will look pretty
     */
    argcnt = 0;
    XtSetArg(arg[argcnt], XmNfontList, &font_list); argcnt++;
    XtGetValues(view, arg, argcnt);

    if (font_list == (XmFontList)NULL)
        return;

    ret = XmFontListInitFontContext(&font_context, font_list);
    if(ret == False)
        return;

    ret = XmFontListGetNextFont(font_context, &charset, &myFont);
    if (ret == False) {
        return;
    } else {
        XmFontListFreeFontContext(font_context);
        free((char *)charset);
    }

#else

    myFont = XLoadQueryFont(dsp, "fixed");

#endif


} /* hdfStartImage */


    /*
     * Place drawing calls here.
     * XDrawPoint, XDrawLine, XDrawArc, etc.
     */

#define sdsDepth      5
#define sdsWidth     35
#define sdsSep       20
#define sdsY         50
#define vgroupY     200
#define vgroupWidth  50

/* ------------------------------- hdfXsds -------------------------------- */
/*
 * Draw an SDS into our image
 */
void
hdfXsds(char *name, int32 rank)
{
    char   rankString[10];
    char   nameString[512];
    XPoint Points[10];
    int x, y;
    int strX, strY;
    int height, width, dir, asc, des;
    int sz;
    XCharStruct overall;

    y = sdsY;   /* all SDSs on same line */
    x = sdsSep + (sdsWidth + sdsSep) * sds;

    /*
     * Make sure our box is big enough by seeing how big the rank
     * string is going to be
     */
    sprintf(rankString, "%d", rank);
    XTextExtents(myFont, rankString, strlen(rankString), &dir, &asc, &des, &overall);
    height = asc + des;
    width = overall.width;

    /* see how big we need to make it */
    sz = sdsWidth;
    if(height > sz) sz = height;
    if(width  > sz) sz = width;

    if(x + sz + sdsDepth > ImSize) return;

    /* draw the cube */
    Points[0].x = (short) x;
    Points[0].y = (short) y;
    Points[1].x = (short) x + sdsDepth;
    Points[1].y = (short) y - sdsDepth;
    Points[2].x = (short) x + sdsDepth + sz;
    Points[2].y = (short) y - sdsDepth;
    Points[3].x = (short) x + sz;
    Points[3].y = (short) y;
    Points[4].x = (short) x + sdsDepth + sz;
    Points[4].y = (short) y - sdsDepth;
    Points[5].x = (short) x + sdsDepth + sz;
    Points[5].y = (short) y - sdsDepth + sz ;
    Points[6].x = (short) x + sz;
    Points[6].y = (short) y + sz;

    XDrawLines(dsp, scrPix, scrGC, Points, 7, CoordModeOrigin);
    XDrawRectangle(dsp, scrPix, scrGC, x, y, sz, sz);

    /* draw the rank now centered in the box */
    strY = (sz + height) / 2 + y;
    strX = (sz - width) / 2 + x;
    XDrawString(dsp, scrPix, scrGC, strX, strY, rankString, strlen(rankString));

    /* draw the name */
    sprintf(nameString, "%s", name);
    if(strlen(nameString) > 4)
        nameString[4] = '\0';
    XTextExtents(myFont, nameString, strlen(nameString), &dir, &asc, &des, &overall);
    strY = sz + height + y;
    strX = (sz - overall.width) / 2 + x;
    XDrawString(dsp, scrPix, scrGC, strX, strY, nameString, strlen(nameString));

    /* remember where we put it for future reference */


    /* increment counter */
    sds++;

} /* hdfXsds */


/* ------------------------------ hdfXvgroup ------------------------------ */
/*

  Draw a Vgroup object into our image

*/
void
hdfXvgroup(char *name, char *class, int32 count)
{
    char   classString[512];
    char   nameString[512];
    XPoint Points[10];
    int x, y;
    int strX, strY;
    int height, width, dir, asc, des;
    int sz;
    XCharStruct overall;

    y = vgroupY;   /* all Vgroups on same line */
    x = sdsSep + (vgroupWidth + sdsSep) * vgroups;

    /*
     * Make sure our box is big enough by seeing how big the name
     * string is going to be
     */
    sprintf(nameString, "%s", name);
    if(strlen(nameString) > 7)
        nameString[7] = '\0';

    XTextExtents(myFont, nameString, strlen(nameString), &dir, &asc, &des, &overall);
    height = asc + des;
    width = overall.width;

    /* see how big we need to make it */
    sz = vgroupWidth;
    if(height > sz) sz = height;
    if(width / 2 > sz) sz = width / 2;

    if(x + sz > ImSize) return;

    /* draw a circle */
    XDrawArc(dsp, scrPix, scrGC, x, y, 2 * sz, sz, 0, 360 * 64);

    /* put the name in */
    strY = sz / 2 + y;
    strX = (sz * 2 - overall.width) / 2 + x;
    XDrawString(dsp, scrPix, scrGC, strX, strY, nameString, strlen(nameString));

    /* write the count out */
    sprintf(nameString, "(%d)", count);
    XTextExtents(myFont, nameString, strlen(nameString), &dir, &asc, &des, &overall);
    strY = sz / 2 + height + y;
    strX = (sz * 2 - overall.width) / 2 + x;
    XDrawString(dsp, scrPix, scrGC, strX, strY, nameString, strlen(nameString));


    /* added another one to the image */
    vgroups++;

} /* hdfXvgroup */


/*
 *  All done building the image
 *  Build the pixmap and clean up
 */
void
hdfEndImage()
{

    int x, y;
    unsigned long pix;
    XImage *scrImage;
    unsigned char *dptr;


/*     XtVaGetValues (view, XtNforeground, &fg_pixel, XtNbackground, &bg_pixel, NULL); */



    scrImage = XGetImage(dsp, scrPix, 0, 0, ImSize, ImSize,
                         AllPlanes, ZPixmap);
    XFreePixmap(dsp, scrPix);
    img = (ImageInfo *)malloc(sizeof(ImageInfo));
    img->width = ImSize;
    img->height = ImSize;
    img->image_data = (unsigned char *)malloc(ImSize * ImSize);
    img->image = NULL;
    dptr = img->image_data;
    for (y=0; y<ImSize; y++)
	{
            for (x=0; x<ImSize; x++)
		{
                    pix = XGetPixel(scrImage, x, y);
                    if (pix == BlackPixel(dsp, DefaultScreen(dsp)))
			{
                            *dptr = (unsigned char)1;
			}
                    else
			{
                            *dptr = (unsigned char)0;
			}
                    dptr++;
		}
	}
    img->num_colors = 256;
    img->reds = (int *)malloc(sizeof(int) * 256);
    img->greens = (int *)malloc(sizeof(int) * 256);
    img->blues = (int *)malloc(sizeof(int) * 256);
    img->reds[0] = 65535;
    img->greens[0] = 65535;
    img->blues[0] = 65535;
    {
      int i;
      for (i = 1; i < 256; i++)
        {
          img->reds[i] = 0;
          img->greens[i] = 0;
          img->blues[i] = 0;
        }
    }
    XDestroyImage(scrImage);

} /* hdfEndImage */


ImageInfo *
hdfFetchImage(intn *bg)
{
    if(bg) *bg = 0;
    return (img);
} /* hdfFetchImage */

#endif
